numpy中带有数组索引的嵌套循环

时间:2012-08-07 21:39:59

标签: python numpy

我想知道如何做到以下几点:

def GetFlux(self, time):
    bx = self.GetField("bx", time) * self.wpewce
    by = self.GetField("by", time) * self.wpewce
    bz = self.GetField("bz", time) * self.wpewce              

    flux  = np.zeros((self.ncells[0]+1,self.ncells[1]+1),"float32", order='FORTRAN')
    flux2  = np.zeros((self.ncells[0]+1,self.ncells[1]+1),"float32", order='FORTRAN')

    dx = self.dl[0]
    dz = self.dl[1]

    nx = self.ncells[0]
    nz = self.ncells[1]

    j = 0

    for i in np.arange(1, nx):
        flux2[i,0] = flux2[i-1,0] + bz[i-1,0]*dx
    flux[1:,0] = flux[0,0] + np.cumsum(bz[:-1,0]*dx)

    for j in np.arange(1,nz):
        flux2[0,j] = flux2[0,j-1] - bx[0,j-1]*dz
    flux[0,1:] = flux[0,0] - np.cumsum(bx[0,:-1]*dz)

    for i in np.arange(1,nx):
        for j in np.arange(1,nz):
            flux2[i,j] = 0.5*(flux2[i-1,j] + bz[i-1,j]*dx) + 0.5*(flux2[i,j-1] - bx[i,j-1]*dz)

    return flux2 

但没有两个嵌套循环,这需要很长时间。 BxBzflux是相同大小的数组。

我已经设法用数组索引和cumsum替换前两个单循环,但我找不到如何替换嵌套循环。

有什么想法吗?

由于

3 个答案:

答案 0 :(得分:1)

内循环相当简单。你有一个基本的等式,如下所示:

X[n] = a * X[n-1] + b[n]

这个等式可以扩展和重写,而不依赖于X [n-1]:

X[n] = a^n * X[0] + a^(n-1) * b[0] + a^(n-2) * b[1] + ... + a^0 * b[n]

所以如果原始代码看起来像这样:

for i in np.arange(1,nx+1):
    for j in np.arange(1,nz+1):
        flux2[i,j] = 0.5*(flux2[i-1,j] + bz[i-1,j]*dx) \
                   + 0.5*(flux2[i,j-1] - bx[i,j-1]*dz)

你可以像这样摆脱内循环:

a = 0.5
aexp = np.arange(nz).reshape(nz, 1) - np.arange(nz).reshape(1, nz)
abcoeff = a**aexp
abcoeff[aexp<0] = 0
for i in np.arange(1,nx+1):
    b = 0.5*flux2[i-1, 1:] + 0.5*bz[i-1, 1:]*dx - 0.5*bx[i,:-1]*dz
    bvals = (abcoeff * b.reshape(1, nz)).sum(axis=1)
    n = np.arange(1, nz+1)
    x0 = flux2[i, 0]
    flux2[i, 1:] = a**n * x0 + bvals

由于浮点错误,值完全不会相同,但足够接近。 我想在理论上你可以应用相同的程序来摆脱两个循环,但它会变得非常复杂,并且,根据你的阵列的形状,可能不会提供太多的性能优势。

答案 1 :(得分:1)

有可能(ab)使用scipy.ndimage.convolve来解决这类问题。也许在scipy中使用一些过滤器方法也可以工作并且更好,因为它不依赖于scipy.ndimage.convolve在适当的位置工作(我可以想象在未来的某个方面会发生这种变化)。 (编辑:首先写了像numpy.convolve的scipy.signal.convolve,不能这样做)

技巧是这个卷积函数可以就地使用,所以double for循环:

for i in xrange(1, flux.shape[0]):
    for j in xrange(1, flux.shape[1]):
        flux[i,j] = 0.5*(flux[i-1,j] + bz[i-1,j]*dx) + 0.5*(flux[i,j-1] - bx[i,j-1]*dz)

可以替换为(抱歉,需要这么多临时数组......):

from scipy.ndimage import convolve
_flux = np.zeros((flux.shape[0]+1, flux.shape[1]+1), dtype=flux.dtype)
temp_bx = np.zeros((bx.shape[0]+1, bx.shape[1]+1), dtype=bx.dtype)
temp_bz = np.zeros((bz.shape[0]+1, bz.shape[1]+1), dtype=bz.dtype)

_flux[:-1,:-1] = flux
convolve(_flux[:-1,:-1], [[0, 0.5], [0.5, 0]], _flux[1:,1:])

temp_bz[1:,1:-1] = bz[:,1:]*dx
temp_bx[1:-1,1:] = bx[1:,:]*dz

conv_b = np.array([[0.0, 0.5], [0.5, 0.5]])
convolve(temp_bz[:-1,:-1], [[0.5, 0.5], [0.5, 0.]], temp_bz[1:,1:])
convolve(temp_bx[:-1,:-1], [[-0.5, 0.5], [0.5, 0.]], temp_bx[1:,1:])

flux = _flux[:-1,:-1] + temp_by[:-1,:-1] + temp_bx[:-1,:-1]

不幸的是,这意味着我们需要弄清楚bx,bz如何进入最终结果,但这种方法避免了创建2的大权力,并且应该明显快于前一个答案。

(请注意,numpy convolve函数不允许此使用。)

答案 2 :(得分:0)

使用convolve的方法非常好,但模板的完成方式并不明显...... 如果这一行

flux[i,j] = 0.5*(flux[i-1,j] + bz[i-1,j]*dx) + 0.5*(flux[i,j-1] - bx[i,j-1]*dz)

取代
flux[i,j] = a*flux[i-1,j] + b*bz[i-1,j] + c*flux[i,j-1] - d*bx[i,j-1]

我认为第一个卷积(在_flux上)将使用模板[[0,a],[b,0]]。但是bz和bx的其他2个模板是什么,假设a,b,c和d是标量?