Interpoler des valeurs NaN dans un tableau numpy

Existe-t-il un moyen rapide de remplacer toutes les valeurs de NaN dans un tableau numpy par (disons) les valeurs interpolées linéairement?

Par exemple,

[1 1 1 nan nan 2 2 nan 0]

serait converti en

[1 1 1 1.3 1.6 2 2  1  0]
42
demandé sur ppperry 2011-06-29 13:58:44

8 réponses

permet de définir d'abord une fonction d'aide simple afin de le rendre plus simple à manipuler des indices et des indices logiques de NaNs:

import numpy as np

def nan_helper(y):
    """Helper to handle indices and logical indices of NaNs.

    Input:
        - y, 1d numpy array with possible NaNs
    Output:
        - nans, logical indices of NaNs
        - index, a function, with signature indices= index(logical_indices),
          to convert logical indices of NaNs to 'equivalent' indices
    Example:
        >>> # linear interpolation of NaNs
        >>> nans, x= nan_helper(y)
        >>> y[nans]= np.interp(x(nans), x(~nans), y[~nans])
    """

    return np.isnan(y), lambda z: z.nonzero()[0]

nan_helper(.) peut maintenant être utilisé comme:

>>> y= array([1, 1, 1, NaN, NaN, 2, 2, NaN, 0])
>>>
>>> nans, x= nan_helper(y)
>>> y[nans]= np.interp(x(nans), x(~nans), y[~nans])
>>>
>>> print y.round(2)
[ 1.    1.    1.    1.33  1.67  2.    2.    1.    0.  ]

- - -

Bien qu'il puisse sembler un peu exagéré de spécifier une fonction séparée pour faire des choses comme ceci:

>>> nans, x= np.isnan(y), lambda z: z.nonzero()[0]

il finira par payer des dividendes.

Donc, chaque fois que vous travaillent avec des données liées aux NaNs, il suffit d'encapsuler toutes les fonctionnalités(liées à la nouvelle NaN) nécessaires, sous une ou des fonctions d'aide spécifiques. Votre base de code sera plus cohérente et lisible, car elle suit des idiomes facilement compréhensibles.

L'Interpolation, en effet, est un contexte agréable pour voir comment Nan handling est fait, mais des techniques similaires sont utilisées dans divers autres contextes aussi bien.

65
répondu eat 2016-04-22 17:14:54

j'ai trouvé ce code:

import numpy as np
nan = np.nan

A = np.array([1, nan, nan, 2, 2, nan, 0])

ok = -np.isnan(A)
xp = ok.ravel().nonzero()[0]
fp = A[-np.isnan(A)]
x  = np.isnan(A).ravel().nonzero()[0]

A[np.isnan(A)] = np.interp(x, xp, fp)

print A

Il imprime

 [ 1.          1.33333333  1.66666667  2.          2.          1.          0.        ]
19
répondu Petter 2011-06-29 10:19:56

il suffit d'utiliser numpy logical et là où la déclaration à appliquer une interpolation 1D.

import numpy as np
from scipy import interpolate

def fill_nan(A):
    '''
    interpolate to fill nan values
    '''
    inds = np.arange(A.shape[0])
    good = np.where(np.isfinite(A))
    f = interpolate.interp1d(inds[good], A[good],bounds_error=False)
    B = np.where(np.isfinite(A),A,f(inds))
    return B
8
répondu BRYAN WOODS 2017-05-17 19:07:56

il pourrait être plus facile de changer la façon dont les données sont générées en premier lieu, mais si ce n'est pas le cas:

bad_indexes = np.isnan(data)

créer un tableau booléen indiquant où les nans sont

good_indexes = np.logical_not(bad_indexes)

créer un tableau booléen indiquant où la zone de bonnes valeurs

good_data = data[good_indexes]

Une version restreinte de l'origine des données à l'exclusion de l'nans

interpolated = np.interp(bad_indexes.nonzero(), good_indexes.nonzero(), good_data)

Exécuter tous les mauvais indices par interpolation

data[bad_indexes] = interpolated

remplacer les données originales par les les valeurs interpolées.

4
répondu Winston Ewert 2011-06-29 10:10:58

ou en s'appuyant sur la réponse de Winston

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

A = np.array([[1, 20, 300],
              [nan, nan, nan],
              [3, 40, 500]])

A = np.apply_along_axis(pad, 0, A)
print A

Résultat

[[   1.   20.  300.]
 [   2.   30.  400.]
 [   3.   40.  500.]]
3
répondu user423805 2012-08-22 12:52:46
griddata fonctionne assez bien pour moi:

>>> import numpy as np
>>> from scipy.interpolate import griddata
>>>
>>> # SETUP
>>> a = np.arange(25).reshape((5, 5)).astype(float)
>>> a
array([[  0.,   1.,   2.,   3.,   4.],
       [  5.,   6.,   7.,   8.,   9.],
       [ 10.,  11.,  12.,  13.,  14.],
       [ 15.,  16.,  17.,  18.,  19.],
       [ 20.,  21.,  22.,  23.,  24.]])
>>> a[np.random.randint(2, size=(5, 5)).astype(bool)] = np.NaN
>>> a
array([[ nan,  nan,  nan,   3.,   4.],
       [ nan,   6.,   7.,  nan,  nan],
       [ 10.,  nan,  nan,  13.,  nan],
       [ 15.,  16.,  17.,  nan,  19.],
       [ nan,  nan,  22.,  23.,  nan]])
>>>
>>> # THE INTERPOLATION
>>> x, y = np.indices(a.shape)
>>> interp = np.array(a)
>>> interp[np.isnan(interp)] = griddata(
...     (x[~np.isnan(a)], y[~np.isnan(a)]), # points we know
...     a[~np.isnan(a)],                    # values we know
...     (x[np.isnan(a)], y[np.isnan(a)]))   # points to interpolate
>>> interp
array([[ nan,  nan,  nan,   3.,   4.],
       [ nan,   6.,   7.,   8.,   9.],
       [ 10.,  11.,  12.,  13.,  14.],
       [ 15.,  16.,  17.,  18.,  19.],
       [ nan,  nan,  22.,  23.,  nan]])

Je l'utilise sur des images 3D, opérant sur des tranches 2D (4000 tranches de 350x350). Toute l'opération prend encore environ une heure :/

2
répondu Gilly 2016-06-17 13:23:40

en S'appuyant sur la réponse de Bryan Woods, j'ai modifié son code pour convertir aussi des listes composées uniquement de NaN pour une liste de zéros:

def fill_nan(A):
    '''
    interpolate to fill nan values
    '''
    inds = np.arange(A.shape[0])
    good = np.where(np.isfinite(A))
    if len(good[0]) == 0:
        return np.nan_to_num(A)
    f = interp1d(inds[good], A[good], bounds_error=False)
    B = np.where(np.isfinite(A), A, f(inds))
    return B

plus Simple, j'espère que ça sera utile à quelqu'un.

2
répondu rbnvrw 2017-05-23 12:02:53

j'ai besoin d'une approche qui serait également remplir NaN au début des fin des données, dont la principale réponse ne semble pas faire.

la fonction que j'ai trouvée utilise une régression linéaire pour remplir les NaN. This overcomes my problem:

import numpy as np

def linearly_interpolate_nans(y):
    # Fit a linear regression to the non-nan y values

    # Create X matrix for linreg with an intercept and an index
    X = np.vstack((np.ones(len(y)), np.arange(len(y))))

    # Get the non-NaN values of X and y
    X_fit = X[:, ~np.isnan(y)]
    y_fit = y[~np.isnan(y)].reshape(-1, 1)

    # Estimate the coefficients of the linear regression
    beta = np.linalg.lstsq(X_fit.T, y_fit)[0]

    # Fill in all the nan values using the predicted coefficients
    y.flat[np.isnan(y)] = np.dot(X[:, np.isnan(y)].T, beta)
    return y

Voici un exemple d'utilisation:

# Make an array according to some linear function
y = np.arange(12) * 1.5 + 10.

# First and last value are NaN
y[0] = np.nan
y[-1] = np.nan

# 30% of other values are NaN
for i in range(len(y)):
    if np.random.rand() > 0.7:
        y[i] = np.nan

# NaN's are filled in!
print y
print linearly_interpolate_nans(y)
1
répondu nlml 2016-08-30 09:23:01