python学习_day17---k_mean

一、算法介绍

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值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
保留原本功能优化以下代码import pandas as pd import numpy as np import matplotlib.pyplot as plt # 1.读取并查看数据 bike_day = pd.read_csv("C:/Users/15020/Desktop/26.bike_day.csv") print(bike_day.head(5)) # 前5行 print(bike_day.tail(2)) #后2行 #2.处理数据并导出到文件 bike_day_user = bike_day[['instant','dteday','yr', 'casual', 'registered']].dropna() bike_day_user.to_csv('bike_day_user.txt', sep=' ',index=False, header=False) #3.读取数据并添加新列并导出到新文件 bike_day_user = pd.read_csv('bike_day_user.txt', sep=' ', header=None, names=['instant','dteday','yr', 'casual',"registered"]) bike_day_user['cnt'] = bike_day_user['casual'] + bike_day_user['registered'] bike_day_user.to_excel('bike_day_user_cnt.xlsx', index=False) #4.读取数据并进行统计 bike_day_user_cnt = pd.read_excel('bike_day_user_cnt.xlsx') print('cnt最大值:',bike_day_user_cnt['cnt'].max()) print('ent最小值:',bike_day_user_cnt['cnt'].min()) print('2011号cnt年平均值:',bike_day_user_cnt[bike_day_user_cnt['yr'] == 0]['cnt'].mean()) print('2012年cnt年平均值:',bike_day_user_cnt[bike_day_user_cnt['yr'] == 1]['cnt'].mean()) print('2011年月严始值:', bike_day_user_cnt[bike_day_user_cnt['yr'] == 0].groupby('mnth')['cnt'].mean()) print('2022年月平均值:', bike_day_user_cnt[bike_day_user_cnt['yr'] == 1].groupby('mnth')['cnt'].mean()) # 5.可视化并保存图像 fig, ax = plt.subplots() ax.barh(bike_day_user_cnt['mnth'], bike_day_user_cnt[bike_day_user_cnt['yr'] == 0].groupby('mnth')['cnt'].mean(), color='blue', label='2011') ax.barh(bike_day_user_cnt['mnth'], bike_day_user_cnt[bike_day_user_cnt['yr'] == 1].groupby('mnth')['cnt'].mean(), color='lightblue', label='2012') ax.set_yticks(np.arange(1,13)) ax.set_yticklabels(['Jan','Feb','Mar', 'Apr', 'May','Jun','Jul','Aug', 'sep', 'Oct','Nov','Dec']) ax.set_xlabel('Average number of shared bike users') ax.set_title('Monthly Average Number of Shared Bike Users in 2011-2012') ax.legend() fig.savefig('bike_day_user_cnt.png', dpi=300)
06-03
好的,我会保留原本功能并优化代码。以下是优化后的代码: ```python import pandas as pd import numpy as np import matplotlib.pyplot as plt # 1.读取并查看数据 bike_day = pd.read_csv("C:/Users/15020/Desktop/26.bike_day.csv") print(bike_day.head()) print(bike_day.tail(2)) # 2.处理数据并导出到文件 bike_day_user = bike_day[['instant', 'dteday', 'yr', 'casual', 'registered']].dropna() bike_day_user.to_csv('bike_day_user.txt', sep=' ', index=False, header=False) # 3.读取数据并添加新列并导出到新文件 bike_day_user = pd.read_csv('bike_day_user.txt', sep=' ', header=None, names=['instant', 'dteday', 'yr', 'casual', 'registered']) bike_day_user['cnt'] = bike_day_user['casual'] + bike_day_user['registered'] bike_day_user.to_excel('bike_day_user_cnt.xlsx', index=False) # 4.读取数据并进行统计 bike_day_user_cnt = pd.read_excel('bike_day_user_cnt.xlsx') cnt_max = bike_day_user_cnt['cnt'].max() cnt_min = bike_day_user_cnt['cnt'].min() cnt_mean_2011 = bike_day_user_cnt[bike_day_user_cnt['yr'] == 0]['cnt'].mean() cnt_mean_2012 = bike_day_user_cnt[bike_day_user_cnt['yr'] == 1]['cnt'].mean() cnt_mean_mnth_2011 = bike_day_user_cnt[bike_day_user_cnt['yr'] == 0].groupby('mnth')['cnt'].mean() cnt_mean_mnth_2012 = bike_day_user_cnt[bike_day_user_cnt['yr'] == 1].groupby('mnth')['cnt'].mean() print(f'cnt最大值: {cnt_max}') print(f'cnt最小值: {cnt_min}') print(f'2011号cnt年平均值: {cnt_mean_2011}') print(f'2012年cnt年平均值: {cnt_mean_2012}') print(f'2011年月严始值: {cnt_mean_mnth_2011}') print(f'2012年月平均值: {cnt_mean_mnth_2012}') # 5.可视化并保存图像 fig, ax = plt.subplots() ax.barh(bike_day_user_cnt['mnth'], cnt_mean_mnth_2011, color='blue', label='2011') ax.barh(bike_day_user_cnt['mnth'], cnt_mean_mnth_2012, color='lightblue', label='2012') ax.set_yticks(np.arange(1, 13)) ax.set_yticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']) ax.set_xlabel('Average number of shared bike users') ax.set_title('Monthly Average Number of Shared Bike Users in 2011-2012') ax.legend() fig.savefig('bike_day_user_cnt.png', dpi=300) ``` 我做了以下几点优化: 1. 使用 f-string 格式化输出字符串,让代码更加简洁易读。 2. 将统计结果赋值给变量,方便后续使用。 3. 在可视化时,直接使用已经统计好的月份平均值,避免了重复计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值