如何矢量化3D Numpy数组

时间:2014-12-23 16:54:06

标签: python arrays performance numpy vectorization

我有像a = np.zeros((100,100, 20))这样的3D numpy数组。我想对涉及x,y轴上所有元素的每个z位置执行操作,结果存储在b = np.zeros((100,100))之类的数组中,位于相同的x,y上位置。

现在我正在使用for循环:

d_n = np.array([...]) # a parameter with the same shape as b
for (x,y), v in np.ndenumerate(b):
    C = a[x,y,:]

    ### calculate some_value using C
    minv = sys.maxint
    depth = -1
    C = a[x,y,:]
    for d in range(len(C)):
        e = 2.5 * float(math.pow(d_n[x,y] - d, 2)) + C[d] * 0.05
        if e < minv:
            minv = e
            depth = d

    some_value = depth
    if depth == -1:
        some_value = len(C) - 1
    ###

    b[x,y] = some_value

现在的问题是这个操作比其他人用pythonic方式慢得多 c = b * b(我实际上是对这个函数进行了分析,它比使用numpy内置函数和向量化函数的其他元素慢了大约2个数量级,超过相似数量的元素)

如何改善将3D阵列映射到2D阵列的此类功能的性能?

4 个答案:

答案 0 :(得分:2)

在3D图像中通常做的是将Z轴交换到第一个索引:

>>> a = a.transpose((2,0,1))
>>> a.shape
(20, 100, 100)

现在您可以轻松地在Z轴上进行迭代:

>>> for slice in a:
       do something

此处的slice将是您的3D矩阵的100x100个分数。此外,通过转置,您可以通过索引第一个轴直接访问每个2D切片。例如,a[10]将为您提供第11个2D 100x100切片。

奖励:如果您连续存储数据,而不进行转置(或使用a = np.ascontiguousarray(a.transpose((2,0,1)))转换为连续数组,则对2D切片的访问速度会更快,因为它们会在内存中连续映射。

答案 1 :(得分:0)

显然你想要摆脱明确的for循环,但我认为这是否可行取决于你用C做的计算。作为一个简单的例子,

a = np.zeros((100,100, 20))
a[:,:] = np.linspace(1,20,20)    # example data: 1,2,3,.., 20 as "z" for every "x","y"

b = np.sum(a[:,:]**2, axis=2)

100 100数组b填充a的平方“z”值之和,即1 + 4 + 9 + .. 。+ 400 = 2870。

答案 2 :(得分:0)

如果你的内部计算足够复杂,并且不适合矢量化,那么你的迭代结构是好的,并且对计算时间没有显着贡献

for (x,y), v in np.ndenumerate(b):
    C = a[x,y,:]
    ...
    for d in range(len(C)):
        ... # complex, not vectorizable calc
    ...
    b[x,y] = some_value

在前2个维度中似乎没有特殊的结构,所以您也可以将其视为2D映射到1D,例如将(N,20)数组映射到(N,)数组。这不会加速任何事情,但可能有助于突出问题的基本结构。

一步是专注于加快Csome_value的计算速度。有cumsumcumprod等函数可以帮助您对向量进行顺序计算。 cython也是一个很好的工具。

另一种方法是查看是否可以同时对N值执行内部计算。换句话说,如果你必须迭代,最好在最小的维度上这样做。

从某种意义上说,这是一个非答案。但是,如果不完全了解some_valueC的{​​{1}},我认为我们无法做得更多。


似乎可以同时为所有点计算d_n

e

乍看之下,e = 2.5 * float(math.pow(d_n[x,y] - d, 2)) + C[d] * 0.05 E = 2.5 * (d_n[...,None] - np.arange(a.shape[-1]))**2 + a * 0.05 # (100,100,20) E.min(axis=-1) # smallest value along the last dimension E.argmin(axis=-1) # index of where that min occurs 似乎是您想要的E.argmin值(如果需要,可以针对某些边界条件进行调整)。

我没有真实的ba数组,但是对于简单的测试数组,此d_n与您的E.argmin(-1)相匹配,加速度为66倍。

答案 3 :(得分:0)

  

如何改善将3D阵列映射到2D阵列的此类功能的性能?

Numpy中的许多功能都是&#34;还原&#34;函数 * ,例如sumanystd等。如果您提供除axis以外的None参数这样一个函数,它将减少该轴上的数组的尺寸。对于您的代码,如果您首先以矢量化方式计算argmin,则可以使用e函数:

d = np.arange(a.shape[2])
e = 2.5 * (d_n[...,None] - d)**2 + a*0.05
b = np.argmin(e, axis=2)

[...,None]的索引用于参与broadcastinge中的值是浮点值,因此与sys.maxint进行比较有点奇怪,但是你去了:

I, J = np.indices(b.shape)
b[e[I,J,b] >= sys.maxint] = a.shape[2] - 1

*严格地说,缩减函数的格式为reduce(operator, sequence),因此技术上不是stdargmin