我有一个大的,对称的2D距离阵列。我想获得最接近的N对观察。
数组存储为numpy压缩数组,并且具有1亿次观察的数量级。
这是一个在较小的阵列上获得100个最近距离的示例(约500k观测值),但它比我想要的慢得多。
import numpy as np
import random
import sklearn.metrics.pairwise
import scipy.spatial.distance
N = 100
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
c = r[:, None]
dists = scipy.spatial.distance.pdist(c, 'cityblock')
# these are the indices of the closest N observations
closest = dists.argsort()[:N]
# but it's really slow to get out the pairs of observations
def condensed_to_square_index(n, c):
# converts an index in a condensed array to the
# pair of observations it represents
# modified from here: http://stackoverflow.com/questions/5323818/condensed-matrix-function-to-find-pairs
ti = np.triu_indices(n, 1)
return ti[0][c]+ 1, ti[1][c]+ 1
r = []
n = np.ceil(np.sqrt(2* len(dists)))
for i in closest:
pair = condensed_to_square_index(n, i)
r.append(pair)
在我看来,必须有更快捷的方法来执行标准的numpy或scipy函数,但我很难过。
注意如果很多对是等距的,那就没关系,在这种情况下我不关心它们的排序。
答案 0 :(得分:3)
您在ti
的每次通话中都无需计算condensed_to_square_index
。这是一个只计算一次的基本修改:
import numpy as np
import random
import sklearn.metrics.pairwise
import scipy.spatial.distance
N = 100
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
c = r[:, None]
dists = scipy.spatial.distance.pdist(c, 'cityblock')
# these are the indices of the closest N observations
closest = dists.argsort()[:N]
# but it's really slow to get out the pairs of observations
def condensed_to_square_index(ti, c):
return ti[0][c]+ 1, ti[1][c]+ 1
r = []
n = np.ceil(np.sqrt(2* len(dists)))
ti = np.triu_indices(n, 1)
for i in closest:
pair = condensed_to_square_index(ti, i)
r.append(pair)
您还可以向量化r
的创建:
r = zip(ti[0][closest] + 1, ti[1][closest] + 1)
或
r = np.vstack(ti)[:, closest] + 1
答案 1 :(得分:2)
如果您使用np.partition
使用numpy 1.8,则可以非常显着地加快最小值的位置:
def smallest_n(a, n):
return np.sort(np.partition(a, n)[:n])
def argsmallest_n(a, n):
ret = np.argpartition(a, n)[:n]
b = np.take(a, ret)
return np.take(ret, np.argsort(b))
dists = np.random.rand(1000*999//2) # a pdist array
In [3]: np.all(argsmallest_n(dists, 100) == np.argsort(dists)[:100])
Out[3]: True
In [4]: %timeit np.argsort(dists)[:100]
10 loops, best of 3: 73.5 ms per loop
In [5]: %timeit argsmallest_n(dists, 100)
100 loops, best of 3: 5.44 ms per loop
一旦你拥有最小的索引,你就不需要循环来提取索引,只需一次完成:
closest = argsmallest_n(dists, 100)
tu = np.triu_indices(1000, 1)
pairs = np.column_stack((np.take(tu[0], closest),
np.take(tu[1], closest))) + 1
答案 2 :(得分:0)
最佳解决方案可能不会产生所有距离。
提案: