遍历numpy数组列的所有成对组合

2024-09-28 22:20:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个核大小的数组

arr.size = (200, 600, 20). 

我想计算最后两个维度的每个成对组合上的scipy.stats.kendalltau。例如:

kendalltau(arr[:, 0, 0], arr[:, 1, 0])
kendalltau(arr[:, 0, 0], arr[:, 1, 1])
kendalltau(arr[:, 0, 0], arr[:, 1, 2])
...
kendalltau(arr[:, 0, 0], arr[:, 2, 0])
kendalltau(arr[:, 0, 0], arr[:, 2, 1])
kendalltau(arr[:, 0, 0], arr[:, 2, 2])
...
...
kendalltau(arr[:, 598, 20], arr[:, 599, 20])

这样我就涵盖了arr[:, i, xi]arr[:, j, xj]i < jxi in [0,20)xj in [0, 20)的所有组合。这是(600 choose 2) * 400单独的计算,但是由于在我的机器上每个计算大约需要0.002 s,所以使用多处理模块的时间不应该超过一天。

遍历这些列(使用i<j)的最佳方法是什么?我想我应该避免

for i in range(600):
    for j in range(i+1, 600):
        for xi in range(20):
            for xj in range(20):

做这件事最重要的方法是什么?

编辑:我更改了标题,因为Kendall Tau对问题并不重要。我知道我也可以做些

import itertools as it
for i, j in it.combinations(xrange(600), 2):
    for xi, xj in product(xrange(20), xrange(20)):

但有一个更好的,更矢量化的方式与numpy。


Tags: 方法inforsizestatsrangeitscipy
2条回答

如果您不想使用递归,那么通常应该使用itertools.combinations.没有特定的原因(afaik)会导致代码运行速度变慢。计算密集的部分仍由numpy处理。Itertools还有可读性的优势。

像这样的矢量化的一般方法是使用广播来创建集合本身的笛卡尔积。在您的例子中,您有一个形状为arr的数组,因此您可以对其进行两个视图:

arr_x = arr[:, :, np.newaxis, np.newaxis, :] # shape (200, 600, 1, 1, 20)
arr_y = arr[np.newaxis, np.newaxis, :, :, :] # shape (1, 1, 200, 600, 20)

为了清楚起见,以上两行已经展开,但我通常会写出等价的:

arr_x = arr[:, :, None, None]
arr_y = arr

如果您有一个向量化函数f,它在除了最后一个维度之外的所有维度上广播,那么您可以:

out = f(arr[:, :, None, None], arr)

然后out将是一个形状为(200, 600, 200, 600)的数组,其中out[i, j, k, l]保持f(arr[i, j], arr[k, l])的值。例如,如果要计算所有成对内部产品,可以执行以下操作:

from numpy.core.umath_tests import inner1d

out = inner1d(arr[:, :, None, None], arr)

不幸的是scipy.stats.kendalltau不是这样矢量化的。根据the docs

"If arrays are not 1-D, they will be flattened to 1-D."

所以你不能这样做,你最终会做Python嵌套循环,不管是显式地写出来,使用itertools还是隐藏在^{}下。这将是缓慢的,因为Python变量上的迭代,并且因为每个迭代步骤都有一个Python函数,这两个操作都很昂贵。

请注意,当您可以采用矢量化的方式时,有一个明显的缺点:如果您的函数是可交换的,即如果f(a, b) == f(b, a),那么您所做的计算是所需的两倍。根据实际计算的开销,这通常会被没有任何Python循环或函数调用所带来的速度提高所抵消。

相关问题 更多 >