有没有一种快速的方法可以将numpy数组中所有的NaN值替换为(比如)线性插值后的值?
例如,
[1 1 1 nan nan 2 2 nan 0]
会被转换为
[1 1 1 1.3 1.6 2 2 1 0]
有没有一种快速的方法可以将numpy数组中所有的NaN值替换为(比如)线性插值后的值?
例如,
[1 1 1 nan nan 2 2 nan 0]
会被转换为
[1 1 1 1.3 1.6 2 2 1 0]
首先,我们定义一个简单的辅助函数,以便更容易处理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(.)
:
>>> 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. ]
>>> nans, x= np.isnan(y), lambda z: z.nonzero()[0]
这将最终带来回报。
因此,每当您处理与 NaN 相关的数据时,只需将所有(新的 NaN 相关的)所需功能封装在某些特定的帮助器函数下。由于它遵循易于理解的惯用语法,因此您的代码库将更具连贯性和可读性。
插值确实是一个不错的上下文,可以了解如何处理 NaN,但类似的技术也应用于其他各种上下文中。
我想出了这段代码:
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
它打印出
[ 1. 1.33333333 1.66666667 2. 2. 1. 0. ]
只需使用numpy的逻辑与和where语句,即可应用一维插值。
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
对于二维数据,SciPy的griddata
对我来说效果相当不错:
>>> 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]])
我正在对3D图像进行操作,处理的是2D切片(共有4000个尺寸为350x350的切片)。整个操作仍需要大约一个小时 :/
bad_indexes = np.isnan(data)
good_indexes = np.logical_not(bad_indexes)
good_data = data[good_indexes]
这是原始数据的受限版本,不包含NaN值。
interpolated = np.interp(bad_indexes.nonzero(), good_indexes.nonzero(), good_data)
将所有错误的索引通过插值运算处理
data[bad_indexes] = interpolated
使用插值值替换原始数据。
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
结果
[[ 1. 20. 300.]
[ 2. 30. 400.]
[ 3. 40. 500.]]
我使用插值方法替换所有NaN(Not a Number)的值。
A = np.array([1, nan, nan, 2, 2, nan, 0])
np.interp(np.arange(len(A)),
np.arange(len(A))[np.isnan(A) == False],
A[np.isnan(A) == False])
输出:
array([1. , 1.33333333, 1.66666667, 2. , 2. , 1. , 0. ])
基于BRYAN WOODS的回应,稍作优化后的版本。它可以正确处理源数据的起始和结束值,并且比原始版本快25-30%。此外,您可以使用不同类型的插值(有关详细信息,请参见scipy.interpolate.interp1d文档)。
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.])
我需要一种方法,可以在数据的开头和结尾填充NaN值,而主要答案似乎没有做到这一点。
我想出的函数使用线性回归来填充NaN值。这解决了我的问题:
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
# 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))
NaN
组成的列表转换为零的列表: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
pd.DataFrame([1, 3, 4, np.nan, 6]).interpolate().values.ravel().tolist()
- Francisco Zamora-Martínezpd.Series([1, 3, 4, np.nan, 6]).interpolate().values.tolist()
更加简洁。 - Alfepd.Series([1, 3, 4, np.nan, 6]).interpolate().tolist()
更短。 - Shadi