点一下关注吧!!!非常感谢!!持续更新!!!
目前已经更新到了:
- Hadoop(已更完)
- HDFS(已更完)
- MapReduce(已更完)
- Hive(已更完)
- Flume(已更完)
- Sqoop(已更完)
- Zookeeper(已更完)
- HBase(已更完)
- Redis (已更完)
- Kafka(已更完)
- Spark(已更完)
- Flink(已更完)
- ClickHouse(已更完)
- Kudu(已更完)
- Druid(已更完)
- Kylin(已更完)
- Elasticsearch(已更完)
- DataX(已更完)
- Tez(已更完)
- 数据挖掘(正在更新...)
章节内容
上节我们完成了如下的内容:
- 无监督学习算法
- KMeans 基本原理
- KMeans 簇内误差平方和
Python实现
导入依赖
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
# 解决坐标轴刻度负号乱码
plt.rcParams['axes.unicode_minus'] = False
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['Simhei']
导入数据集
此处使用鸢尾花数据集为例:
python
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
#导入数据集
iris = pd.read_csv("iris.txt",header = None)
iris.head()
iris.shape
执行结果如下图所示:
编写距离计算函数
我们需要定义一个两个长度相等的数组之间欧式距离计算函数,在不直接应用计算结果,只比较距离远近的情况下,我们可以用距离平方和代替距离进行比较,化简开平方运算,从而减少函数计算量。此外需要说明的是,涉及到距离计算的,一定要注意量纲的统一。
如果量纲不统一的话,模型极易偏向量纲大的那一方。
- 函数功能:计算两个数据集之间的欧式距离
- 输入:两个 array 数据集
- 返回:两个数据集之间的欧式距离(此处用距离平方和代替距离)
python
def distEclud(arrA, arrB):
d = arrA - arrB
dist = np.sum(np.power(d, 2), axis=1)
return dist
编写随机函数生成质心函数
在定义随机质心生成函数时,首先需要计算每列数值的范围,然后从该范围中随机生成指定个数的质心。
- 函数功能:随机生成 k 个质心
- 参数说明:dataSet 包含标签的数据集,k 是簇的个数,返回 data_cent 是 k 个质心
python
def randCent(dataSet, k):
# n为列数,假设dataSet是一个DataFrame
n = dataSet.shape[1] # 获取数据集的列数(例如 iris 数据集有 5 列)
# 获取每一列的最小值和最大值(仅使用前 n-1 列,最后一列是标签或类别)
data_min = dataSet.iloc[:, :n-1].min() # 前4列的最小值
data_max = dataSet.iloc[:, :n-1].max() # 前4列的最大值
# 在最小值和最大值之间生成 k 个随机中心点,形状为 (k, n-1)
data_cent = np.random.uniform(data_min, data_max, (k, n-1))
return data_cent
经过上述定义,在 iris 中随机生成了三个质心:
执行对应的代码:
python
iris_cent = randCent(iris, 3)
iris_cent
执行结果如下图所示:
编写 K-Means 聚类函数
在执行 K-Means 的时候,需要不断的迭代质心,因此我们需要两个可迭代的容器来完成该目标:
- 第一个容器用于存放和更新质心,该容器可考虑使用 list 来执行,list 不仅是可迭代对象,同时 list 内不同元素索引位置也可以用于标记和区分各质心,即各簇的编号:即代码中的 centroids。
- 第二个容器则需要记录,保存和更新到各点到质心之间的距离,并能够方便对其进行比较,该容器考虑使用一个三列的数组来执行。
第二个容器中:
- 第一列用于存放最近一次计算完成后某点到各质心的最短距离
- 第二列用于记录最近一次计算完成后根据最短距离得到的代表对应质心的数值索引,即所述簇,即质心编号。
- 第三列用于存放上一次某点对应质心编号(某点所属簇),后两列用于比较质心发生变化后某点所属簇的情况是否发生变化。
函数功能:K-均值聚类算法
参数说明:
- dataSet 带标签数据集
- k 簇的个数
- distMeas 距离计算函数
- createCent 随机质心生成函数
返回:
- centroids 质心
- result_set 所有数据划分结果
python
# 假设 distEclud 和 randCent 是你定义的距离测量函数和随机生成质心函数
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
# 获取数据集的维度,m 是行数,n 是列数
m, n = dataSet.shape # m是行数(数据量),n是列数(例如 iris 为 150*5)
# 初始化质心 centroids,生成 k 个随机质心
centroids = createCent(dataSet, k) # centroids 为 k*n 的矩阵(随机生成)
# 初始化 clusterAssment 矩阵,用来存储每个点的簇分配结果
# clusterAssment: [该行到最近质心的距离, 本次迭代中最近质心编号, 上次迭代中最近质心编号]
clusterAssment = np.zeros((m, 3)) # 初始化为 m*3 的矩阵
clusterAssment[:, 0] = np.inf # 设置初始距离为无穷大
clusterAssment[:, 1:3] = -1 # 质心编号初始化为 -1
# 将数据集和 clusterAssment 合并,形成 result_set
result_set = pd.concat([dataSet, pd.DataFrame(clusterAssment)], axis=1, ignore_index=True)
# 标记簇是否发生变化
clusterChanged = True
while clusterChanged:
clusterChanged = False
# 遍历每个样本点,计算它与每个质心的距离,并更新簇分配信息
for i in range(m):
# 计算当前数据点到所有质心的距离
dist = distMeas(dataSet.iloc[i, :n-1].values, centroids) # 计算距离,dist 是 k*1 的矩阵
# 记录最小距离和对应质心的索引
result_set.iloc[i, n] = dist.min() # 记录最小距离
result_set.iloc[i, n+1] = np.where(dist == dist.min())[0][0] # 记录最近质心的索引
# 检查当前簇分配与上次是否完全一致
clusterChanged = not (result_set.iloc[:, -1] == result_set.iloc[:, -2]).all()
# 如果簇分配发生变化,则更新质心和 result_set
if clusterChanged:
# 根据新的簇分配,计算新的质心位置
cent_df = result_set.groupby(n+1).mean() # 根据最新簇分配,分组计算新的质心
centroids = cent_df.iloc[:, :n-1].values # 更新质心,使用新的均值作为质心
# 更新簇分配编码,将当前簇分配替换为上次的簇分配
result_set.iloc[:, -1] = result_set.iloc[:, -2]
return centroids, result_set
将鸢尾花数据带进去,查看模型的效果:
python
iris_cent,iris_result = kMeans(iris, 3)
iris_cent
iris_result.head()
执行结果若下所示:
有几点需要特别注意:
- 设置统一的操作对象 result_set,为了调用和使用的方便,这里将 clusterAssment 转换为了 DataFrame 并与输入 DataFrame 合并,组成的对象作为后续调用的统一对象,该对象内即保存了原始数据,也保存了迭代运算的中间结果,包括数据所属簇标记和数据质心距离等,该对象同时也作为最终函数的返回结果
- 判断质心是否发生改变条件,在K-Means 中判断质心是否发生改变,即判断是否继续进行下一步迭代的依据并不是某点距离新的质心距离变短,而是某点新的距离向量(到各质心的距离)中最短的分量位置是否发生变化,即质心变化后某点是否应归属另外的簇,在质心变化导致各点所属簇发生变化的过程中,点到质心的距离不一定会变短,即判断条件不能用下述语句表示
- 质心和类别一一对应,在最后生成的结果中,centroids的行标为 result_set 中各点所属类别