K-Means算法是无监督的聚类算法,它实现起来比较简单,聚类效果也不错,因此应用很广泛。
K-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。
样本:
要求
通过客户消费频率与金额为客户群体分3类
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
第一步:
#读取样本,取需要的属性
data = pd.read_csv('company.csv', sep=',', encoding='gbk')
datas = data[['平均消费周期(天)', '平均每次消费金额']]
我们可以看一下原始分布
#绘制出分布
plt.figure()
plt.scatter(data['平均消费周期(天)'], data['平均每次消费金额'])
plt.title('original')
plt.show()
x轴为消费频率,y为平均消费
第二步,实现分类函数:
#分类
def d(datas, a): #datas是样本,a是自己随机选取的3个质心
type = []
dian = {}
for i in range(datas.shape[0]):
li = np.array([datas.loc[i, :][0], datas.loc[i, :][1]]) #遍历样本的每一行
d = np.sum((a.iloc[:, :-1] - li) ** 2, axis=1) ** (1 / 2) #每一行的样本与自己选取的质心,计算欧氏距离
type.append(d.sort_values(axis=0).index[0]) #根据欧式距离升序排列,选取最小值,添加到type列表中
datas['type'] = type #为样本表添加上分好的类
one = datas[datas['type'] == 'one'] #根据不同的类的位置总和平均值求出一个新的质心
dian['one'] = [one[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均消费周期(天)'],
one[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均每次消费金额']
]
two = datas[datas['type'] == 'two']
dian['two'] = [two[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均消费周期(天)'],
two[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均每次消费金额'],
]
three = datas[datas['type'] == 'three']
dian['three'] = [three[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均消费周期(天)'],
three[['平均消费周期(天)', '平均每次消费金额']].mean(axis=0)['平均每次消费金额'],
]
cen = pd.DataFrame(dian).T
cen['type'] = ['one', 'two', 'three']
return cen #返回3类的质心
第三步:实现通过不同分类绘制散点图
##根据每一类绘制散点图
def denlei(datas, a):
plt.figure()
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
type = []
for i in range(datas.shape[0]):
li = np.array([datas.loc[i, :][0], datas.loc[i, :][1]])
d = np.sum((a.iloc[:, :-1] - li) ** 2, axis=1) ** (1 / 2)
type.append(d.sort_values(axis=0).index[0])
datas['type'] = type
plt.scatter(a.iloc[:, 0][0], a.iloc[:, 1][0], marker="*", color='r', edgecolors='k')
plt.scatter(a.iloc[:, 0][1], a.iloc[:, 1][1], marker="^", color='b', edgecolors='k')
plt.scatter(a.iloc[:, 0][2], a.iloc[:, 1][2], marker="s", color='c', edgecolors='k')
plt.legend(['第一类', '第二类', '第三类'])
one = datas[datas['type'] == 'one']
x = one[['平均消费周期(天)', '平均每次消费金额']]['平均消费周期(天)']
y = one[['平均消费周期(天)', '平均每次消费金额']]['平均每次消费金额']
plt.scatter(x, y, marker='*', color='r')
two = datas[datas['type'] == 'two']
x = two[['平均消费周期(天)', '平均每次消费金额']]['平均消费周期(天)']
y = two[['平均消费周期(天)', '平均每次消费金额']]['平均每次消费金额']
plt.scatter(x, y, marker='^', color='b')
three = datas[datas['type'] == 'three']
x = three[['平均消费周期(天)', '平均每次消费金额']]['平均消费周期(天)']
y = three[['平均消费周期(天)', '平均每次消费金额']]['平均每次消费金额']
plt.scatter(x, y, marker='+', color='c')
plt.xlabel('消费频率(天数)')
plt.ylabel('消费金额(元)')
plt.show()
第四步,执行函数:
#大致选出3类的质心,注意,选中的3类第一次分类后,3类必须都有值,否则重新选点
a = pd.DataFrame({'one': [2.0, 100.0],
'two': [40.0, 600.0],
'three': [80.0, 200.0]}).T
a['type'] = ['one', 'two', 'three']
#第一类,第二类,第三类
a = a
n = 0
while True:
n += 1
back = d(datas, a)
if np.all(back == a):
#结束条件,当最后一次循环和上一次循环返回的3个质心相同就跳出循环
print(n)
print('----', back)
denlei(datas, back) #执行绘图函数
break
else:
denlei(datas, a) #否则传入上一次返回的3个质心继续执行分类
a = back
结果:
函数总共执行了3次
第一次,根据我自己给的3个质心分类后的散点图
第二次:分类函数根据第一次分类选取3个质心重新分类
第三次,最后的结果
3个质心
最后就完成对客户群体的分类