以正确的方式使用NumPy将函数应用于数组中的特定点

时间:2014-02-04 10:13:27

标签: python arrays numpy scipy vectorization

cArr是以下形式的数组:

cArr=np.array([[0,x0,y0,z0,1],[1,x1,y1,z1,1]])

每行的中间三个数字代表3D中两点的坐标(点0和1用于参考)。其他函数使用每行中的第一个和最后一个值。

cEDA是一个4D阵列形状(100,100,100,4),基本上是随机数。

对于每个点0和1,在每个点周围有一个点邻域,坐标在6个基本方向上仅相差一个。

x0,y0,z0
x0+1,y0,z0
x0,y0+1,z0
x0,y0,z0+1
x0-1,y0,z0
x0,y0-1,z0
x0,y0,z0-1

计算与点0和点1相关的这7个点之间的距离(因此点1与点0和点0周围点之间的距离),并与第-2个相应值(100,100,100)相结合)在cEDA中的数组并放置在cEDA中的第i个100,100,100立方体中,其中i分别为0或1。

我是来自C背景的python的新手,并且正在努力摆脱循环思维。我已经尝试了预先计算距离和求和数组但速度要慢得多,但到目前为止最快的方法是通过每个单独的值并分配它。该函数运行了很多次,是代码中的瓶颈,我相信有更好的方法可以做到。

我在概念上理解并且可以使用NumPy但在这种情况下似乎无法应用它 - 关于如何通过使用NumPy加速此功能的任何想法都是正确的方法?我已经尽力解释这个功能的功能对不起我知道这很混乱!

def cF2(cArr, cEDA):
    # the coordinates of points 0 and 1 for readability
    x0 = cArr[0,1]
    y0 = cArr[0,2]
    z0 = cArr[0,3]
    x1 = cArr[1,1]
    y1 = cArr[1,2]
    z1 = cArr[1,3]

    # for each point around point 0 and the point itself, calculate the distance between this point and point 1
    # use this value and the corresponding value in cEDA(x,y,z,-2) and place result in  cEDA(x,y,z,0)
    cEDA[x0,y0,z0,0] = cEDA[x0,y0,z0,-2]-0.4799/(np.linalg.norm([x0,y0,z0]-cArr[1,1:4]))
    cEDA[x0-1,y0,z0,0] = cEDA[x0-1,y0,z0,-2]-0.4799/(np.linalg.norm([x0-1,y0,z0]-cArr[1,1:4]))
    cEDA[x0+1,y0,z0,0] = cEDA[x0+1,y0,z0,-2]-0.4799/(np.linalg.norm([x0+1,y0,z0]-cArr[1,1:4]))
    cEDA[x0,y0-1,z0,0] = cEDA[x0,y0-1,z0,-2]-0.4799/(np.linalg.norm([x0,y0-1,z0]-cArr[1,1:4]))
    cEDA[x0,y0+1,z0,0] = cEDA[x0,y0+1,z0,-2]-0.4799/(np.linalg.norm([x0,y0+1,z0]-cArr[1,1:4]))
    cEDA[x0,y0,z0-1,0] = cEDA[x0,y0,z0-1,-2]-0.4799/(np.linalg.norm([x0,y0,z0-1]-cArr[1,1:4]))
    cEDA[x0,y0,z0+1,0] = cEDA[x0,y0,z0+1,-2]-0.4799/(np.linalg.norm([x0,y0,z0+1]-cArr[1,1:4]))

    cEDA[x1,y1,z1,1] = cEDA[x1,y1,z1,-2]+0.4799/(np.linalg.norm([x1,y1,z1]-cArr[0,1:4]))
    cEDA[x1-1,y1,z1,1] = cEDA[x1-1,y1,z1,-2]+0.4799/(np.linalg.norm([x1-1,y1,z1]-cArr[0,1:4]))
    cEDA[x1+1,y1,z1,1] = cEDA[x1+1,y1,z1,-2]+0.4799/(np.linalg.norm([x1+1,y1,z1]-cArr[0,1:4]))
    cEDA[x1,y1-1,z1,1] = cEDA[x1,y1-1,z1,-2]+0.4799/(np.linalg.norm([x1,y1-1,z1]-cArr[0,1:4]))
    cEDA[x1,y1+1,z1,1] = cEDA[x1,y1+1,z1,-2]+0.4799/(np.linalg.norm([x1,y1+1,z1]-cArr[0,1:4]))
    cEDA[x1,y1,z1-1,1] = cEDA[x1,y1,z1-1,-2]+0.4799/(np.linalg.norm([x1,y1,z1-1]-cArr[0,1:4]))
    cEDA[x1,y1,z1+1,1] = cEDA[x1,y1,z1+1,-2]+0.4799/(np.linalg.norm([x1,y1,z1+1]-cArr[0,1:4]))
    return cEDA

2 个答案:

答案 0 :(得分:0)

如果你想调用这个很多时间,你需要通过numba或Cython将整个for循环转换为C循环。但是有一些方法可以提高你的功能速度,这是你原来的功能:

def cF2(cArr, cEDA):
    # the coordinates of points 0 and 1 for readability
    x0 = cArr[0,1]
    y0 = cArr[0,2]
    z0 = cArr[0,3]
    x1 = cArr[1,1]
    y1 = cArr[1,2]
    z1 = cArr[1,3]

    # for each point around point 0 and the point itself, calculate the distance between this point and point 1
    # use this value and the corresponding value in cEDA(x,y,z,-2) and place result in  cEDA(x,y,z,0)
    cEDA[x0,y0,z0,0] = cEDA[x0,y0,z0,-2]-0.4799/(np.linalg.norm([x0,y0,z0]-cArr[1,1:4]))
    cEDA[x0-1,y0,z0,0] = cEDA[x0-1,y0,z0,-2]-0.4799/(np.linalg.norm([x0-1,y0,z0]-cArr[1,1:4]))
    cEDA[x0+1,y0,z0,0] = cEDA[x0+1,y0,z0,-2]-0.4799/(np.linalg.norm([x0+1,y0,z0]-cArr[1,1:4]))
    cEDA[x0,y0-1,z0,0] = cEDA[x0,y0-1,z0,-2]-0.4799/(np.linalg.norm([x0,y0-1,z0]-cArr[1,1:4]))
    cEDA[x0,y0+1,z0,0] = cEDA[x0,y0+1,z0,-2]-0.4799/(np.linalg.norm([x0,y0+1,z0]-cArr[1,1:4]))
    cEDA[x0,y0,z0-1,0] = cEDA[x0,y0,z0-1,-2]-0.4799/(np.linalg.norm([x0,y0,z0-1]-cArr[1,1:4]))
    cEDA[x0,y0,z0+1,0] = cEDA[x0,y0,z0+1,-2]-0.4799/(np.linalg.norm([x0,y0,z0+1]-cArr[1,1:4]))

    cEDA[x1,y1,z1,1] = cEDA[x1,y1,z1,-2]+0.4799/(np.linalg.norm([x1,y1,z1]-cArr[0,1:4]))
    cEDA[x1-1,y1,z1,1] = cEDA[x1-1,y1,z1,-2]+0.4799/(np.linalg.norm([x1-1,y1,z1]-cArr[0,1:4]))
    cEDA[x1+1,y1,z1,1] = cEDA[x1+1,y1,z1,-2]+0.4799/(np.linalg.norm([x1+1,y1,z1]-cArr[0,1:4]))
    cEDA[x1,y1-1,z1,1] = cEDA[x1,y1-1,z1,-2]+0.4799/(np.linalg.norm([x1,y1-1,z1]-cArr[0,1:4]))
    cEDA[x1,y1+1,z1,1] = cEDA[x1,y1+1,z1,-2]+0.4799/(np.linalg.norm([x1,y1+1,z1]-cArr[0,1:4]))
    cEDA[x1,y1,z1-1,1] = cEDA[x1,y1,z1-1,-2]+0.4799/(np.linalg.norm([x1,y1,z1-1]-cArr[0,1:4]))
    cEDA[x1,y1,z1+1,1] = cEDA[x1,y1,z1+1,-2]+0.4799/(np.linalg.norm([x1,y1,z1+1]-cArr[0,1:4]))
    return cEDA

np.random.seed(42)
cArr = np.random.randint(0, 100, (2, 5))
cEDA = np.random.rand(100, 100, 100, 4)
r1 = cF2(cArr, cEDA)

以下是优化功能:

tmp = np.eye(3)
offsets = np.concatenate((tmp, -tmp, [[0, 0, 0]]), 0).astype(int)[:, None, :]

def fast_cF2(cArr, cEDA):
    cArr = cArr[:, 1:4]
    t = cArr[None,:,:] + offsets
    X0, Y0, Z0 = t[:, 0, :].T
    X1, Y1, Z1 = t[:, 1, :].T

    d1 = 0.4799/np.linalg.norm(t[:, 0, :] - cArr[1], axis=1)
    d0 = 0.4799/np.linalg.norm(t[:, 1, :] - cArr[0], axis=1)

    cEDA[X0, Y0, Z0, 0] = cEDA[X0, Y0, Z0, -2] - d1
    cEDA[X1, Y1, Z1, 1] = cEDA[X1, Y1, Z1, -2] + d0
    return cEDA

np.random.seed(42)
cArr = np.random.randint(0, 100, (2, 5))
cEDA = np.random.rand(100, 100, 100, 4)
r2 = fast_cF2(cArr, cEDA)
print np.allclose(r1, r2)

时间结果:

%timeit cF2(cArr, cEDA)
%timeit fast_cF2(cArr, cEDA)

输出:

1000 loops, best of 3: 320 µs per loop
10000 loops, best of 3: 91.6 µs per loop

答案 1 :(得分:0)

我认为scipy.ndimage.generic_filter会这样做......

def neighborly_function(in_arr1d):
    # put your equation below - will have to figure out the details
    # below is just an example
    # has to return a scalar
    return sum(in_arr1d)

import scipy.ndimage as nd

huge_arr3d = [....]

# Since you want the cardinal directions you have to 'footprint'
footprint = np.array([[[False, False, False],
    [False,  True, False],
    [False, False, False]],

   [[False,  True, False],
    [ True, False,  True],
    [False,  True, False]],

   [[False, False, False],
    [False,  True, False],
    [False, False, False]]])

out = nd.generic_filter(huge_arr3d, neighborly_function, footprint=footprint)

查看http://docs.scipy.org/doc/scipy/reference/tutorial/ndimage.html上的ndimage文档和http://docs.scipy.org/doc/scipy/reference/tutorial/ndimage.html#generic-filter-functions上的generic_filter