NumPy - Vectorizing loops involving range iterators

牧云@^-^@ 提交于 2020-01-13 10:10:12

问题


Is there any way to make this work without for loops?

import import numpy as np
import matplotlib.pyplot as plt    

L = 1
N = 255
dh = 2*L/N
dh2 = dh*dh

phi_0 = 1
c = int(N/2)
r_0 = L/2

arr = np.empty((N, N))

for i in range(N):

    for j in range(N):

            arr[i, j] = phi_0 if (i - c)**2 + (j - c)**2 < r_0**2/dh2 else 0


plt.imshow(arr)

I've tried calling function(x[None,:], y[:, None]), where:

function(i, j):

    return phi_0 if (i - c)**2 + (j - c)**2 < r_0**2/dh2 else 0

but it requires list .any or .all methods. I'm looking for specifically functionless method (without fromfunction and vectorization). Big thanks!


回答1:


Vectorized solution using open grids

We could use two open range/grid arrays for N simulating the same behavior as the iterators -

I = np.arange(N)
mask = (I[:,None] - c)**2 + (I - c)**2 < r_0**2/dh2
out = np.where(mask,phi_0,0)

For a generic range on the two loops

For the generic case where we would iterate through two loops that extend till say M and N respectively, we could make use of np.ogrid to create those open grids and then use on the same lines -

I,J = np.ogrid[:M,:N]
mask = (I - c)**2 + (J - c)**2 < r_0**2/dh2

For a generic number of loops

For a generic number of loops, simply create as many variables as the number of loops. Hence, for three loops :

for i in range(M):
    for j in range(N):
        for k in range(P):

, we would have :

I,J,K = np.ogrid[:M,:N,:P]

, then use I,J,K instead of i,j,k respectively for element-wise operations like we have here.


Alternative to replace last step for this specific case

Last step could also be implemented with elementwise multiplication by scaling to phi_0 with mask as the else part is setting to 0s -

out = mask*phi_0



回答2:


If you want to use numbers of row and column to calculation, a loop is necessary. You can use one loop. Numpy has ndenumerate attribute that iterate over your matrix.

def function(i, j):
    return phi_0 if (i - c)**2 + (j - c)**2 < r_0**2/dh2 else 0

for (i,j), value in np.ndenumerate(arr):
    arr[i, j] = function(i, j)




回答3:


If your real goal is not to avoid loops, but to get good performance (in this case 670x speedup) a simple approach would be to use a compiler. In this example I use Numba but you can also use Cython, which is a bit more work to to (type declaration,...)

Exampe

import numpy as np
import numba as nb
import matplotlib.pyplot as plt    

L = 1
N = 255
dh = 2*L/N
dh2 = dh*dh

phi_0 = 1
c = int(N/2)
r_0 = L/2

@nb.njit()
def create_arr(N,phi_0,c,r_0,dh2):
  arr = np.empty((N, N))
  for i in range(N):
    for j in range(N):
      if (i - c)**2 + (j - c)**2 < r_0**2/dh2:
        arr[i,j]=phi_0
      else:
        arr[i,j]=0.
  return arr

arr=create_arr(N,phi_0,c,r_0,dh2)

Timings

#Pure Python:   58 ms
#Numba version: 0.086 ms (the first call takes longer and isn't included in the timings)


来源:https://stackoverflow.com/questions/52704777/numpy-vectorizing-loops-involving-range-iterators

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!