76 votes

Interpoler des valeurs NaN dans un tableau numpy

Existe-t-il un moyen rapide de remplacer toutes les valeurs 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]

8 votes

Je m'excuse d'écrire sur un vieux fil de discussion, mais je pense que la confusion en vaut la peine. Une manière plus simple est d'utiliser pandas et numpy : pd.DataFrame([1, 3, 4, np.nan, 6]).interpolate().values.ravel().tolist()

7 votes

J'ai trouvé pd.Series([1, 3, 4, np.nan, 6]).interpolate.get_values().tolist() encore plus courte.

0 votes

À partir de pandas 1.2.4 : pd.Series([1, 3, 4, np.nan, 6]).interpolate().tolist() encore plus court

117voto

eat Points 4573

Définissons tout d'abord une simple fonction d'aide afin de rendre plus simple la gestion des indices et des indices logiques de l'alphabet. 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]

Maintenant, le 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 d'abord sembler un peu exagéré de spécifier une fonction séparée pour faire juste ce genre de choses :

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

cela finira par rapporter des dividendes.

Ainsi, chaque fois que vous travaillez avec des données liées aux NaNs, il suffit d'encapsuler toutes les fonctionnalités (liées aux NaNs) nécessaires, dans une ou plusieurs fonctions d'aide spécifiques. Votre code de base sera plus cohérent et plus lisible, car il suivra des idiomes facilement compréhensibles.

L'interpolation est en effet un bon contexte pour voir comment la gestion des NaN est effectuée, mais des techniques similaires sont également utilisées dans d'autres contextes.

28voto

Petter Points 5822

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

4 votes

@fmonegaglia, malheureusement ce script n'interpole que sur un axe des tableaux 2D, ce n'est pas une interpolation 2D. Le besoin d'interpolation sur les NaNs dans les tableaux 2D a un problème scipy : github.com/scipy/scipy/issues/1682

0 votes

D'après le numéro référencé, vous pouvez utiliser directement la fonction convolve d'Astropy.

4 votes

Remplacer le - par ~ pour que cela fonctionne (possibilité de changement de version dans le temps)

13voto

BRYAN WOODS Points 121

Il suffit d'utiliser numpy logical et there where statement pour 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

3 votes

Cela ne gère pas les NaN au début ou à la fin de la séquence.

10voto

Gilly Points 747

Pour les données bidimensionnelles, l'outil SciPy's 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, en opérant sur des coupes 2D (4000 coupes de 350x350). L'ensemble de l'opération prend encore environ une heure :/

1 votes

Merci pour cette solution simple et compacte ! Cela prend tellement de temps, car, ironiquement, griddata ne profite pas de la propriété de la grille.

0 votes

C'est une excellente solution (bien que longue), merci !

6voto

Winston Ewert Points 17746

Il serait peut-être plus facile de changer la façon dont les données sont générées en premier lieu, mais sinon.. :

bad_indexes = np.isnan(data)

Créer un tableau booléen indiquant où se trouvent les nans.

good_indexes = np.logical_not(bad_indexes)

Créer un tableau booléen indiquant où se trouvent les bonnes valeurs

good_data = data[good_indexes]

Une version restreinte des données originales excluant les nans.

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

Passez tous les mauvais index par interpolation.

data[bad_indexes] = interpolated

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

0 votes

Ça ne marche pas pour moi. J'obtiens ValueError: setting an array element with a sequence. pour l'appel interp

2 votes

@Ben, Désolé, je n'ai pas pu/ne peux pas le tester maintenant. Essayez d'ajouter [0] après les deux nonzero()s.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X