文章目录

  • 一、算法介绍
  • 二、算法原理
  • 三、算法流程
  • 四、实例实现---超市客户分群
  • 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、前后两次,聚类结果变化量小于某个阈值

mean_square_error函数python mean python_聚类

四、实例实现—超市客户分群

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)

八、算法评估

mean_square_error函数python mean python_python_02

mean_square_error函数python mean python_迭代_03

mean_square_error函数python mean python_聚类算法_04


轮廓系数范围[-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值