在numpy.sum()或mean()之前有效的numpy.roll

时间:2014-09-24 14:21:02

标签: python numpy

我有几个(1000个订单)3D形状阵列(1000,800,1024)我想学习。我需要计算沿轴= 0的平均值,但在我能做到之前,我必须沿着轴2滚动数据,直到它“在正确的位置”。

这听起来很奇怪,所以我会试着解释一下。形状(1024,)的1D子阵列是来自物理环形缓冲器的数据。我知道,环形缓冲区以不同的方式读出。所以我有几个形状pos的阵列(1000,800)。告诉我环读缓冲区被读出的位置。以及我需要根据data滚动的形状(1000,800,1024)的3D阵列pos

只有在滚动之后...... 3D阵列对我来说很有意义,我可以开始分析它们。 在C中,人们可以编写这样做非常简单的代码,所以我想知道我是否可以“告诉”numpy mean()或sum()例程,它们应该从不同的索引开始,并在结束时'滚动' 1D-子阵列。

我目前所做的是:

rolled = np.zeros_like(data) # shape (1000, 800, 1024)
for a in range(rolled.shape[0]):
    for b in range(rolled.shape[1]):
        rolled[a,b] = np.roll(data[a,b], pos[a,b])

这需要约60秒 然后我会这样做:

m = rolled.mean(axis=0)
s = rolled.std(axis=0)

只需要15秒左右。

我的观点是,使滚动副本占用大量空间和时间(好吧我可以通过将滚动的内容写回data来节省空间),而有一种方法(在C中)实现这个平均并在一个循环中滚动,从而节省了大量时间。 我的问题是......如果有办法与numpy做类似的事情吗?

1 个答案:

答案 0 :(得分:11)

我很无聊并在Cython中编写了你的​​功能。它的运行速度比您发布的代码快10倍,而无需分配中间数组。

import numpy as np
cimport numpy as np
cimport cython
from libc.math cimport sqrt

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
def rolled_mean_std(double[:,:,::1] data, int[:,::1] pos):
  cdef Py_ssize_t s1,s2,s3,a,b,c
  s1 = data.shape[0]
  s2 = data.shape[1]
  s3 = data.shape[2]
  cdef double[:,::1] sums = np.zeros((s2,s3))
  cdef double[:,::1] sumsq = np.zeros((s2,s3))
  cdef double d
  cdef int p
  # Compute sums and sum-of-squares.
  for a in range(s1):
    for b in range(s2):
      p = pos[a,b]
      for c in range(s3):
        d = data[a,b,(c+s3-p)%s3]
        sums[b,c] += d
        sumsq[b,c] += d * d
  # Calculate mean + std in place.
  for b in range(s2):
    for c in range(s3):
      d = sums[b,c]
      sums[b,c] /= s1
      sumsq[b,c] = sqrt((s1*sumsq[b,c] - (d*d)))/s1
  return sums, sumsq

请注意,这会使用Naive mean+stdv algorithm,因此您可能会遇到浮点精度错误。但是,我的测试并没有显示出很大的影响。