从numpy距离数组中提取N个最近的对

zpqajqem  于 2023-08-05  发布在  其他
关注(0)|答案(4)|浏览(77)

我有一个大的,对称的,2D距离数组。我想得到最接近的N对观测值。
该数组被存储为numpy压缩数组,并且具有1亿个观测值的数量级。
这里有一个例子,在一个较小的数组上获得100个最近的距离(~ 500 k个观察值),但它比我想要的要慢得多。

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函数一定有更快的方法来做到这一点,但我被难倒了。
如果很多对是等距的,那是可以的,在这种情况下,我不关心它们的顺序。

tyg4sfes

tyg4sfes1#

您不需要在每次调用condensed_to_square_index时计算ti。这里有一个基本的修改,只计算一次:

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

fcipmucu

fcipmucu2#

如果使用numpy 1.8和np.partition,可以显著加快最小值的定位速度:

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

k75qkfdt

k75qkfdt3#

最佳解决方案可能无法生成所有距离。
提案:
1.创建一个最大大小为100的堆(如果堆变大,请将其减小)。
1.使用“最近对”算法查找最近对。
1.将对添加到堆(优先级队列)。
1.从那对中选一个。将其99个最近的邻居添加到堆中。
1.从列表中删除所选点。
1.找到下一个最接近的配对,然后重复。添加的相邻点的数量为100减去运行“最接近对”算法的次数。

qrjkbowd

qrjkbowd4#

您可以使用pandas DataFrame。首先,您将相似性矩阵(例如使用sklearn中的pairwise_distances())声明为DataFrame,添加源数据中的列名和索引名。然后按名称选择任何列(这是您感兴趣的列),然后使用pandas.DataFrame.sort_values(),然后选择top5或top10。就是这样

相关问题