文章目录
- 一、算法介绍
- 二、算法原理
- 三、算法流程
- 四、实例实现---超市客户分群
- 1、基本实现
- 2、动态展示实现
- 3、sklearn实现
- 五、sklearn数据标准化
- 六、算法特点
- 七、算法API
- 八、算法评估
一、算法介绍
k-means算法也就是K均值算法,是最经典的聚类算法
二、算法原理
k-means算法以k为参数,把n个对象分为k个簇,使簇内具有较高的相似度,而簇间的相似度较低
三、算法流程
1、随机选择k个点 作为每组的中心(聚类中心)
2、将每个点 归属为 和其最相似的 聚类中心
计算每个点到K个聚类中心的距离,归属为和其距离最小的聚类中心
3、重新确定聚类中心(聚类中心就取每类的点的平均值)
4、重复2和3步
停止条件
精准停止条件:
1、聚类中心的位置不再发生变化
2、前后两次,聚类结果不再发生变化
粗略停止条件:
1、达到一定的迭代次数
2、前后两次,聚类中心位置差距小于某个阈值
2、前后两次,聚类结果变化量小于某个阈值
四、实例实现—超市客户分群
1、基本实现
import pandas as pd
import numpy as np
import pdb # 断点
# 需求:客户分3组,分别是普通会员、VIP、SVIP
df = pd.read_csv("D:\pycharm\pythonProject\k-means算法./company.csv", encoding='ANSI')
# 客户年龄和会员卡关系不大
X = df[["平均每次消费金额", "平均消费周期(天)"]]
"""
# 聚类算法 通常不进行 数据拆分
# print(X)
# 算法过程
# 1. 随机指定K个聚类中心
center = np.array([[13, 1], # 类0
[789, 90], # 类1
[100, 40]]) # 类2
label_list = []
# 2. 将每个样本归属为和其最近的聚类中心
for sample in X.values: # X.values是二维数组
# print(sample)
# 计算该样本到3个聚类中心的距离
dis = np.sqrt(((center - sample) ** 2).sum(axis=1))
# 数组最小值所在的索引
# print(np.argmin(dis))
label = np.argmin(dis) # 所属的类别
label_list.append(label)
print("第一次计算后,归属的结果", label_list)
X["组号"] = label_list
print("第一次聚类结果\n", X)
# 计算新的聚类中心
new_center = X.groupby(by="组号")[["平均每次消费金额", "平均消费周期(天)"]].mean()
print("新的聚类中心\n", new_center)
"""
def k_mean(center):
label_list = [] # 用于存储所有样本的类别
# 2. 将每个样本归属为和其最近的聚类中心
for sample in X[["平均每次消费金额", "平均消费周期(天)"]].values: # X.values是二维数组
# print(sample)
# 计算该样本到3个聚类中心的距离
dis = np.sqrt(((center - sample) ** 2).sum(axis=1))
# 数组最小值所在的索引
# print(np.argmin(dis))
label = np.argmin(dis) # 所属的类别
label_list.append(label)
X["组号"] = label_list
# 计算新的聚类中心
new_center = X.groupby(by="组号")[["平均每次消费金额", "平均消费周期(天)"]].mean()
return new_center.values
# 初始化聚类中心
# center = np.array([[13, 1], # 类0
# [789, 90], # 类1
# [100, 40]]) # 类2
center = np.array([[317, 10], # 类0
[147, 13], # 类1
[190, 3]]) # 类2
timer = 0
while True:
# 输入聚类中心;输出新的聚类中心
new_center = k_mean(center)
timer += 1
print("第{}次的聚类结果是{}".format(timer, X["组号"].tolist()))
# pdb.set_trace() # 设置一个断点
# 如果前后两次聚类中心一致,就退出循环
if (center == new_center).all():
break
else:
# 否则 更新聚类中心
center = new_center
print("总共经历{}次聚类".format(timer))
print("最终聚类结果\n", X["组号"].tolist())
"""
初始聚类中心的选择
选择的不合适,可能出现的问题
1. 聚类的数目 小于 我们需要的数目
2. 聚类迭代次数增加
"""
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(X["平均每次消费金额"], X["平均消费周期(天)"], c=X["组号"])
plt.xlabel("平均每次消费金额")
plt.ylabel("平均消费周期(天)")
plt.scatter(new_center[:,0], new_center[:,1], marker='*', s=200, c=[0,1,2])
plt.show()
2、动态展示实现
import pandas as pd
import numpy as np
import pdb # 断点
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False
# 需求:客户分3组,分别是普通会员、VIP、SVIP
df = pd.read_csv("D:\pycharm\pythonProject\k-means算法./company.csv", encoding='ANSI')
# 客户年龄和会员卡关系不大
X = df[["平均每次消费金额", "平均消费周期(天)"]]
def k_mean(center):
label_list = [] # 用于存储所有样本的类别
# 2. 将每个样本归属为和其最近的聚类中心
for sample in X[["平均每次消费金额", "平均消费周期(天)"]].values: # X.values是二维数组
# print(sample)
# 计算该样本到3个聚类中心的距离
dis = np.sqrt(((center - sample) ** 2).sum(axis=1))
# 数组最小值所在的索引
# print(np.argmin(dis))
label = np.argmin(dis) # 所属的类别
label_list.append(label)
X["组号"] = label_list
# 计算新的聚类中心
new_center = X.groupby(by="组号")[["平均每次消费金额", "平均消费周期(天)"]].mean()
return new_center.values
center = np.array([[317, 10], # 类0
[147, 13], # 类1
[190, 3]]) # 类2
def show_result(timer):
plt.scatter(X["平均每次消费金额"], X["平均消费周期(天)"], c=X["组号"])
plt.xlabel("平均每次消费金额")
plt.ylabel("平均消费周期(天)")
plt.scatter(new_center[:, 0], new_center[:, 1], marker='*', s=200, c=[0, 1, 2])
plt.title("第{}次的结果".format(timer))
# plt.show()
plt.pause(3) # 每隔3秒显示一下
# 打开动画开关
plt.ion()
timer = 0
while True:
# 输入聚类中心;输出新的聚类中心
new_center = k_mean(center)
timer += 1
plt.cla()
show_result(timer)
print("第{}次的聚类结果是{}".format(timer, X["组号"].tolist()))
# pdb.set_trace() # 设置一个断点
# 如果前后两次聚类中心一致,就退出循环
if (center == new_center).all():
break
else:
# 否则 更新聚类中心
center = new_center
print("总共经历{}次聚类".format(timer))
print("最终聚类结果\n", X["组号"].tolist())
plt.ioff()
plt.show()
3、sklearn实现
初始聚类中心的选择
选择的不合适,可能出现的问题
1、聚类的数目 小于 我们需要的数目
2、 聚类迭代次数增加
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans # k-means聚类算法的API
from sklearn.preprocessing import StandardScaler # 标准差标准化
from sklearn.preprocessing import MinMaxScaler # 离差标准化
from sklearn.preprocessing import MaxAbsScaler # 小数定标标准化
# 需求:客户分3组,分别是普通会员、VIP、SVIP
df = pd.read_csv("D:\pycharm\pythonProject\k-means算法./company.csv", encoding='ANSI')
# 客户年龄和会员卡关系不大
X = df[["平均每次消费金额", "平均消费周期(天)"]]
min_max_sca = MinMaxScaler()
new_X = min_max_sca.fit_transform(X)
# ---------------------------算法阶段-------------------------------------
# 1. 算法对象实例化
# 参数 n_clusters 即k值
km = KMeans(n_clusters=3, random_state=1)
# 2. 拟合
# 聚类算法 只需要传入 特征 就可以进行拟合
km.fit(new_X)
# 3. 查看聚类结果
y_pred = km.predict(new_X)
print("预测结果", y_pred)
# print(km.predict([[500, 1]]))
#
# print("查看聚类中心\n", km.cluster_centers_)
"""
fit 的过程。计算出最终的聚类中心
predict的过程,就是计算该样本到各个聚类中心的距离,输入距离最小的聚类中心的标签
"""
center = km.cluster_centers_
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(X["平均每次消费金额"], X["平均消费周期(天)"], c=y_pred)
plt.xlabel("平均每次消费金额")
plt.ylabel("平均消费周期(天)")
# plt.scatter(center[:, 0], center[:, 1], marker='*', s=200, c=[0, 1, 2])
plt.show()
五、sklearn数据标准化
from sklearn.preprocessing import StandardScaler # 标准差标准化
from sklearn.preprocessing import MinMaxScaler # 离差标准化
from sklearn.preprocessing import MaxAbsScaler # 小数定标标准化
import pandas as pd
df = pd.read_csv("D:\pycharm\pythonProject\k-means算法./company.csv", encoding='ANSI')
# 客户年龄和会员卡关系不大
X = df[["平均每次消费金额", "平均消费周期(天)"]].head()
print(X)
min_max_sca = MinMaxScaler()
# X-min / (max-min)
# min_max_sca.fit(X[["平均每次消费金额"]]) # 计算最小值 最大值
#
# out = min_max_sca.transform(X[["平均每次消费金额"]])
# print(out)
# min_max_sca.fit(X)
# X = min_max_sca.transform(X)
# print(X)
X = min_max_sca.fit_transform(X)
print(X)
# sklearn的返回值 大部分情况都是 数组的结构
六、算法特点
1、原理简单,算法易实现
2、当簇和簇之间区别明显时,算法效果较好
3、需要自己预先给定K值
4、初始质心的选择对结果影响很大
5、采用迭代的方法,可能只得到局部最优解,无法得到全局最优解
6、孤立点和含噪声数据对结果的影响挺大
七、算法API
def init(self, n_clusters=8, *, init=‘k-means++’, n_init=10,
max_iter=300, tol=1e-4, precompute_distances=‘deprecated’,
verbose=0, random_state=None, copy_x=True,
n_jobs=‘deprecated’, algorithm=‘auto’):
n_clusters:聚类中心
max_iter:执行一次k-means算法所进行的最大迭代数
n_init:用不同的质心初始化值运行算法的次数,最终解是在inertia意义下选出的最优结果。
init:指定初始化方法,默认值为 ‘k-means++’
三个可选值:’k-means++’, ‘random’,或者传递一个ndarray向量。
precompute_distances:预计算距离,计算速度更快但占用更多内存
三个可选值,‘auto’,True 或者 False。
tol:默认值= 1e-4 与inertia结合来确定收敛条件。
n_jobs:指定计算所用的进程数
random_state:用于初始化质心的生成器(generator)
八、算法评估
轮廓系数范围[-1,1]
import pandas as pd
from sklearn.preprocessing import StandardScaler # 标准差标准化
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import silhouette_score # 轮廓系数
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False
air = pd.read_csv("D:\pycharm\pythonProject\k-means算法./air_data.csv", encoding="ANSI")
#print(air.columns)
# 一、数据清洗
# 指定如果两个任意一个存在空值,删除该行
air.dropna(axis=0, how='any', inplace=True)
# 如果两年的票价都是0,删除该行
air["票价"] = air["SUM_YR_1"] + air["SUM_YR_2"]
con1 = air["票价"] <= 0
con2 = air["avg_discount"] <= 0
con3 = air["SEG_KM_SUM"] <= 0
# 票价 飞行历程、折扣 任意一个小于等于0 删除该行
con = con1 | con2 | con3
drop_index = air.loc[con, :].index # 获取要删除的索引名字
air.drop(index=drop_index, inplace=True)
# 二、构建LRFMC特征
air["LOAD_TIME"] = pd.to_datetime(air["LOAD_TIME"])
air["FFP_DATE"] = pd.to_datetime(air["FFP_DATE"])
L = (air["LOAD_TIME"] - air["FFP_DATE"]) / pd.Timedelta(days=30)
X = air[["LAST_TO_END", "FLIGHT_COUNT", "SEG_KM_SUM", "avg_discount"]]
X.columns = ["R", "F", "M", "C"]
X.insert(loc=0, column="L", value=L)
# 三、进行聚类
# 基于5个关键特征 进行客户 分组 分4组
std_sca = MinMaxScaler()
X = std_sca.fit_transform(X)
print("标准化后\n", X)
# ---------------------------算法阶段-------------------------------------
# 1. 算法对象实例化
# 参数 n_clusters 即k值
km = KMeans(n_clusters=4, random_state=1)
# 2. 拟合
# 聚类算法 只需要传入 特征 就可以进行拟合
km.fit(X)
# 3. 预测结果
y_pred = km.predict(X)
# print("预测结果", y_pred)
# 4. 效果评估
# 得分:所有样本到各自聚类中心的距离 的和
# 得分越靠近0 聚类效果是最好的(-inf, 0]
score = km.score(X)
print("聚类算法评估结果", score)
sil_score = silhouette_score(X, y_pred)
print("轮库系数的得分", sil_score)
如何选择最优的K值
设置一些列K值
测试不同K值对应的score、轮廓系数; 选择评估结果最好的k值