k-means算法
基于划分的方法
给定一个有n个数据对象的数据集D以及要生成的簇数k(k < n),划分方法D中对象分配到k个分组,每一个分组就代表一个聚类。然后用一个目标函数来评估划分的质量,使得簇内对象相似,而与其它簇内的对象相异。
特点
- 很适合发现中小规模的数据集中的球状互斥的簇。
- 是基于距离
- 可以用均值或中心点等代表簇中心
K-means简介
由于具有出色的速度和良好的可扩展性,K-means聚类算法算得上是最著名的聚类方法。K-means算法是一个重复移动类中心点的过程,把类的中心点,也称重心(centroids),移动到其包含成员的平均位置,然后重新划分其内部成员。
K-means算法相关概念
距离度量
曼哈顿距离
在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是“曼哈顿距离”,也称为“城市街区距离”。
欧式距离
欧氏距离是一个通常采用的距离定义,指在m维空间中两个点之间的真实距离
余弦相似度
余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。
余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。
常见距离代码实现
import numpy as np
def distance(x,y,p=2):
'''
input:x(ndarray):第一个样本的坐标
y(ndarray):第二个样本的坐标
p(int):等于1时为曼哈顿距离,等于2时为欧氏距离
output:distance(float):x到y的距离
'''
if(p==1):
return sum(abs(x-y))
elif(p==2):
return (sum((x-y)**2))**(1/2)
计算一个样本与数据集中所有样本的欧氏距离的平方
def euclidean_distance(one_sample, X):
one_sample = one_sample.reshape(1, -1)
distances = np.power(np.tile(one_sample, (X.shape[0], 1)) - X, 2).sum(axis=1)
return distances
质心与目标函数
质心
质量中心的简称。质点系的质心是质点系质量分布的平均位置。
而质心可能随数据临近性度量不同和聚类目标不同而改变。
邻近性度量
通常对欧式空间的点使用欧几里得距离,对文档采用余弦相似性。
在此,我们采用欧式距离
目标函数
对于邻近性度量为欧几里得距离的数据,我们通常采用误差的平方和(Sum of the Squared Error,SSE)作为度量聚类质量的目标函数。换言之,计算每个数据点的误差,即它到最近质心的欧式距离,然后计算误差的平方和。
K-means算法流程
python类初始化
class MyKmeans():
"""Kmeans聚类算法.
Parameters:
-----------
k: int
聚类的数目.
max_iterations: int
最大迭代次数.
varepsilon: float
判断是否收敛, 如果上一次的所有k个聚类中心与本次的所有k个聚类中心的差都小于varepsilon,
则说明算法已经收敛
"""
def __init__(self, k=5, max_iterations=300, varepsilon=0.0001, random_state=2):
self.k = k
self.max_iterations = max_iterations
self.varepsilon = varepsilon
np.random.seed(random_state)
选取初始质心
# 从所有样本中随机选取self.k样本作为初始的聚类中心
def init_random_centroids(self, X):
n_samples, n_features = np.shape(X)
centroids = np.zeros((self.k, n_features))
for i in range(self.k):
centroid = X[np.random.choice(range(n_samples))]
centroids[i] = centroid
return centroids
将每个对象分配到最相似的簇
# 返回距离该样本最近的一个中心索引[0, self.k)
def _closest_centroid(self, sample, centroids):
distances = euclidean_distance(sample, centroids)
closest_i = np.argmin(distances)
return closest_i
# 将所有样本进行归类,归类规则就是将该样本归类到与其最近的中心
def create_clusters(self, centroids, X):
n_samples = np.shape(X)[0]
clusters = [[] for _ in range(self.k)]
for sample_i, sample in enumerate(X):
centroid_i = self._closest_centroid(sample, centroids)
clusters[centroid_i].append(sample_i)
return clusters
更新簇的质心
# 对中心进行更新
def update_centroids(self, clusters, X):
n_features = np.shape(X)[1]
centroids = np.zeros((self.k, n_features))
for i, cluster in enumerate(clusters):
centroid = np.mean(X[cluster], axis=0)
centroids[i] = centroid
return centroids
计算所属标签和质心
# 将所有样本进行归类,其所在的类别的索引就是其类别标签
def get_cluster_labels(self, clusters, X):
y_pred = np.zeros(np.shape(X)[0])
for cluster_i, cluster in enumerate(clusters):
for sample_i in cluster:
y_pred[sample_i] = cluster_i
return y_pred
# 对clusters,返回其聚类结果的质心
def get_cluster_centroids(self, clusters, X):
centroids = np.zeros((self.k,np.shape(X)[1]))
for i, cluster in enumerate(clusters):
centroid = np.mean(X[cluster], axis=0)
centroids[i] = centroid
return centroids
聚类
# 对整个数据集X进行Kmeans聚类,返回其聚类的标签
def predict(self, X):
# 从所有样本中随机选取self.k样本作为初始的聚类中心
centroids = self.init_random_centroids(X)
# 迭代,直到算法收敛(上一次的聚类中心和这一次的聚类中心几乎重合)或者达到最大迭代次数
for _ in range(self.max_iterations):
# 将所有进行归类,归类规则就是将该样本归类到与其最近的中心
clusters = self.create_clusters(centroids, X)
former_centroids = centroids
# 计算新的聚类中心
centroids = self.update_centroids(clusters, X)
# 如果聚类中心几乎没有变化,说明算法已经收敛,退出迭代
diff = centroids - former_centroids
if diff.any() < self.varepsilon:
break
return self.get_cluster_labels(clusters, X),self.get_cluster_centroids(clusters, X)
K-means实战
要求
数据中各个国家的疫情情况都不尽相同,有些甚至差异很大,因此我们想着先将78个国家进行聚类分析,由此来评判各个国家防疫的效果。
数据集
数据来源:https://github.com/datasets/covid-19
通过数据清洗和集成,部分数据如下所示
数据规约
累计确诊病例密度=累计确诊病例数/总人口;现有确诊病例密度=现有确诊病例数/总人口
故对于人口总数那一列属于冗余数据,故在进行k-means算法时,将这一列数据删除。
数据转换
数据规范化处理是数据挖掘的一项基本操作。现实中,数据中不同特征的量纲可能不一致,数值间的差别可能很大,不进行处理可能会影响到数据分析的结果,因此,需要对数据按照一定比例进行缩放,使之落在一个特定的区域,便于进行综合分析。
数据规范化可以有效地改进涉及距离度量的算法的精度和有效性。对于采用距离度量的分类算法和聚类算法,数据规范化的特别重要的步骤。
方法
- 最小最大规范化
- z-score规范化(零均值规范化)StandardScaler
- 小数定标规范化
python中sklearn-preprocessing介绍
sklearn-preprocessing里有StandardScaler函数可以用来做零均值规范化
scaler = StandardScaler()
scaler.fit(data)
data = scaler.transform(data)
实战k-means算法代码
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from My_KMeans import MyKmeans
df = pd.read_csv('Test\\77国家聚类.csv',header=0,index_col='国家')
data = df.iloc[:,:-1]
scaler = StandardScaler()
scaler.fit(data)
data = scaler.transform(data)
mykm = MyKmeans()
label,centroids = mykm.predict(data)
print(label)
result_centroid = scaler.inverse_transform(centroids)
# pd.DataFrame(result_centroid).to_csv('Test\\centroids.csv')
print(result_centroid)
# km = KMeans(5,random_state=2) #这里采用sklearn里的KMeans库(一样可以实现效果)
# res = km.fit(data)
# print(res.labels_+1)
# result = scaler.inverse_transform(res.cluster_centers_)
# print(result)