<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 层次聚类
这节我们要看的是层次聚类,它的目的就是形成一个层级的聚类情况,从下到上反映了从单点相粘合形成小聚类,小聚类形成大聚类的过程.不像K-means或者高斯混合模型这种划分聚类方法需要事先给一个聚类的数量K,一般它会形成一个树状图,允许我们在各个层次(对应不同数量的聚类)看聚类情况,从而克服了划分聚类方法由固定的K带来的缺点.
层次聚类分类
层次聚类方法按照构建层级的方法分为两种
Step1:  数据获取
该数据集中的数据都是int类型,可以说可以直接使用
Step2:  数据预处理
可以看到虽然channel和region也是int类型,但明显和后面几个不同,channel和region应该是分类标签,后面的则是连续数据,而后面的数值变化跨度太大,因此可以对每个数据取对数作为特征.
Step3:  数据集拆分
Step4:  训练模型
Step5:  模型评估
<ASSISTANT_TASK:>
Python Code:
import requests
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics import (
    adjusted_rand_score,
    adjusted_mutual_info_score,
    homogeneity_score,
    completeness_score,
    v_measure_score
)
Explanation: 层次聚类
这节我们要看的是层次聚类,它的目的就是形成一个层级的聚类情况,从下到上反映了从单点相粘合形成小聚类,小聚类形成大聚类的过程.不像K-means或者高斯混合模型这种划分聚类方法需要事先给一个聚类的数量K,一般它会形成一个树状图,允许我们在各个层次(对应不同数量的聚类)看聚类情况,从而克服了划分聚类方法由固定的K带来的缺点.
层次聚类分类
层次聚类方法按照构建层级的方法分为两种:
聚合型 : 从下到上,从每个单点构成的聚类出发,一步步将相近的聚类粘合
分裂型 : 从上到下,所有的点都属于一个聚类,慢慢将远的子集分裂出去形成新的聚类
度量和聚类距离
这个时候,我们就要好好定义下距离的"远""近"了.我们从点和点之间的度量开始,到聚类和聚类之间的距离(linkage criterion).
基于点和点之间的度量$d$,我们就可以规定聚类A,B之间的距离.常用的有以下几种：
1. 最大距离（Complete-linkage clustering）,$\max{ d(a,b) : a \in A, b\in B }$
最小距离(Single-linkage clustering),$\min{ d(a,b) : a \in A, b\in B }$
平均距离(UPGMA),$ \frac {1} {n\times m} \sum \limits_ {a \in A, b\in B} d(a,b) }$,其中$n,m$分别是A,B聚类的基数，也就是包含的点的数量.
中点距离(UPGMC),$\vert d(C_A,C_B)  \vert$,其中$C_A, C_B$分别是A，B的中点，也就是离中心最近的点
能量距离（Energy distance）,$ \frac {2} {n\times m} \sum \limits_ {a \in A, b\in B} \vert a-b\vert_2 - \frac{1}{n^2} \sum \limits_{a_i,a_j \in A} \vert a_i -a_j \vert_2 -\frac{1}{m^2} \sum \limits_{b_i,b_j \in B} \vert b_i -_j \vert_2 }$
SLINK算法介绍、复杂度
至于聚合型聚类的复杂度,不可能小于$ O(n^2) $,因为我们需要计算每对数据点之间的距离.我们介绍一种R.Sibson在1972年提出的SLINK算法,时间复杂度是$ O(n^2) $，空间复杂度是$ O(n) $.
我们把n个数据点从0到$ n-1 $编号，SLINK算法求出的是两个数组A和B，A(i)代表把i数据点和起码一个比i序号大的数据点并入一个聚类的层级(表现在树状图上是高度,本质上是聚类之间的距离),而B(i)表示这个层级包含i的聚类中序号最大的点.
得到这两个数组后
我们可以从A中最小的数 $ a_0 $(可能有多个)开始,对应的点集$ {i}$ (因为可能多个,所以用集合表示)中的每个点,将它和它的B(i)划分为同一聚类并一直处于一个聚类,没有涉及的点单独成一个聚类;
将$ a_0 $从A中去除，找A中剩下的最小的数,对应的点集$ {i} $中的每个点,将它和它的B(i)划入同一聚类并一直处于一个聚类,没有涉及的聚类(可能是单点聚类)保持不变.
重复2步骤,直到把所有点划入一个聚类.
方法优缺点
相比于K-means方法，层次聚类每次运行都会得到相同结果，也可以在任何层次停止，处理的数据类型也更加丰富，比如可以通过设定合适的度量来处理字符串类型，但算法复杂度也会提高.
使用sklearn中的接口实现层次聚类
sklearn中有接口sklearn.cluster.AgglomerativeClustering可以实现一种叫做凝聚层次聚类的层次聚类算法.它通过递归地聚合样本实现层次聚类
另一类似的接口是sklearn.cluster.FeatureAgglomeration,它与AgglomerativeClustering不同之处在于它使用特征作为聚合的目标而非样本.
例:将数据集Wholesale+customers聚类
End of explanation
csv_content = requests.get("http://archive.ics.uci.edu/ml/machine-learning-databases/00292/Wholesale%20customers%20data.csv").text
csv_list = csv_content.strip().split("\n")
row_matrix = [line.strip().split(",") for line in csv_list]
row_name = row_matrix[0]
dataset = pd.DataFrame(row_matrix[1:],columns=row_name,dtype="int64")
dataset[:10]
dataset.describe()
Explanation: 数据获取
该数据集中的数据都是int类型,可以说可以直接使用
End of explanation
for i in row_name[2:]:
    dataset[i] = np.log(dataset[i]+0.0000001)
dataset[:10]
Explanation: 数据预处理
可以看到虽然channel和region也是int类型,但明显和后面几个不同,channel和region应该是分类标签,后面的则是连续数据,而后面的数值变化跨度太大,因此可以对每个数据取对数作为特征.
End of explanation
train_set,validation_set = train_test_split(dataset)
Explanation: 数据集拆分
End of explanation
agc = AgglomerativeClustering(n_clusters=3)
agc.fit(train_set[row_name[2:]])
Explanation: 训练模型
End of explanation
adjusted_rand_score(train_set["Channel"],agc.labels_)
adjusted_mutual_info_score(train_set["Channel"],agc.labels_)
homogeneity_score(train_set["Channel"],agc.labels_)
completeness_score(train_set["Channel"],agc.labels_)
v_measure_score(train_set["Channel"],agc.labels_)
Explanation: 模型评估
End of explanation
<END_TASK>