it-swarm.com.de

Interpolieren Sie NaN-Werte in einem numpy-Array

Gibt es eine schnelle Möglichkeit, alle NaN-Werte in einem numpy-Array durch (linear) interpolierte Werte zu ersetzen?

Zum Beispiel, 

[1 1 1 nan nan 2 2 nan 0]

würde in umgewandelt werden

[1 1 1 1.3 1.6 2 2  1  0]
47
Petter

Definieren wir zunächst eine einfache Hilfsfunktion, um die Handhabung von Indizes und logischen Indizes von NaNs zu vereinfachen.

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]

Die nan_helper(.) kann nun wie folgt verwendet werden:

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

---
Es mag zwar zunächst etwas übertrieben erscheinen, wenn Sie eine separate Funktion angeben, um nur folgende Funktionen auszuführen:

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

es wird schließlich Dividenden zahlen. 

Wenn Sie also mit NaNs-bezogenen Daten arbeiten, kapseln Sie einfach alle (neuen, mit NaN zusammenhängenden) Funktionen unter einer bestimmten Hilfsfunktion zusammen. Ihre Codebasis wird kohärenter und lesbarer, da sie leicht verständlichen Redewendungen folgt. 

Interpolation ist in der Tat ein Nizza-Kontext, um zu sehen, wie die Handhabung von NaN erfolgt, aber ähnliche Techniken werden auch in verschiedenen anderen Kontexten verwendet.

77
eat

Ich habe diesen Code gefunden:

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

Es druckt 

 [ 1.          1.33333333  1.66666667  2.          2.          1.          0.        ]
22
Petter

Verwenden Sie einfach die numpy-Logik und die Where-Where-Anweisung, um eine 1D-Interpolation anzuwenden.

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
BRYAN WOODS

Es kann einfacher sein, die Art und Weise, wie die Daten generiert werden, zu ändern, wenn nicht:

bad_indexes = np.isnan(data)

Erstellen Sie ein boolesches Array, das angibt, wo sich die Nans befinden

good_indexes = np.logical_not(bad_indexes)

Erstellen Sie ein boolesches Array, das angibt, wo der Wertebereich liegt

good_data = data[good_indexes]

Eine eingeschränkte Version der Originaldaten mit Ausnahme der Nans

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

Führen Sie alle fehlerhaften Indizes durch Interpolation aus

data[bad_indexes] = interpolated

Ersetzen Sie die Originaldaten durch die interpolierten Werte.

5
Winston Ewert

Oder auf Winstons Antwort bauen

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

Ergebnis

[[   1.   20.  300.]
 [   2.   30.  400.]
 [   3.   40.  500.]]
4
user423805

Ich brauchte einen Ansatz, der auch NaNs am Anfang des Endes der Daten ausfüllt, was die Hauptantwort nicht zu sein scheint.

Die von mir entwickelte Funktion verwendet eine lineare Regression, um die NaNs aufzufüllen. Das überwindet mein 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

Hier ist ein Beispiel für einen Anwendungsfall:

# 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))
3
nlml

Für zweidimensionale Daten funktioniert die griddata des SciPys für mich ziemlich gut:

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

Ich verwende es für 3D-Bilder und arbeite mit 2D-Schnitten (4000 Schnitten von 350 x 350). Die ganze Operation dauert immer noch ungefähr eine Stunde: /

2
Gilly

Aufbauend auf der Antwort von Bryan Woods habe ich seinen Code geändert, um auch Listen zu konvertieren, die nur aus NaN bestehen, in eine Liste von Nullen:

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

Einfache Ergänzung, ich hoffe, es wird jemandem nützlich sein.

2
rbnvrw

Leicht optimierte Version basierend auf der Antwort von BRYAN WOODS . Er verarbeitet Start- und Endwerte von Quelldaten korrekt und ist bei 25-30% schneller als die ursprüngliche Version. Sie können auch verschiedene Arten von Interpolationen verwenden (Einzelheiten finden Sie unter scipy.interpolate.interp1d-Dokumentationen).

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