聚类简介与基本概念
# 只需 shift+回车 运行本单元格,就可以让jupyter notebook宽屏显示
from IPython.core.display import display, HTML
display(HTML('<style>.container { width:100% !important; }</style>'))
# jupyter notebook 笔记高亮
# <div class="alert alert-block alert-success">
# 绿色警示框:成功 alert-success<br>
# 蓝色警示框:信息提示 alert-info<br>
# 红色警示框:高危 alert-danger<br>
# 黄色警示框:警告 alert-warning
# </div>
1 聚类简介
- 物以类聚,人以群分,即为聚类。聚类是将样本集合中相似的样本分配到相同的类,不同的样本分配到不同的类。
- 聚类分为硬聚类和软聚类。如果一个样本只能属于一个类为硬聚类,如果一个样本可以属于多个类则为软聚类。
- 聚类属于无监督学习,因为只是根据样本的相似度或距离将其进行归类,而类或簇事先并不知道。
2 聚类的基本概念
2.1 相似度或距离
- 聚类的核心就是相似度或距离,相似度直接影响聚类的结果,因此选择合适的相似度至关重要。
2.1.1 闵可夫斯基距离
- 聚类中,将样本集合看作为向量空间中点的集合,以该空间的距离来表示样本之间的相似度。
- 闵可夫斯基距离越大相似度越小,距离越小相似度越大。
给定样本集合 X X X, X X X是 m m m维实数向量空间 R m R^m Rm,其中 x i , x j ∈ X x_i,x_j\in X xi,xj∈X, x i = ( x 1 i , x 2 i , … , x m i ) x_i=(x_{1i},x_{2i},\dots,x_{mi}) xi=(x1i,x2i,…,xmi), x j = ( x 1 j , x 2 j , … , x m j ) x_j=(x_{1j},x_{2j},\dots,x_{mj}) xj=(x1j,x2j,…,xmj),样本闵可夫斯基距离定义为:
d i j = ( ∑ k = 1 m ∣ x k i − x k j ∣ P ) 1 p (1) d_{ij}=\left(\sum_{k=1}^m|x_{ki}-x_{kj}|^P\right)^{\frac1p}\tag{1} dij=(k=1∑m∣xki−xkj∣P)p1(1)
这里 p ≥ 1 p\geq1 p≥1。当 p = 2 p=2 p=2时称为欧氏距离,即:
d i j = ( ∑ k = 1 m ∣ x k i − x k j ∣ 2 ) 1 2 (2) d_{ij}=\left(\sum_{k=1}^m|x_{ki}-x_{kj}|^2\right)^{\frac12}\tag{2} dij=(k=1∑m∣xki−xkj∣2)21(2)
当 P = 1 P=1 P=1时称为曼哈顿距离,即:
d i j = ( ∑ k = 1 m ∣ x k i − x k j ∣ ) (3) d_{ij}=\left(\sum_{k=1}^m|x_{ki}-x_{kj}|\right)\tag{3} dij=(k=1∑m∣xki−xkj∣)(3)
当 p = ∞ p=\infty p=∞时称为切比雪夫距离,取各个坐标值差的绝对值的最大值,即:
d i j = max k ∣ x k i − x k j ∣ (4) d_{ij}=\max \limits_{k} \left|x_{ki}-x_{kj}\right| \tag{4} dij=kmax∣xki−xkj∣(4)
import numpy as np
test = np.random.randint(0,10,(2,3))
a = test[1]
b = test[0]
print(a,b)
[7 5 1] [7 0 8]
def d_HM(p,x_ki,x_kj):
# p为选择的距离算法;x_ki,x_kj均为一行的二维数组
# 曼哈顿距离
if p == 1:
distance = np.fabs(x_ki - x_kj).sum()
# 欧式距离
elif p == 2:
a = x_ki - x_kj
distance = np.dot(a,a.T)**0.5
# 切比雪夫距离
elif p >= 99:
distance = np.fabs(x_ki - x_kj).max()
else:
a = x_ki - x_kj
d = 0
for i in range(len(a)):
c = abs(a[i]) ** p
d = d + c
distance = d ** (1 / p)
return distance.round(3)
d_HM(2,a,b)
8.602
2.1.2 马哈拉诺比斯距离
- 马哈拉诺比斯距离简称为马氏距离。
- 考虑了各个分量(特征)之间的相关性与各个分量的尺度无关。
- 马氏距离越大相似度越小,越小相似度越大。
给定样本集合 X X X, X = ( x i j ) m × n X=\left(x_{ij}\right)_{m\times n} X=(xij)m×n,其协方差矩阵记为 S S S。样本 x i x_i xi与样本 x j x_j xj之间马氏距离 d i j d_{ij} dij定义为:
d i j = [ ( x i − x j ) T S − 1 ( x i − x j ) ] 1 2 (5) d_{ij}=\left[\left(x_i-x_j\right)^T S^{-1}\left(x_i-x_j\right) \right]^\frac12 \tag{5} dij=[(xi−xj)TS−1(xi−xj)]21(5)
其中:
x i = ( x 1 i , x 2 i , … , x m i ) T ; x j = ( x 1 j , x 2 j , … , x m j ) (6) x_i=\left(x_{1i},x_{2i},\dots,x_{mi}\right)^T ; x_j=\left(x_{1j},x_{2j},\dots,x_{mj}\right)\tag{6} xi=(x1i,x2i,…,xmi)T;xj=(x1j,x2j,…,xmj)(6)
当 S S S为单位矩阵时,即样本数据的各个分量相互独立且各个分量的方差为 1 1 1,由(5)式可知,马氏距离即为欧式距离,是欧式距离的推广。
from scipy import linalg
def d_PCM(S,xi,xj):
d = np.dot(xi - xj,linalg.inv(S))
distance = np.dot(d,(xi-xj).T) ** 0.5
return distance.round(3)
# 当协方差阵为单位矩阵时
S = np.eye(3)
d_PCM(S,a,b)
8.602
2.1.3 相关系数
- 样本之间的相似度也可以用相关系数来表示。
- 相关系数的绝对值接近于 1 1 1时,表示样本越相似;接近于 0 0 0时,样本就越不相似。
样本 x i x_i xi与样本 x j x_j xj之间的相关系数定义为:
r i j = ∑ k = 1 m ( x k i − x i ˉ ) ( x k j − x j ˉ ) [ ∑ k = 1 m ( x k i − x ˉ i ) 2 ∑ k = 1 m ( x k j − x ˉ j ) 2 ] 1 2 (7) r_{ij}=\frac{\sum_{k=1}^{m}(x_{ki}-\bar{x_i})(x_{kj}-\bar{x_j})}{\left[\sum_{k=1}^m{\left(x_{ki}-\bar{x}_i\right)^2}\sum_{k=1}^m{\left(x_{kj}-\bar{x}_j\right)^2}\right]^\frac12}\tag{7} rij=[∑k=1m(xki−xˉi)2∑k=1m(xkj−xˉj)2]21∑k=1m(xki−xiˉ)(xkj−xjˉ)(7)
其中:
x ˉ i = 1 m ∑ k = 1 m x k i ; x ˉ j = 1 m ∑ k = 1 m x k j (8) \bar{x}_i=\frac1m\sum_{k=1}^m{x_{ki}} ; \bar{x}_j=\frac1m\sum_{k=1}^m{x_{kj}}\tag{8} xˉi=m1k=1∑mxki;xˉj=m1k=1∑mxkj(8)
def r(xi,xj):
a,b,c = [],[],[]
for i in range(len(xi)):
x_i = xi[i] - xi.mean()
x_j = xj[i] - xj.mean()
a.append(x_i * x_j)
b.append(x_i**2)
c.append(x_j**2)
a,b,c = sum(a),sum(b),sum(c)
r = a / ((b * c) ** (1 / 2))
return r.round(3)
r(a,b)
-0.3
2.1.4 夹角余弦
- 夹角余弦接近于 1 1 1时,表示样本越相似;接近于 0 0 0时,样本就越不相似。
样本 x i x_i xi与样本 x j x_j xj之间的夹角余弦定义为:
s i j = ∑ k = 1 m x k i x k j [ ∑ k = 1 m x k i 2 ∑ k = 1 m x k j 2 ] 1 2 (9) s_{ij}=\frac{\sum_{k=1}^m{x_{ki}x_{kj}}}{\left[\sum_{k=1}^{m}{x_{ki}^2}\sum_{k=1}^m{x_{kj}^2}\right]^{\frac12}}\tag{9} sij=[∑k=1mxki2∑k=1mxkj2]21∑k=1mxkixkj(9)
def s(xi,xj):
s = np.dot(xi,xj.T) / (np.dot(xi,xi.T) * np.dot(xj,xj.T) ** 0.5)
return s.round(3)
s(a,b)
0.071
2.1.5 总结
- 距离度量相似度时,距离越小样本越相似;用相关系数时,相关系数越大样本越相似。
- 不同相似度度量得到的结果并不一定相同,可能存在着差异。
2.2 类或簇
-
聚类得到的类或簇,为样本的子集。当样本只属于一个类或类的交集为空集,则该方法为硬聚类;反之则为软聚类。
-
G G G表示类或簇; x i , x j x_i,x_j xi,xj表示类中的样本,用 n G n_G nG表示 G G G中的样本个数; d i j d_{ij} dij表示样本 x i , x j x_i,x_j xi,xj之间的距离。
① T T T为给定正数, G G G中任意两个样本 x i , x j x_i,x_j xi,xj,有 d i j ≤ T d_{ij}\leq T dij≤T,则称 G G G为一个类或簇。
② T T T为给定正数, G G G中任意样本 x i x_i xi,一定存在样本 x j x_j xj,有 d i j ≤ T d_{ij}\leq T dij≤T,则称 G G G为一个类或簇。
③ T T T为给定正数, G G G中任意样本 x i x_i xi,另一个样本 x j x_j xj满足, 1 n G − 1 ∑ x j ∈ G d i j ≤ T \frac1{n_G-1}\sum_{x_j\in G}{d_{ij}}\leq T nG−11∑xj∈Gdij≤T,则称 G G G为一个类或簇。
④ T , V T,V T,V为给定正数, G G G中任意两个样本 x i , x j x_i,x_j xi,xj满足, 1 n G ( n G − 1 ) ∑ x i ∈ G ∑ x j ∈ G d i j ≤ T \frac1{n_G(n_G-1)}\sum_{x_i\in G}{\sum_{x_j\in G}{d_{ij}}}\leq T nG(nG−1)1∑xi∈G∑xj∈Gdij≤T和 d i j ≤ V d_{ij}\leq V dij≤V,则称 G G G为一个类或簇。
- 这四种定义,第一个较为常用,由此可推出下面的三个定义。
"""
据定义①,并根据欧式距离进行归类
"""
# 导入库
import numpy as np
import pandas as pd
from scipy import linalg
# 创建数据
test = np.random.randint(0,100,(40,2))
df = pd.DataFrame(test,columns=list("XY"))
# 用pandas查看数据
df.head(3)
X | Y | |
---|---|---|
0 | 52 | 84 |
1 | 20 | 67 |
2 | 93 | 6 |
# 随机选取两个样本,并计算其他样本分别与这两个样本的距离
test = np.random.randint(0,40,2)
c = [] # 与df.loc[test[0]] 一类
d = [] # 与tdf.loc[test[1]] 一类
d_c = [] # 与df.loc[test[0]] 一类的距离
d_d = [] # 与df.loc[test[1]] 一类的距离
for i in df.index:
if i not in test:
a = d_HM(2,df.loc[i].values,df.loc[test[0]])
d_c.append(a)
b = d_HM(2,df.loc[i].values,df.loc[test[1]])
d_d.append(b)
# 根据距离的大小进行分类
if a < b:
c.append(i)
else:
d.append(i)
print("第一类:",test[0],c,"最大距离为:{}".format(max(d_c)),"\n","第二类:",test[1],d,"最大距离为:{}".format(max(d_d)))
第一类: 10 [0, 1, 4, 6, 7, 8, 11, 12, 13, 14, 18, 19, 22, 24, 25, 28, 29, 30, 31, 33, 34, 36, 37, 38, 39] 最大距离为:94.921
第二类: 32 [2, 3, 5, 9, 15, 16, 17, 20, 21, 23, 26, 27, 35] 最大距离为:127.279
# 可视化
import matplotlib.pyplot as plt
import matplotlib
# matplotlib.style.use("ggplot")
%matplotlib inline
df1 = df.loc[c]
df2 = df.loc[d]
plt.figure(figsize=(14,8))
plt.scatter(df1["X"],df1["Y"],s=200,c="blue",marker="*")
plt.scatter(df2["X"],df2["Y"],s=200,c="green",marker="o")
plt.scatter(df.loc[test[0]]["X"],df.loc[test[0]]["Y"],s=900,c="blue",marker="*")
plt.scatter(df.loc[test[1]]["X"],df.loc[test[1]]["Y"],s=900,c="green",marker="o")
<matplotlib.collections.PathCollection at 0x1af898db8d0>
- 类的特征:
① 类的均值,即类的中心: x ˉ G = 1 n G ∑ i = 1 n G x i \bar{x}_G=\frac1{n_G}\sum_{i=1}^{n_G}{x_i} xˉG=nG1∑i=1nGxi
② 类的直径(任意两个样本之间的最大距离): D G = max x i , x j ∈ G d i j D_G=\max \limits_{x_i,x_j\in G}d_{ij} DG=xi,xj∈Gmaxdij
③ 类的样本散步矩阵 A G A_G AG和样本协方差矩阵 S G S_G SG: A G = ∑ i = 1 n G ( x i − x ˉ G ) ( x i − x ˉ G ) T A_G=\sum_{i=1}^{n_G}(x_i-\bar{x}_G)(x_i-\bar{x}_G)^T AG=∑i=1nG(xi−xˉG)(xi−xˉG)T; S G = 1 m − 1 A G S_G=\frac1{m-1}A_G SG=m−11AG, m m m为样本的维数
c.append(test[0])
d.append(test[1])
G1 = df.loc[c]
G1.reset_index(drop=True,inplace=True)
G2 = df.loc[d]
G2.reset_index(drop=True,inplace=True)
# 查看G1的聚类中心点
aa = (G1["X"].sum() / len(G1)).round(3)
bb = (G1["Y"].sum() / len(G1)).round(3)
print("G1的聚类中心点为:",(aa,bb))
# 查看G2的聚类中心点
cc = (G2["X"].sum() / len(G2)).round(3)
dd = (G2["Y"].sum() / len(G2)).round(3)
print("G2的聚类中心点为:",(cc,dd))
G1的聚类中心点为: (30.269, 55.077)
G2的聚类中心点为: (74.143, 45.214)
# 查看类G1的直径
d_G1 = []
for i in G1.index:
for j in G1.index:
if i != j:
d_G1.append(d_HM(2,G1.loc[i].values,G1.loc[j].values))
D_G1 = max(d_G1)
print("G1类的直径为:",D_G1)
# 查看类G2的直径
d_G2 = []
for i in G2.index:
for j in G2.index:
if i != j:
d_G2.append(d_HM(2,G2.loc[i].values,G2.loc[j].values))
D_G2 = max(d_G2)
print("G2类的直径为:",D_G2)
G1类的直径为: 93.638
G2类的直径为: 92.087
# G1类散步矩阵
x_G1 = np.array([aa,bb]) # 聚类中心点
A_G = np.mat([[0,0],
[0,0]])
for i in G1.index:
a = G1.loc[i].values - x_G1
b = np.mat(a).T * np.mat(a)
A_G = A_G + b
print("G1类散步矩阵为:","\n",A_G)
# G1类样本协方差矩阵
S_G = A_G / (len(G1) - 1)
print("G1类样本协方差矩阵为:","\n",S_G)
G1类散步矩阵为:
[[ 8433.115386 3932.461538]
[ 3932.461538 22535.846154]]
G1类样本协方差矩阵为:
[[337.32461544 157.29846152]
[157.29846152 901.43384616]]
# G2类散步矩阵
x_G2 = np.array([cc,dd]) # 聚类中心点
A_G = np.mat([[0,0],
[0,0]])
for i in G2.index:
a = G2.loc[i].values - x_G2
b = np.mat(a).T * np.mat(a)
A_G = A_G + b
print("G2类散步矩阵为:","\n",A_G)
# G2类样本协方差矩阵
S_G = A_G / (len(G2) - 2)
print("G2类样本协方差矩阵为:","\n",S_G)
G2类散步矩阵为:
[[ 2787.714286 1014.571428]
[ 1014.571428 10510.357144]]
G2类样本协方差矩阵为:
[[232.30952383 84.547619 ]
[ 84.547619 875.86309533]]
2.3 类与类之间的距离
- 类 G p G_p Gp与 G q G_q Gq之间的距离 D ( p , q ) D_{(p,q)} D(p,q),也称为连接。
- 设类类 G p G_p Gp包含 n p n_p np个样本,类 G q G_q Gq包含 n q n_q nq个样本,分别用 x ˉ p \bar{x}_p xˉp和 x ˉ q \bar{x}_q xˉq表示 G p G_p Gp与 G q G_q Gq的均值,即类的中心。
① 最短距离或单连接: D p q = m i n { d i j ∣ x i ∈ G p , x j ∈ G q } D_{pq}=min \{ d_{ij}|x_i \in G_p,x_j \in G_q \} Dpq=min{dij∣xi∈Gp,xj∈Gq}
② 最长距离或完全连接: D p q = m a x { d i j ∣ x i ∈ G p , x j ∈ G q } D_{pq}=max \{ d_{ij}|x_i \in G_p,x_j \in G_q \} Dpq=max{dij∣xi∈Gp,xj∈Gq}
③ 中心距离: D p q = d x ˉ p x ˉ q D_{pq}=d_{\bar{x}_p\bar{x}_q} Dpq=dxˉpxˉq
④ 平均距离: D p q = 1 n p n q ∑ x i ∈ G p ∑ x j ∈ G q d i j D_{pq}=\frac1{n_p n_q}\sum_{x_i \in G_p}{\sum_{x_j \in G_q}{d_{ij}}} Dpq=npnq1∑xi∈Gp∑xj∈Gqdij
# ① 最短距离或单连接
# 查看类G1的直径
d = []
for i in df.index:
for j in range(i+1,len(df)):
if i != j:
d.append(d_HM(2,df.loc[i].values,df.loc[j].values))
print("最短距离或单连接为:",min(d))
print("最长距离或完全连接为:",max(d))
# 中心距离
d_center = d_HM(2,x_G1,x_G2)
print("两类的中心距离为:",d_center)
# 平均距离
d_G1_G2 = []
for i in G1.index:
for j in G2.index:
d_G1_G2.append(d_HM(2,G1.loc[i].values,G2.loc[j].values))
mean_d_G1_G2 = sum(d_G1_G2) / (len(G1) * len(G2))
print("两类的平均距离为:",mean_d_G1_G2.round(3))
最短距离或单连接为: 1.414
最长距离或完全连接为: 127.279
两类的中心距离为: 44.969
两类的平均距离为: 60.621