构建大距离矩阵

数据挖掘 Python 聚类
2022-01-30 13:23:31

我正在尝试为大约 600,000 个具有纬度和经度的位置构建距离矩阵。我想使用这个距离矩阵进行凝聚聚类。由于这是一大组位置,计算距离矩阵是一项极其繁重的操作。有什么方法可以优化这个过程,同时记住我稍后将使用这个矩阵进行聚类。下面是我正在使用的代码。

from scipy.spatial.distance import pdist
import time
start = time.time()
# dist is a custom distance function that I wrote
y = pdist(locations[['Latitude', 'Longitude']].values, metric=dist)
end = time.time()
print(end - start)
2个回答

您是否考虑过以下步骤会更糟?

层次聚类的标准算法规模为 O(n³)。您只是不想在大数据上使用它。它不缩放。

你为什么不自己做一个简单的实验:测量计算 n=1000,2000,4000,8000,16000,32000 的距离(并进行聚类)的时间,然后估计你需要多长时间来处理整个数据集假设你有足够的内存……你会发现在这么大的数据上使用这个算法是不可行的。

你应该重新考虑你的方法......

对于这样的任务,我想到的最简单的解决方案是使用与稍后的层次聚类步骤计划的完全相同的指标(在您的情况下,使用 p=2 的 eucledian / minkowski)进行简单的 kmeans 聚类(或批处理变体)。对于最初的 kmeans 步骤,您选择了集群的数量 k,以便对这些集群中心的距离计算是可行的。有了这个,你基本上初始化了层次结构下一层的层次聚类。

import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import pdist, squareform
from sklearn.neighbors import NearestNeighbors
x = np.random.rand(600000*2).reshape((600000, -1))
kmeans = KMeans(k=int(len(x)/100), n_jobs=16)
kmeans.fit(x)
x_ = kmeans.cluster_centers_
D = squareform(pdist(x_))

这里 x 是您的数据(这里通过正方形中的随机坐标进行模拟),第一步的缩减因子是 100。在计算时间方面,这个操作仍然相当昂贵。一个更快的解决方案如下:

请注意,如果您的数据非常统一,请考虑随机预选数据子集(可能具有类似于泊松圆盘采样中发生的距离标准)而不是 kmeans。那将是超级快:

import numpy as np
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances
# input data
x = np.random.rand(600000*2).reshape((600000, -1))

# reduction of the size of the set of samples using uniformity
# chose a couple of times randomly from the input data and compute clusters in that subset
# this allows to avoid expensive clustering on the huge input data set
repetitions = 20
reduction = 1000
x_ = [x[np.random.choice(len(x), size=int(len(x)/reduction), replace=False)] for i in range(repetitions)]
x_ = np.reshape(x_, (repetitions*int(len(x)/reduction),2))
kmeans = KMeans(int(len(x)/reduction), n_jobs = 16)
kmeans.fit(x_)
# Compute the distance matrix to start with for the hierarchical clustering
D = pairwise_distances(kmeans.cluster_centers_, n_jobs=16) 
# Compute the assignments from each data point to any of the level-0 clusters
D_data2clusters = pairwise_distances(x, kmeans.cluster_centers_, n_jobs=16)
cluster_labels = np.argmin(D_data2clusters,1)

from matplotlib import pyplot as plt
for cluster in np.unique(cluster_labels):
    plt.scatter(*x[cluster_labels == cluster].T)
plt.scatter(*kmeans.cluster_centers_.T)
plt.savefig("clustertest")

绿点是聚类中心和进一步层次聚类的起点。

这里kmeans.cluster_centers_(绿点)或距离矩阵D可以作为层次聚类的输入。

from scipy.cluster.hierarchy import linkage, dendrogram
z = linkage(D)
plt.figure()
dendrogram(z)
plt.savefig("dendrogram")

层次聚类树状图

在这种情况下,另一种可能的解决方法是使用基于邻居树的方法计算不完整的距离矩阵。这基本上是您对距离矩阵的近似。

为此,您首先将 sklearn.neighbors.NearestNeighbors 树拟合到您的数据,然后使用“距离”模式(这是一个稀疏距离矩阵)计算图形。您需要将非对角零值推到很远的距离(或无穷大)。在该稀疏矩阵中,基本上只存储有关每个数据的较近邻域的信息,甚至不计算更大的距离并将其放入该矩阵中。但是,对于您的场景内存,必须分配大小为 600000^2 的浮点矩阵 - 即 2.62 TiB,这是不现实的。

假设您有这样的距离矩阵,您可以尝试解决任何层次聚类方法是否适当地处理那种不完整的距离矩阵,但正如前面的答案所指出的那样,它会非常昂贵。因此,我建议在这种情况下使用非常有效的 kmeans(如上所示在数据本身上 - 在这种情况下批处理版本也可能有用)或kmedoid(在稀疏距离矩阵上),您可以构建和应用分层方式也是。

通常,如果您可以重新制定算法以使其不需要立即访问完整距离矩阵,则可以使用 sklearn 的pairwise_distances_chunked