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]
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.
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. ]
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
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.
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.]]
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))
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: /
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.
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)