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

6voto

user423805 Points 888

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

0 votes

C'est plutôt bien, sauf que cela ne fonctionne pas si plus d'une valeur est manquante pour une raison quelconque.

4voto

nlml Points 235

J'avais besoin d'une approche qui permette également de remplir les NaN au début et à la fin des données, ce que la réponse principale ne semble pas faire.

La fonction que j'ai trouvée utilise une régression linéaire pour remplir les NaN. Cela résout mon problème :

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))

4voto

Prokhozhii Points 112

Version légèrement optimisée basée sur la réponse de BRYAN WOODS . Il gère correctement les valeurs de début et de fin des données sources, et il est plus rapide de 25-30% que la version originale. Vous pouvez également utiliser différents types d'interpolations (voir les documentations scipy.interpolate.interp1d pour plus de détails).

import numpy as np
from scipy.interpolate import interp1d

def fill_nans_scipy1(padata, pkind='linear'):
"""
Interpolates data to fill nan values

Parameters:
    padata : nd array 
        source data with np.NaN values

Returns:
    nd array 
        resulting data with interpolated values instead of nans
"""
aindexes = np.arange(padata.shape[0])
agood_indexes, = np.where(np.isfinite(padata))
f = interp1d(agood_indexes
           , padata[agood_indexes]
           , bounds_error=False
           , copy=False
           , fill_value="extrapolate"
           , kind=pkind)
return f(aindexes)

In [17]: adata = np.array([1, 2, np.NaN, 4])
Out[18]: array([ 1.,  2., nan,  4.])
In [19]: fill_nans_scipy1(adata)
Out[19]: array([1., 2., 3., 4.])

0 votes

TypeError : ufunc 'isfinite' non supporté pour les types d'entrée, et les entrées n'ont pas pu être coerciées en toute sécurité vers des types supportés selon la règle de casting ''safe''.

0 votes

Pourriez-vous être plus précis ? Qu'essayez-vous d'interpoler ? Veuillez consulter mon exemple ci-dessus. Tout fonctionne comme prévu.

2voto

rbnvrw Points 337

Sur la base de la réponse de Bryan Woods j'ai modifié son code pour convertir également les listes composées uniquement de NaN à 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

Simple ajout, j'espère qu'il sera utile à quelqu'un.

1voto

Peter Cotton Points 520

Importer scipy me semble excessif. Voici un moyen simple d'utiliser numpy et de conserver les mêmes conventions que np.interp

   def interp_nans(x:[float],left=None, right=None, period=None)->[float]:
    """ 
      e.g. [1 1 1 nan nan 2 2 nan 0] -> [1 1 1 1.3 1.6 2 2  1  0]

    """
    xp = [i for i, yi in enumerate(x) if np.isfinite(yi)]
    fp = [yi for i, yi in enumerate(x) if np.isfinite(yi)]
    return list(np.interp(x=list(range(len(x))), xp=xp, fp=fp,left=left,right=right,period=period))

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