5

I have a function acted on each 2D slices of a 3D array. How to vectorize the function to avoid loop to improve the performace? For example:

def interp_2d(x0,y0,z0,x1,y1):
    # x0, y0 and z0 are 2D array
    # x1 and y1 are 2D array
    # peform 2D interpolation
    return z1

# now I want to call the interp_2d for each 2D slice of z0_3d as following:
for k in range(z0_3d.shape[2]):
    z1_3d[:,:,k]=interp_2d(x0, y0, z0_3d[:,:,k], x1, y1)
6
  • 1
    What does interp_2d do? Without that information, there's no way to tell if it's possible to vectorize it. For applying a completely generic python function, the way you're doing it isn't going to be vastly slower than any of the other options. "Vectorizing" usually means re-writing the operation using numpy functions/expressions, rather than just applying wrappers like numpy.vectorize. (Which is understandably rather confusing...) Commented Jun 6, 2013 at 16:35
  • 1
    All of that having been said, if interp_2d is just bilinear, cubic, or nearest interpolation, scipy.ndimage.map_coordinates or scipy.ndimage.zoom can be used, depending on the relationship of x0, y0 to x1, y (are both on regular grids, or just z0?). Commented Jun 6, 2013 at 16:39
  • Joe, thank you for your reply. No need to consideration of the implementation of interp_2d. I just want to know if there is a way to vectorize the following code: for k in range(z0_3d.shape[2]): z1_3d[:,:,k]=interp_2d(x0, y0, z0_3d[:,:,k], x1, y1) Commented Jun 6, 2013 at 19:21
  • 3
    Yes, but it's equivalent speed-wise to the for loop you already have. You can use numpy.vectorize to "vectorize" it, but there won't be any appreciable speedup. To use numpy.vectorize, just reorder the arguments in your interp_2d function such that z0 is the first argument (or use a wrapper function). This won't result in any noticeable speedup, though. Commented Jun 6, 2013 at 19:43
  • Note that if you need this to be faster, you could implement the loop and interp_2d in C via weave or something similar.
    – cge
    Commented Jun 6, 2013 at 22:29

1 Answer 1

1

This can not be vectorized without reimplementing interp_2d. However, assuming interp_2d is some type of interpolation, then the operation is probably linear. That is lambda z0: interp_2d(x0, y0, z0, x1, y1) is probably equivalent to np.dot(M, z0) where M is some (probably sparse) matrix that depends on x0, y0, x1 and y1. Right now, by calling the interp_2d function, you are implicitly recalculating this matrix on every call even though it is the same each time. It's more efficient to figure out what that matrix is once and reapply it to the new z0 many times.

Here is a really trivial 1D interpolation example:

x0 = [0., 1.]
x1 = 0.3
z0_2d = "some very long array with shape=(2, n)"

def interp_1d(x0, z0, x1):
    """x0 and z0 are length 2, 1D arrays, x1 is a float between x0[0] and x0[1]."""

    delta_x = x0[1] - x0[0]
    w0 = (x1 - x0[0]) / delta_x
    w1 = (x0[1] - x1) / delta_x
    return w0 * z0[0] + w1 * z0[1]

# The slow way.
for i in range(n):
     z1_2d[i] = interp_1d(x0, z0_2d[:,i], x1)
# Notice that the intermediate products w1 and w2 are the same on each
# iteration but we recalculate them anyway.

# The fast way.
def interp_1d_weights(x0, x1):
    delta_x = x0[1] - x0[0]
    w0 = (x1 - x0[0]) / delta_x
    w1 = (x0[1] - x1) / delta_x
    return w0, w1

 w0, w1 = interp_1d_weights(x0, x1)
 z1_2d = w0 * z0_2d[0,:] + w1 * z0_2d[1:0]

If n is very large, expect a speed up of well over a factor of 100.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.