Interpolate a 3D array in Python

Interpolate a 3D array in Python



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:


axis=0


>>> [[[ 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.





maybe this answer: stackoverflow.com/a/12073605/8069403 (use np.apply_along_axis)
– xdze2
Aug 30 at 10:39


np.apply_along_axis




2 Answers
2



A solution using apply_along_axis:


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.





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 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 at 13:09


arr


A



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.


np.apply_along_axis





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 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 at 14:42





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 at 14:50


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





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 at 15:01



Required, but never shown



Required, but never shown






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)