Python - K均值聚类,某些列方差为零
我有一个数据集,大约有200个99x20的频率数组,每一列的总和都是1。我用热图把这些数据可视化,像这样 。每个数组都很稀疏,在99个位置中,只有大约1到7个值是非零的。
不过,我想根据这些频率的相似性来对样本进行聚类(比如说最小欧几里得距离之类的)。我把每个99x20的数组整理成了一个1980x1的数组,然后把它们合并成一个200x1980的观测数组。
在寻找聚类之前,我尝试用 scipy.cluster.vq.whiten
来对数据进行白化处理。whiten
是通过每列的方差来标准化数据,但因为我把数据数组压平了,有8列的频率都是零,所以它们的方差也是零。因此,白化后的数组会出现无穷大的值,导致找不到中心点(或者得到大约200个中心点)。
我想问的是,我该如何解决这个问题?到目前为止,我尝试过:
- 不对数据进行白化。这会导致k-means每次运行时给出不同的中心点(这有点意料之中),尽管我把
iter
参数调得很高。 - 在压平数组之前先转置它们。零方差的列只是位置发生了变化。
直接删除这些零方差的列可以吗?这样会不会对聚类造成偏差?
补充:我还尝试了自己写的白化函数,内容就是
for i in range(arr.shape[1]):
if np.abs(arr[:,i].std()) < 1e-8: continue
arr[:,i] /= arr[:,i].std()
这个方法似乎有效,但我不确定这是否会对聚类造成偏差。
谢谢
2 个回答
去掉全是0的那一列数据不会影响数据的整体情况。如果你有N维的数据,但其中一维的数值都是一样的,那其实就和只有N-1维的数据是一样的。这种有效维度的特性叫做秩。
想象一下3维的数据,但所有的数据点都在x=0的平面上。你能明白这其实和2维的数据是一样的吗?
首先,去掉那些常量列是完全可以的。显然,这些列没有提供任何信息,所以没必要保留它们。
不过,K-means算法对稀疏向量的效果并不好。问题在于,最终得到的“中心点”可能会彼此之间更相似,而不是和各自的群组成员相似。因为在稀疏数据中,每个对象在某种程度上都是异常值。而K-means对异常值非常敏感,因为它试图最小化平方和。
我建议你可以这样做:
找到一个适合你领域的相似性度量。花一些时间去研究,看看怎样才能捕捉到你特定用例的相似性。
一旦你有了相似性度量,就计算一个200x200的相似性矩阵。由于你的数据集非常小,你实际上可以使用一些计算量大的聚类方法,比如层次聚类,这些方法在处理成千上万的对象时就不太适用了。如果你愿意,也可以尝试OPTICS聚类或DBSCAN。不过,特别是DBSCAN在数据集更大的时候会更有趣。对于小数据集,层次聚类就可以了。