一、算法原理
- 根据数据之间是相似的(相关的),将数据分组(簇);
- 选择K个初始质心(K是簇的个数),每个点指派到最近的质心,而指派到一个质心的点集为一个簇,根据指派到簇的点,更新每个簇的质心,重复指派和更新步骤,直到簇不发生变化或等价地直到质心不发生变化。
二、手写代码
调包:
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import make_blobs
模拟数据集:
# 模拟出一些数据集出来
X , Y = make_blobs(n_samples = 300,
cluster_std = [0.3, 0.3, 0.3],
centers = [[0,0],[1,1],[-1,1]]
,random_state = 4
)
sim_data = pd.DataFrame(X, columns = ['x1', 'x2'])
sim_data.head(5)
#画图plt.scatter()
plt.scatter(sim_data['x1'], sim_data['x2'])
1、创建质心
# 自动自动生成随机的质心点
def initial_centers(datasets, k = 3):
cols = datasets.columns
data_content = datasets.loc[:, cols != 'label']
range_info = data_content.describe().loc[['min','max']]
# np.random.uniform(a, b, c) 随机生成在[a,b)区间里的3个数
k_randoms = [np.random.uniform(range_info[i]['min'],
range_info[i]['max'], k)
for i in range_info.columns]
centers = pd.DataFrame(k_randoms, index = range_info.columns)
return centers.T
# 手动的创建质心点,无法保证随机性
centers = [[-1.5, 0.5],[1.5, 0],[1, -0.5]]
centers = pd.DataFrame(centers,columns = ["x1","x2"],index = ["blue","green","red"])
2、计算样本点与质心点距离
d = []
for i in centers.index:
tmp_center = centers.loc[i,:]
d.append(np.power(sim_data - tmp_center, 2).sum(axis = 1))
3、保持质心不变-更新类别
# 将3个列拼接起来
dis = pd.concat(d,axis = 1)
# 指定列名
dis.columns = centers.index
# 选出每一个样本(或者每一行)里面最小的那个距离所对应的列名
tmp_label = dis.idxmin(axis = 1)
tmp_label.name = "label"
# 更新类别
pd.concat([sim_data,tmp_label], axis = 1)
4、保持类别不变-更新质心
sim_data.groupby(tmp_label).mean()
5、整体代码-循环迭代
#初始化质心点
centers = [[-1.5,0.5],[1.5,0],[1,-0.5]]
centers = pd.DataFrame(centers,columns = ["x1","x2"],index = ["blue","green","red"])
SSE = 0
# 开始迭代
while True:
last_SSE = SSE
# 计算样本点到所有质心的距离
l = []
for i in centers.index:
tmp_center = centers.loc[i,:]
l.append(np.power(sim_data - tmp_center, 2).sum(axis = 1))
dis = pd.concat(l,axis = 1)
# 计算SSE,添加停止条件
SSE = dis.min(axis = 1).sum()
if SSE == last_SSE:
break
# 保持质心,更新类别
tmp_label = dis.idxmin(axis = 1)
tmp_label.name = "label"
pd.concat([sim_data, tmp_label], axis = 1)
# 保持类别,更新质心
centers = sim_data.groupby(tmp_label).mean()
三、sklearn算法实现
1、Kmeans算法包
from sklearn.cluster import KMeans
# 训练模型
model = KMeans(n_clusters= 3) # 实例化
model = model.fit(X) # 模型学习:将学习到的标签,质心等保存到model里
# 模型学习的结果
model.cluster_centers_ # 质心
model.inertia_ # SSE:所有簇的离散程度之和
model.labels_ # 标签
model.n_iter_ # 迭代次数
dir(model) # 带一个_的是学习的结果
# 将聚类的结果和中心点的结果画图表示
plt.scatter(sim_data["x1"], sim_data["x2"], c = model.labels_)
plt.scatter(model.cluster_centers_[: , 0],model.cluster_centers_[:, 1], color = "red")
二、模型评估
1、SSE
SSE评估模型的缺点:
- 随着k值增大,SSE一直减小,但不代表模型越好;
- SSE对数据要求满足凸分布,会让聚类算法在一些细长簇,环形簇,或者不规则形状的流形时表现不佳
绘制SSE学习曲线:
iner = []
for i in range(1, 20):
model = KMeans(n_clusters = i)
model = model.fit(X)
iner.append(model.inertia_)
plt.plot(range(1, 20), iner)
2、轮廓系数
- 样本与其自身所在的簇中的其他样本的相似度a,等于样本与同一簇中所有其他点之间的平均距离;
- 样本与其他簇中的样本的相似度b,等于样本与下一个最近的簇中的所有点之间的平均距离;
- 根据聚类的要求 ”簇内差异小,簇外差异大“,希望b大于a,且大得越多越好;
- 轮廓系数范围(-1,1):
a:值越接近1表示样本与自己所在的簇中的样本很相似,与其他簇中的样本不相似;
b:轮廓系数为负,样本点与簇外的样本更相似;
c:轮廓系数为0,两个簇中的样本相似度一致,两个簇本应该是一个簇;
sklearn调用轮廓系数:
# 尝试使用轮廓系数来查看
from sklearn.metrics import silhouette_score # 所有轮廓系数均值
from sklearn.metrics import silhouette_samples # 每个样本轮廓系数
# 给出所有样本点的轮廓系数的均值
silhouette_score(X, model.labels_)
# 给出所有样本点的轮廓系数
# silhouette_samples(X, model.labels_)
# 用轮廓系数绘制学习曲线
sil = []
for i in range(2, 20):
model = KMeans(n_clusters= i)
model = model.fit(X)
sil.append(silhouette_score(X, model.labels_))
plt.plot(range(2, 10), sil[:8])
--------------------------------------------------------------------------------------------------------------------------------
基于轮廓系数来选择n_clusters:
from sklearn.cluster import KMeans
#from sklearn.metrics import silhouette_samples, sxilhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
#先设定我们要分成的簇数
n_clusters = 4
#创建一个画布,画布上共有一行两列两个图
fig, (ax1, ax2) = plt.subplots(1, 2)
#画布尺寸
fig.set_size_inches(18, 7)
# 第一个图是我们的轮廓系数图像,是由各个簇的轮廓系数组成的横向条形图
# 横向条形图的横坐标是我们的轮廓系数取值,纵坐标是我们的每个样本,因为轮廓系数是对于每一个样本进行计算的
# 首先我们来设定横坐标
# 轮廓系数的取值范围在[-1,1]之间,但我们至少是希望轮廓系数要大于0的
# 太长的横坐标不利于我们的可视化,所以只设定X轴的取值在[-0.1,1]之间
ax1.set_xlim([-0.1, 1])
# 接下来设定纵坐标,通常来说,纵坐标是从0开始,最大值取到X.shape[0]的取值
# 但我们希望,每个簇能够排在一起,不同的簇之间能够有一定的空隙
# 以便我们看到不同的条形图聚合成的块,理解它是对应了哪一个簇
# 因此我们在设定纵坐标的取值范围的时候,在X.shape[0]上,加上一个距离(n_clusters + 1) * 10,留作间隔用
ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])
# 开始建模,调用聚类好的标签
clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
cluster_labels = clusterer.labels_
# 调用轮廓系数分数,注意,silhouette_score生成的是所有样本点的轮廓系数均值
# 两个需要输入的参数是,特征矩阵X和聚类完毕后的标签
silhouette_avg = silhouette_score(X, cluster_labels)
#用print来报一下结果,现在的簇数量下,整体的轮廓系数究竟有多少
print("For n_clusters =", n_clusters,
"The average silhouette_score is :", silhouette_avg)
# 调用silhouette_samples,返回每个样本点的轮廓系数,这就是我们的横坐标
sample_silhouette_values = silhouette_samples(X, cluster_labels)
#设定y轴上的初始取值
y_lower = 10
#接下来,对每一个簇进行循环
for i in range(n_clusters):
# 从每个样本的轮廓系数结果中抽取出第i个簇的轮廓系数,并对他进行排序
ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
#注意, .sort()这个命令会直接改掉原数据的顺序
ith_cluster_silhouette_values.sort()
#查看这一个簇中究竟有多少个样本
size_cluster_i = ith_cluster_silhouette_values.shape[0]
#这一个簇在y轴上的取值,应该是由初始值(y_lower)开始,到初始值+加上这个簇中的样本数量结束(y_upper)
y_upper = y_lower + size_cluster_i
#colormap库中的,使用小数来调用颜色的函数
#在nipy_spectral([输入任意小数来代表一个颜色])
#在这里我们希望每个簇的颜色是不同的,我们需要的颜色种类刚好是循环的个数的种类
#在这里,只要能够确保,每次循环生成的小数是不同的,可以使用任意方式来获取小数
#在这里,我是用i的浮点数除以n_clusters,在不同的i下,自然生成不同的小数
#以确保所有的簇会有不同的颜色
color = cm.nipy_spectral(float(i)/n_clusters)
#开始填充子图1中的内容
#fill_between是填充曲线与直角之间的空间的函数
#fill_betweenx的直角是在纵坐标上
#fill_betweeny的直角是在横坐标上
#fill_betweenx的参数应该输入(定义曲线的点的横坐标,定义曲线的点的纵坐标,柱状图的颜色)
ax1.fill_betweenx(np.arange(y_lower, y_upper)
,ith_cluster_silhouette_values
,facecolor=color
,alpha=0.7
)
#为每个簇的轮廓系数写上簇的编号,并且让簇的编号显示坐标轴上每个条形图的中间位置
#text的参数为(要显示编号的位置的横坐标,要显示编号的位置的纵坐标,要显示的编号内容)
ax1.text(-0.05
, y_lower + 0.5 * size_cluster_i
, str(i))
# 为下一个簇计算新的y轴上的初始值,是每一次迭代之后,y的上线再加上10
#以此来保证,不同的簇的图像之间显示有空隙
y_lower = y_upper + 10
#给图1加上标题,横坐标轴,纵坐标轴的标签
ax1.set_title("The silhouette plot for the various clusters.")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")
#把整个数据集上的轮廓系数的均值以虚线的形式放入我们的图中
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
#让y轴不显示任何刻度
ax1.set_yticks([])
#让x轴上的刻度显示为我们规定的列表
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
#开始对第二个图进行处理,首先获取新颜色,由于这里没有循环,因此我们需要一次性生成多个小数来获取多个颜色
colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
ax2.scatter(X[:, 0], X[:, 1]
,marker='o' #点的形状
,s=8 #点的大小
,c=colors
)
#把生成的质心放到图像中去
centers = clusterer.cluster_centers_
# Draw white circles at cluster centers
ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
c="red", alpha=1, s=200)
#为图二设置标题,横坐标标题,纵坐标标题
ax2.set_title("The visualization of the clustered data.")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")
#为整个图设置标题
plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
"with n_clusters = %d" % n_clusters),
fontsize=14, fontweight='bold')
plt.show()
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
for n_clusters in [2,3,4,5,6,7]:
n_clusters = n_clusters
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_size_inches(18, 7)
ax1.set_xlim([-0.1, 1])
ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])
clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
cluster_labels = clusterer.labels_
silhouette_avg = silhouette_score(X, cluster_labels)
print("For n_clusters =", n_clusters,
"The average silhouette_score is :", silhouette_avg)
sample_silhouette_values = silhouette_samples(X, cluster_labels)
y_lower = 10
for i in range(n_clusters):
ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.nipy_spectral(float(i)/n_clusters)
ax1.fill_betweenx(np.arange(y_lower, y_upper)
,ith_cluster_silhouette_values
,facecolor=color
,alpha=0.7
)
ax1.text(-0.05
, y_lower + 0.5 * size_cluster_i
, str(i))
y_lower = y_upper + 10
ax1.set_title("The silhouette plot for the various clusters.")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
ax1.set_yticks([])
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
ax2.scatter(X[:, 0], X[:, 1]
,marker='o'
,s=8
,c=colors
)
centers = clusterer.cluster_centers_
# Draw white circles at cluster centers
ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
c="red", alpha=1, s=200)
ax2.set_title("The visualization of the clustered data.")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")
plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
"with n_clusters = %d" % n_clusters),
fontsize=14, fontweight='bold')
plt.show()
3、重要参数
初始质心:
- random_state:控制每次生成的初始质心都在相同位置,可以画学习曲线来确定最优的 random_state;
- 数init ='kmeans ++':使得初始质心彼此远离,以此来引导出比随机初始化更可靠的结果;
max_iter:最大迭代次数;
tol:两次迭代间Inertia下降的量,如果两次迭代之间Inertia下降的值小于tol所设定的 值,迭代就会停下;
三、DBSCAN
DBSCAN是一种基于密度的聚类方法,旨在寻找被低密度区域分离的高密度区域;
1、基于中心的定义是否相似的方法:
数据集中特定点的密度通过对该点半径之内的点计数(包括点本身)来估计,但点的密度取决于 指定的半径。
点分类:
- 稠密区域内部的点(核心点):该点的给定领域内的点的个数超过给定的阈值;
- 稠密区域边缘上的点(边界点):不是核心点,但它落在某个核心点的领域内;
- 稀疏区域中的点(噪声或背景点):既非核心点也非边界点的任何点;
2、算法原理
任意两个足够靠近(相互之间的距离在Eps之内)的核心点将放在同一个簇中。任何与核心点足够靠近的边界点也放到与核心点相同的簇中(如果一个边界点靠近不同簇的核心点,则可能需要解决平局问题),噪声点被丢弃。
- 将所有点标记为核心点、边界点或噪声点;
- 删除噪声点;
- 为距离在Eps之内的所有核心点之间赋予一条边;
- 每个彼此联通的核心点组成一个簇;
- 将每个边界点指派到一个与之关联的核心点的簇当中;
3、代码
from sklearn.datasets import make_moons
X,y = make_moons(200, noise = 0.05, random_state=0)
plt.scatter(X[:,0],X[:,1])
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)
centers = kmeans.cluster_centers_
plt.scatter(X[:,0],X[:,1], c = kmeans.labels_)
plt.scatter(centers[:,0], centers[:,1], color= "red")
# 使用DBSCAN
from sklearn.cluster import DBSCAN
db = DBSCAN(eps = 0.3, min_samples = 10)
db.fit(X)
plt.scatter(X[:,0],X[:,1], c = db.labels_)