使用Numpy提高执行速度

时间:2018-04-11 02:07:00

标签: python numpy vectorization

我有以下for循环,并希望使用numpy vectorizing,boolean,mask数组等来提高执行速度。 arr是一个numpy数组

def translate(arr, x=0):
    arr1 = arr.copy()
    for i in range(arr1.shape[0]):
        for j in range(arr1.shape[1]):
            if i + x < arr1.shape[0] and i + x > 0:
                arr1[i,j] = arr[i+x,j]
            if i + x < 0 or i + x > arr1.shape[0]:
                arr1[i,j] = 255
    return arr1

非常感谢任何帮助或建议。

编辑:而不是255,当(i + x)<0时,它应该是arr [i,j]

def translate(arr, x=0):
    arr1 = arr.copy()
    for i in range(arr1.shape[0]):
        for j in range(arr1.shape[1]):
            if i + x < arr1.shape[0] and i + x > 0:
                arr1[i,j] = arr[i+x,j]
            if i + x < 0 or i + x > arr1.shape[0]:
                arr1[i,j] = arr[i,j]
    return arr1

4 个答案:

答案 0 :(得分:1)

这是做我认为你想做的事情(向上或向下移动原始数组并用255填充空白区域)

>>> def translate_vectorized(arr, x=0):
...     if x == 0:
...         return arr.copy()
...     out = np.full(arr.shape, 255)
...     if x < 0:
...         out[-x:] = arr[:x]
...     else:
...         out[:-x] = arr[x:]
...     return out

演示

>>> arr = 3 - np.maximum.outer(*2*(np.abs(np.arange(-3, 4)),))
>>> arr
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [0, 1, 2, 2, 2, 1, 0],
       [0, 1, 2, 3, 2, 1, 0],
       [0, 1, 2, 2, 2, 1, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [0, 0, 0, 0, 0, 0, 0]])
>>> 
>>> translate_vectorized(arr, -2)
array([[255, 255, 255, 255, 255, 255, 255],
       [255, 255, 255, 255, 255, 255, 255],
       [  0,   0,   0,   0,   0,   0,   0],
       [  0,   1,   1,   1,   1,   1,   0],
       [  0,   1,   2,   2,   2,   1,   0],
       [  0,   1,   2,   3,   2,   1,   0],
       [  0,   1,   2,   2,   2,   1,   0]])
>>> translate_vectorized(arr, 1)
array([[  0,   1,   1,   1,   1,   1,   0],
       [  0,   1,   2,   2,   2,   1,   0],
       [  0,   1,   2,   3,   2,   1,   0],
       [  0,   1,   2,   2,   2,   1,   0],
       [  0,   1,   1,   1,   1,   1,   0],
       [  0,   0,   0,   0,   0,   0,   0],
       [255, 255, 255, 255, 255, 255, 255]])

答案 1 :(得分:1)

由于@Paul Panzer解决方案没有给出与你的函数相同的输出,我通过尝试布尔数组修改了他的工作。希望它是你想要的。

<强>代码:

def my_translate(arr, x=0):
    arr = arr.copy()
    if x == 0:
        return arr.copy()
    elif x > 0:
        replacement1 = np.zeros(arr.shape)
        replacement1[:-x] = arr[x:]
    else:
        replacement1 = np.zeros(arr.shape)
        replacement1[-x:] = arr[:x]
    replacement2 = np.zeros(arr.shape)+255 # Array filled with 255 for second logic
    l = [np.repeat(i,arr.shape[1]) for i in range(arr.shape[0])]
    firstlooplogic1 = np.asarray(l)+x < arr.shape[0]
    firstlooplogic2 = np.asarray(l)+x > 0
    secondlooplogic1 = np.asarray(l)+x > arr.shape[0]
    secondlooplogic2 = np.asarray(l)+x < 0

    part1logic = np.logical_and(firstlooplogic1,firstlooplogic2)
    part2logic = np.logical_or(secondlooplogic1,secondlooplogic2)

    part1 = part1logic*replacement1
    part2 = part2logic*replacement2
    part3 = ((part1 == 0) * (part2 == 0)) * arr       
    return (part1 + part2 +part3).astype(arr.dtype)

<强>结果:

arr = 3 - np.maximum.outer(*2*(np.abs(np.arange(-3, 4)),))
output1 = my_translate(arr,-2) #output from my function
output2 = translate(arr,-2)    #output from your function above
np.array_equal(output1,output2)
>Out[10]: True

基本上,我只是将嵌套的for循环分解为布尔数组并执行操作。

答案 2 :(得分:1)

我已经优化了您的解决方案(更好的缓存利用率导致约30%的加速)。但如果你保持原样,它也会好的。在过度循环的numpy代码上你唯一需要做的就是编译它。这可以使用Numba或使用Cython进行更多的工作来完成。

编译版本应该比您的解决方案快约3000倍。由于运行在内存瓶颈中,并行化在这里并不是必需的(加速率只有大约20%)。

示例

import numba as nb
import numpy as np


@nb.njit(fastmath=True,parallel=True)
def translate(arr, x):
    arr1 = np.empty(arr.shape,dtype=arr.dtype)
    for i in nb.prange(arr1.shape[0]):
        for j in range(arr1.shape[1]):
            arr1[i,j]=arr[i,j]
            if i + x < arr1.shape[0] and i + x > 0:
                arr1[i,j] = arr[i+x,j]
            if i + x < 0 or i + x > arr1.shape[0]:
                arr1[i,j] = arr[i,j]
    return arr1

答案 3 :(得分:0)

感谢您的回答。尝试使用numpy.roll(arr,shift_amount)。它也有效,但在第一个位置重新引入超出最后位置的元素。因此,当(i + x)小于shape [0]时,可以使用它。

if (i+x) < arr1.shape[0]:
    arr2 = np.roll(arr1,x)