Python 数据处理数据挖掘(七):K-Means聚类算法

声明:本文为学习笔记,侵权删

一、一些基本概念

K-Means是非监督学习的聚类算法,将一组数据分为K类(或者叫簇/cluster),每个簇有一个质心(centroid),同类的数据是围绕着质心被分类的。数据被分为了几类就有几个质心。

算法步骤:

1、先从原始数据集中随机选出K个数据,作为K个质心。

2、将剩余的数据分配到与之最相似的的质心的那个簇里。

3、第一次分类完成后,计算每个簇内样本的均值,并根据这个均值生成新的质心

4、重复2,3步,直至质心的变化距离小于某个值(主观设定),如果质心始终没法稳定下来,也可以设定一个最大迭代次数,即使质心仍不稳定一样会跳出循环。

解释:

如何判断数据间的相似性?

K-Means算法判断数据间的相似性的标准是欧氏距离(可以简单理解为中学里学的点到点之间的距离公式)。每个数据可以看做一个点,一个数据可以由多个维度组成(比如能不能考研上岸可以由:智商、努力、运气决定,智商、努力、运气都可以量化成数字,于是可以想象X轴是智商,Y轴是努力,Z轴是运气,这样能否考研上岸就可以变成三维坐标里的一个点了)。OK,现在数据变成坐标系里的点了,就可以计算欧氏距离了。欧氏距离越小,两个数据点越相似。

怎么计算每个簇内样本的均值?

每个轴上的数据分别算平均值,然后生成一个新的点:X轴的数据算平均值,Y轴的数据算平均值以此类推。新点的坐标:(mean(X),mean(Y),mean(Z)...)

如何判断结果的优劣?

这里主要讨论簇内误差平方和(SSE),简单来说:SSE=一个簇内所有点到质心的距离之和。SSE越小,效果通常更好。当然,SSE不能作为唯一的判定标准,因为SSE只考虑了簇内样本的相关性,并未考虑簇与簇之间的相关性。同时如果K值设定得过大,分了太多的簇,一个簇里总共没几个样本,那SSE当然就更小,但这并不代表分类效果更好。

同一组数据重复多次K-Means算法,做出来的结果是一样的吗?

不是,因为初始的质心是随即的,所以最终结果会有一定的随机性,但大体上是一样的。

二、实践

回忆一下之前做RFM分类中,将用户分为8大类。RFM分类有一个缺陷就是:分类的标准是主观的,因此对分类标准很难做到客观准确。而使用K-Means就会弥补这个缺陷。

这里用的数据集还是做RFM的那个。

 在使用K-Means的时候,还是选择R(最近一次消费至今的时间间隔),F(消费次数),M(消费总金额)三组数据最为用户分类的指标。

三组数据如下:

 产生一个问题

R,F,M作为坐标点,描述一个数据点,但是在K-Means中要计算数据点之间的距离来判断不同数据点的相似性。如果用原数据(如上图),计算距离的时候,M(消费总金额)对距离的影响太大了,进而导致模型效果不好(因为M数值很大,在计算数据点之间的欧式距离的时候,F,M在其中的作用就微乎其微,这是不理想的。举个例子:用户A只消费了1次,且消费时间距今20天,但金额巨大,有100万;用户B消费了100次,且最近一次消费距今5天,消费总金额也很大,共99万。计算A,B的相似性的时候,几乎只有M在起作用,因此可能就把A,B分为一类的,但很明显,A是暴发户,B是回头客,明显不是一类人嘛)。为了消除某个数据特别大/特别小而影响模型,我们需要对数据标准化处理(Normalization)。

数据标准化:把范围较大的数据限制到一个某一特定区间内。最常见的也是本例中用的标准化方法就是将值限制到均值为0,方差为1的正态分布中:X=\frac{x_{i}-\mu }{\sigma }  。好处有很多,就不一一赘述了。

#normalization
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_normalization = scaler.fit_transform(x)
x_df = pd.DataFrame(x_normalization,columns=x.columns,index=x.index)
print(x_normalization)
print(x_df)

 很明显,通过数据标准化,各项数值之间的差距变小了很多,因此模型也能更好得训练。

搭建K-Means模型并训练:

先按着之前做RFM模型的预想,将用户分为8类:

from sklearn.cluster import KMeans
KMeans_model = KMeans(n_clusters=8,random_state = 1)
KMeans_model.fit(x_normalization)
#质心
print(KMeans_model.cluster_centers_)
#每个样本的标签
print(KMeans_model.labels_)
#SSE
print(KMeans_model.inertia_)

 

二维数组表示8个质心的坐标(R,F,M)。

一维数组表示每个样本被分到了哪一类

最后的22651表示SSE

从RFM的角度来说,这样就分类成功了。但又有一个问题:

真的需要分成8类吗?是不是分的太细了?

这么问了,当然是不需要分为8类的,但为什么呢?因为:在做K-Means的时候,需要遵从肘部法则

肘部法则

肘部法则:在K-Means算法的时候,取不同的K值,计算出对应的SSE,绘制折线图,会出现一个K值K_,当K<K_时,SSE下降幅度很大,K≥K_时,SSE的下降幅度趋于平缓。

意义:SSE下降很大时,说明新增的每一个类对降低SSE都有显著效果,也就是说,新增的这个类是必要的。反之,则说明新增的类没啥大用了。

我们可以画出本例中,K取不同值时候的SSE的变化情况:

 从上图中可以看到,K≥4的时候(分成4类及以上的时候),SSE的下降已经不明显了,而K<3的时候SSE下降明显,因此选用K=3训练模型,并画出对应的3D散点图.

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
labels = KMeans_model.labels_
fig = plt.figure()
ax = Axes3D(fig)
color = ["dodgerblue", "seagreen", "lightcoral"]
for i in range(3):
    #获取分到的类是类i的所有数据
    d = x[labels == i]
    ax.scatter(d["Recency"], d["订单数量"], d["消费总金额"], color=color[i], label=f"type{i}")

ax.set_xlabel("R")
ax.set_ylabel("F")
ax.set_zlabel("M")
# 使用plt.legend()函数展示图例
plt.legend()
# 展示图像
plt.show()

 

 

本程序是在python中完成,基于sklearn.cluster中的k-means聚类包来实现数据的聚类,对于里面使用的数据格式如下:(注意更改程序中的相关参数) 138 0 124 1 127 2 129 3 119 4 127 5 124 6 120 7 123 8 147 9 188 10 212 11 229 12 240 13 240 14 241 15 240 16 242 17 174 18 130 19 132 20 119 21 48 22 37 23 49 0 42 1 34 2 26 3 20 4 21 5 23 6 13 7 19 8 18 9 36 10 25 11 20 12 19 13 19 14 5 15 29 16 22 17 13 18 46 19 15 20 8 21 33 22 41 23 69 0 56 1 49 2 40 3 52 4 62 5 54 6 32 7 38 8 44 9 55 10 70 11 74 12 105 13 107 14 56 15 55 16 65 17 100 18 195 19 136 20 87 21 64 22 77 23 61 0 53 1 47 2 33 3 34 4 28 5 41 6 40 7 38 8 33 9 26 10 31 11 31 12 13 13 17 14 17 15 25 16 17 17 17 18 14 19 16 20 17 21 29 22 44 23 37 0 32 1 34 2 26 3 23 4 25 5 25 6 27 7 30 8 25 9 17 10 12 11 12 12 12 13 7 14 6 15 6 16 12 17 12 18 39 19 34 20 32 21 34 22 35 23 33 0 57 1 81 2 77 3 68 4 61 5 60 6 56 7 67 8 102 9 89 10 62 11 57 12 57 13 64 14 62 15 69 16 81 17 77 18 64 19 62 20 79 21 75 22 57 23 73 0 88 1 75 2 70 3 77 4 73 5 72 6 76 7 76 8 74 9 98 10 90 11 90 12 85 13 79 14 79 15 88 16 88 17 81 18 84 19 89 20 79 21 68 22 55 23 63 0 62 1 58 2 58 3 56 4 60 5 56 6 56 7 58 8 56 9 65 10 61 11 60 12 60 13 61 14 65 15 55 16 56 17 61 18 64 19 69 20 83 21 87 22 84 23 41 0 35 1 38 2 45 3 44 4 49 5 55 6 47 7 47 8 29 9 14 10 12 11 4 12 10 13 9 14 7 15 7 16 11 17 12 18 14 19 22 20 29 21 23 22 33 23 34 0 38 1 38 2 37 3 37 4 34 5 24 6 47 7 70 8 41 9 6 10 23 11 4 12 15 13 3 14 28 15 17 16 31 17 39 18 42 19 54 20 47 21 68 22
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三头猪等于一头大猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值