大NumPy数组的配对距离(Chunking?

问题描述 投票:4回答:1

问题:我有一个约为[350000,1]的向量,我希望计算成对的距离。我有一个大约是[350000, 1]的向量,我希望计算出一对的距离。这将导致一个[350000,350000]整数数据类型的矩阵,不适合放在RAM中。我最终想用一个布尔值(适合于RAM)来结束,所以我目前一次只做一个元素,但这不是很省时。

编辑:由于数据的大小,标准的sklearn和scipy函数无法工作--但如果我可以以某种方式将其分块使用硬盘,那么我应该可以使用这些。

问题可视化。[a_1, a_2, a_3]^t -> [[a_1 - a_1, a_1 - a_2, a_1 - a_3], [a_2 - a_1, a_2 - a_2, a_2 - a_3], [a_3 - a_1, a_3 - a_2, a_3 - a_3]] 。

需要注意的是,由于取abs值时是对称的,所以只需要计算上三角。

需要分块的矢量化代码或其他解决方案。我已经找到了一种方法来计算所有点之间的距离(减法),这种方法可以在小矩阵上使用广播,但需要一种方法来在较大的矩阵上进行计算,而不会受到RAM的限制。

或者可以提出一个更好的方法,对下面的MWE更快的方法?

distMatrix = np.absolute((points[np.newaxis, :, :] - points[:, np.newaxis, :])[:, :, 0])

其他尝试。我试过使用dask和memmap,但还是出现内存错误,所以一定是做错了什么。我还尝试了memmap和手动分块数据,但没有得到一套完整的结果,所以任何帮助都将是非常感激的。

当前方法的MWE。


## Data ##
#Note that the datatype and code may not match up exactly as just creating to demonstrate. Essentially want to take first column and create distance matrix with itself through subtracting, and then take 2nd and 3rd column and create euclidean distance matrix.

data = np.random.randint(1, 5, size=[350001,3])
minTime = 3
maxTime = 4
minDist = 1
maxDist = 2

### CODE ###
n = len(data)

for i in trange(n):
    for j in range(i+1, n):
        #Within time threshold?
        if minTime <= (data[j][idxT] - data[i][idxT]) <= maxTime:
            #Within distance threshold?
            xD = math.pow(data[j][idxX] - data[i][idxX], 2)
            yD = math.pow(data[j][idxY] - data[i][idxY], 2)
            d = math.sqrt(xD + yD)
            #If within  threshold then
            if minDist <= d <= maxDist:
                #DO SOMETHING

原因:我有大约350000个点的时间,x_coordinate,y_coordinate向量。我想计算所有时间点之间的距离(简单的减法)和每个(x,y)点之间的欧氏距离。然后我希望能够识别所有在时间和距离出现阈值内的点对,并产生一个布尔值。

python numpy distance chunking
1个回答
3
投票

你可以把你的数组分割成更小的尺寸,然后分别计算每对的距离。

splits = np.array_split(data, 10)
for i in range(len(splits)):
    for j in range(i, len(splits)):
        m = scipy.spatial.distance.cdist(splits[i], splits[j])
        # do something with m

因为大部分的计算都发生在scipy中,python循环的开销将是最小的。

如果你的布尔数组适合放在内存中,并且你试图找到在一定范围内的值,你可以做的是

import numpy as np
import scipy.spatial.distance


boolean = np.zeros((350, 350), dtype=np.bool_)
a = np.random.randn(350, 2)
splits = np.array_split(a, 10)
shift = splits[0].shape[0]
minDist = -0.5
maxDist = +0.5
for i in range(len(splits)):
    for j in range(i, len(splits)):
        m = scipy.spatial.distance.cdist(splits[i], splits[j])
        masked = (minDist <= m) & (m <= maxDist)
        boolean[i * shift: (i + 1) * shift, j * shift : (j + 1) * shift] = masked
        boolean[j * shift : (j + 1) * shift, i * shift: (i + 1) * shift] = masked.T
© www.soinside.com 2019 - 2024. All rights reserved.