1

I have a 3D NumPy array that looks like this:

arr = np.empty((4,4,5))
arr[:] = np.nan
arr[0] = 1
arr[3] = 4

arr
>>> [[[ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]]

     [[ nan nan nan nan nan]
      [ nan nan nan nan nan]
      [ nan nan nan nan nan]
      [ nan nan nan nan nan]]

     [[ nan nan nan nan nan]
      [ nan nan nan nan nan]
      [ nan nan nan nan nan]
      [ nan nan nan nan nan]]

     [[ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]]]

I would like to interpolate along axis=0 so that I get the following:

>>> [[[ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]
      [ 1.  1.  1.  1.  1.]]

     [[ 2.  2.  2.  2.  2.]
      [ 2.  2.  2.  2.  2.]
      [ 2.  2.  2.  2.  2.]
      [ 2.  2.  2.  2.  2.]]

     [[ 3.  3.  3.  3.  3.]
      [ 3.  3.  3.  3.  3.]
      [ 3.  3.  3.  3.  3.]
      [ 3.  3.  3.  3.  3.]]

     [[ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]
      [ 4.  4.  4.  4.  4.]]]

I've been looking at the SciPy module and there seems to be methods to do this on a 1D and 2D array, but not 3D like I need - though I may have missed something.

tda
  • 2,045
  • 1
  • 17
  • 42
  • 1
    maybe this answer: https://stackoverflow.com/a/12073605/8069403 (use `np.apply_along_axis`) – xdze2 Aug 30 '18 at 10:39

2 Answers2

2

A solution using apply_along_axis:

import numpy as np

def pad(data):
    good = np.isfinite(data)
    interpolated = np.interp(np.arange(data.shape[0]),
                             np.flatnonzero(good), 
                             data[good])
    return interpolated


arr = np.arange(6, dtype=float).reshape((3,2))
arr[1, 1] = np.nan
print(arr)

new = np.apply_along_axis(pad, 0, arr)
print(arr)
print(new)

output:

[[ 0.  1.]
 [ 2. nan]
 [ 4.  5.]]

[[ 0.  1.]
 [ 2. nan]
 [ 4.  5.]]

[[0. 1.]
 [2. 3.]
 [4. 5.]]

[edit] The first proposed solution:

With some modification of the code from this answer:

import numpy as np
from scipy import interpolate

A = np.empty((4,4,5))
A[:] = np.nan
A[0] = 1
A[3] = 4

indexes = np.arange(A.shape[0])
good = np.isfinite(A).all(axis=(1, 2)) 

f = interpolate.interp1d(indexes[good], A[good],
                         bounds_error=False,
                         axis=0)

B = f(indexes)
print(B)

gives:

[[[1. 1. 1. 1. 1.]
  [1. 1. 1. 1. 1.]
  [1. 1. 1. 1. 1.]
  [1. 1. 1. 1. 1.]]

 [[2. 2. 2. 2. 2.]
  [2. 2. 2. 2. 2.]
  [2. 2. 2. 2. 2.]
  [2. 2. 2. 2. 2.]]

 [[3. 3. 3. 3. 3.]
  [3. 3. 3. 3. 3.]
  [3. 3. 3. 3. 3.]
  [3. 3. 3. 3. 3.]]

 [[4. 4. 4. 4. 4.]
  [4. 4. 4. 4. 4.]
  [4. 4. 4. 4. 4.]
  [4. 4. 4. 4. 4.]]]

It works well only if NaNs are all on same slice. The slice in which there is an isolated NaN will be ignored.

xdze2
  • 3,986
  • 2
  • 12
  • 29
  • Thanks that looks promising - I've changed your reference to 'A' to 'arr' and am getting a TypeError: 'numpy.ndarray' object is not callable. Was your 'A' an array or an ndarray? – tda Aug 30 '18 at 10:44
  • @TD-Asker I changed `arr` for `A` in the code, it is a nd-array, I do not get this error, do you still have it? – xdze2 Aug 30 '18 at 13:09
0

From the comment provided by xdze2 and previous answer here, I came up with this:

import numpy as np

def pad(data):
    bad_indexes = np.isnan(data)
    good_indexes = np.logical_not(bad_indexes)
    good_data = data[good_indexes]
    interpolated = np.interp(bad_indexes.nonzero()[0], good_indexes.nonzero()[0], 
    good_data)
    data[bad_indexes] = interpolated
    return data

arr = np.empty((4,4,5))
arr[:] = np.nan

arr[0] = 25
arr[3] = 32.5

# Apply the pad method to each 0 axis
new = np.apply_along_axis(pad, 0, arr)

The 'pad' method essentially applies the interpolation and the np.apply_along_axis method ensures that this is applied to the 3D array.

tda
  • 2,045
  • 1
  • 17
  • 42
  • This function also modify in-place the given array, I don't think this effect is wanted? I added in my answer a solution without this effect, (by performing the interpolation over the whole array even if it not strictly necessary) – xdze2 Aug 30 '18 at 13:54
  • That's a good point though I'm having trouble replicating it? You're correct that I do not want to alter the original values so perhaps your suggestion is best. Do you have an example of the code above affecting the original values? – tda Aug 30 '18 at 14:42
  • 1
    for example `print(arr); new = np.apply_along_axis(pad, 0, arr); print(arr);` the second print gives the interpolated array instead of the original one – xdze2 Aug 30 '18 at 14:50
  • I see. Apologies I thought you meant the values were changing rather than the in-place array. For me this does not matter, but well spotted and if your answer leaves the original array alone, then it's the accepted answer! Thanks for your help. – tda Aug 30 '18 at 15:01