在数据上应用任何聚类算法前,一个重要问题是,即使数据不包含任何集群,聚类方法也会返回群集。换句话说,如果盲目地在数据集上应用聚类算法,它也会将数据划分为聚类,因为这是它应该执行的。
因此,评估数据集是否包含有意义的聚类(即:非随机结构)有时会变得有必要。
此过程被定义为 聚类趋势的评估 或 聚类可行性的分析。
而通常,与非随机结构相对的是均匀分布,霍普金斯统计量的计算原理,便是检查数据是否符合均匀分布。
霍普金斯统计量介绍
霍普金斯统计 用于通过测量给定数据集由统一数据分布生成的概率来评估数据集的聚类趋势。换句话说,它测试数据的空间随机性。
如何解释霍普金斯统计量 H ?
如果数据点在空间中均匀分布,H大约是0.5;
如果聚类情况存在于数据集中,H会接近1;
当 H 高于0.75表示在90%的置信水平下,数据集中存在聚类趋势。
我们可以反复进行霍普金斯统计测试,使用0.5作为拒绝数据集中存在聚类趋势的阈值。
即,如果 H < 0.5,则数据集中不太可能有具有统计学意义的群集。
换句话说,如果霍普金斯统计值接近 1,那么我们可以得出结论,该数据集显然是可聚类数据。
这里提供霍普金斯统计量的Python实现:
import pandas as pd
from numpy.random import uniform,normal
from scipy.spatial.distance import cdist
#n维霍普金斯统计量计算,input:DataFrame类型的二维数据,output:float类型的霍普金斯统计量
#默认从数据集中抽样的比例为0.3
def hopkins_statistic(data:pd.DataFrame,sampling_ratio:float = 0.3) -> float:
#抽样比例超过0.1到0.5区间任意一端则用端点值代替
sampling_ratio = min(max(sampling_ratio,0.1),0.5)
#抽样数量
n_samples = int(data.shape[0] * sampling_ratio)
#原始数据中抽取的样本数据
sample_data = data.sample(n_samples)
#原始数据抽样后剩余的数据
data = data.drop(index = sample_data.index) #,inplace = True)
#原始数据中抽取的样本与最近邻的距离之和
data_dist = cdist(data,sample_data).min(axis = 0).sum()
#人工生成的样本点,从平均分布中抽样(artificial generate samples)
ags_data = pd.DataFrame({col:uniform(data[col].min(),data[col].max(),n_samples)\
for col in data})
#人工样本与最近邻的距离之和
ags_dist = cdist(data,ags_data).min(axis = 0).sum()
#计算霍普金斯统计量H
H_value = ags_dist / (data_dist + ags_dist)
return H_value
以下是通过例子说明霍普金斯统计量对聚类趋势判断如何生效:
import pandas as pd
from numpy.random import uniform,normal
from scipy.spatial.distance import cdist
#霍普金斯统计量计算,input:DataFrame类型的二维数据,output:float类型的霍普金斯统计量
#默认从数据集中抽样的比例为0.3
def hopkins_statistic(data:pd.DataFrame,sampling_ratio:float = 0.3) -> float:
#抽样比例超过0.1到0.5区间任意一端则用端点值代替
sampling_ratio = min(max(sampling_ratio,0.1),0.5)
#抽样数量
n_samples = int(data.shape[0] * sampling_ratio)
#原始数据中抽取的样本数据
sample_data = data.sample(n_samples)
#原始数据抽样后剩余的数据
data = data.drop(index = sample_data.index) #,inplace = True)
#原始数据中抽取的样本与最近邻的距离之和
data_dist = cdist(data,sample_data).min(axis = 0).sum()
#人工生成的样本点,从平均分布中抽样(artificial generate samples)
ags_data = pd.DataFrame({col:uniform(data[col].min(),data[col].max(),n_samples)\
for col in data})
#人工样本与最近邻的距离之和
ags_dist = cdist(data,ags_data).min(axis = 0).sum()
#计算霍普金斯统计量H
H_value = ags_dist / (data_dist + ags_dist)
return H_value
#生成符合均匀分布的数据集
data = pd.DataFrame(uniform(0,10,(50000,10)))
print(hopkins_statistic(data))
#生成符合正态分布的数据集
data = pd.DataFrame(normal(size = (50000,10)))
print(hopkins_statistic(data))
#生成由两个不同的正态分布组成的数据集
data = pd.DataFrame(normal(loc = 6,size = (25000,10))).append(pd.DataFrame(normal(size = (25000,10))),ignore_index = True)
print(hopkins_statistic(data))
代码运行结果:
#符合均匀分布的数据集
>>> 0.5000266809427586
#符合正态分布的数据集
>>> 0.7659490625832939
#由两个不同的正态分布组成的数据集
>>> 0.8774643641621805
最后说明一下,为什么霍普金斯统计量被使用和提及得不多呢?
一方面,Python作为数据科学的流行语言,没有现成的实现,不便于使用。
另一方面,基于该霍普金斯统计量的定义,计算代价比较大,计算速度不快,相比起直接聚类然后再判断是否有好的聚类效果,这种方式在时间上可能没有明显的节省。
但即使少用,最起码要有可用的代码实现,并且重要的是,霍普金斯统计量对于聚类趋势的判断比起其他方法更为科学和准确。