我有一个 NumPy 数组
a
如下所示:
>>> str(a)
'[ nan nan nan 1.44955726 1.44628034 1.44409573\n 1.4408188 1.43657094 1.43171624 1.42649744 1.42200684 1.42117704\n 1.42040255 1.41922908 nan nan nan nan\n nan nan]'
我想用最接近的非 NaN 值替换每个 NaN,这样开头的所有 NaN 都设置为
1.449...
,最后的所有 NaN 都设置为 1.419...
.
我可以看到如何针对像这样的特定情况执行此操作,但我需要能够对任何长度的数组进行一般操作,在数组的开头和结尾处使用任何长度的 NaN(其中没有 NaN)数字的中间)。有什么想法吗?
我可以用
np.isnan()
很容易地找到NaN,但我无法弄清楚如何获得最接近每个NaN的值。
作为替代解决方案(这也将对中间的数组
NaN
s进行线性插值):
import numpy as np
# Generate data...
data = np.random.random(10)
data[:2] = np.nan
data[-1] = np.nan
data[4:6] = np.nan
print data
# Fill in NaN's...
mask = np.isnan(data)
data[mask] = np.interp(np.flatnonzero(mask), np.flatnonzero(~mask), data[~mask])
print data
这产生:
[ nan nan 0.31619306 0.25818765 nan nan
0.27410025 0.23347532 0.02418698 nan]
[ 0.31619306 0.31619306 0.31619306 0.25818765 0.26349185 0.26879605
0.27410025 0.23347532 0.02418698 0.02418698]
我想用最接近的非 NaN 值替换每个 NaN...数字中间不会有 NaN
以下将做到这一点:
ind = np.where(~np.isnan(a))[0]
first, last = ind[0], ind[-1]
a[:first] = a[first]
a[last + 1:] = a[last]
这是一个直接的
numpy
解决方案,不需要 Python 循环、递归、列表理解等。
NaN
s 具有比较自己不同的有趣属性,因此我们可以快速找到非 nan 元素的索引:
idx = np.nonzero(a==a)[0]
现在很容易用所需的值替换 nans:
for i in range(0, idx[0]):
a[i]=a[idx[0]]
for i in range(idx[-1]+1, a.size)
a[i]=a[idx[-1]]
最后,我们可以把它放在一个函数中:
import numpy as np
def FixNaNs(arr):
if len(arr.shape)>1:
raise Exception("Only 1D arrays are supported.")
idxs=np.nonzero(arr==arr)[0]
if len(idxs)==0:
return None
ret=arr
for i in range(0, idxs[0]):
ret[i]=ret[idxs[0]]
for i in range(idxs[-1]+1, ret.size):
ret[i]=ret[idxs[-1]]
return ret
编辑
哎哟,来自 C++,我总是忘记列表范围...@aix 的解决方案比我的 C++ 循环更优雅和高效,用它代替我的。
递归解决方案!
def replace_leading_NaN(a, offset=0):
if a[offset].isNaN():
new_value = replace_leading_NaN(a, offset + 1)
a[offset] = new_value
return new_value
else:
return a[offset]
def replace_trailing_NaN(a, offset=-1):
if a[offset].isNaN():
new_value = replace_trailing_NaN(a, offset - 1)
a[offset] = new_value
return new_value
else:
return a[offset]
我遇到了这个问题,不得不为分散的 NaN 找到一个定制的解决方案。下面的函数用右边出现的第一个数字替换任何 NaN,如果不存在,则用左边出现的第一个数字替换它。可以进行进一步的操作以将其替换为边界出现的平均值。
import numpy as np
Data = np.array([np.nan,1.3,np.nan,1.4,np.nan,np.nan])
nansIndx = np.where(np.isnan(Data))[0]
isanIndx = np.where(~np.isnan(Data))[0]
for nan in nansIndx:
replacementCandidates = np.where(isanIndx>nan)[0]
if replacementCandidates.size != 0:
replacement = Data[isanIndx[replacementCandidates[0]]]
else:
replacement = Data[isanIndx[np.where(isanIndx<nan)[0][-1]]]
Data[nan] = replacement
结果是:
>>> Data
array([ 1.3, 1.3, 1.4, 1.4, 1.4, 1.4])
我有这样的东西
i = [i for i in range(len(a)) if not np.isnan(a[i])]
a = [a[i[0]] if x < i[0] else (a[i[-1]] if x > i[-1] else a[x]) for x in range(len(a))]
有点笨拙,因为它分为两行,如果在其中一行中,则嵌套内联。
这里是一个使用简单的 python 迭代器的解决方案。它们在这里实际上比
numpy.where
更有效率,尤其是对于大数组!请参阅类似代码的比较here.
import numpy as np
a = np.array([np.NAN, np.NAN, np.NAN, 1.44955726, 1.44628034, 1.44409573, 1.4408188, 1.43657094, 1.43171624, 1.42649744, 1.42200684, 1.42117704, 1.42040255, 1.41922908, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN])
mask = np.isfinite(a)
# get first value in list
for i in range(len(mask)):
if mask[i]:
first = i
break
# get last vaue in list
for i in range(len(mask)-1, -1, -1):
if mask[i]:
last = i
break
# fill NaN with near known value on the edges
a = np.copy(a)
a[:first] = a[first]
a[last + 1:] = a[last]
print(a)
输出:
[1.44955726 1.44955726 1.44955726 1.44955726 1.44628034 1.44409573
1.4408188 1.43657094 1.43171624 1.42649744 1.42200684 1.42117704
1.42040255 1.41922908 1.41922908 1.41922908 1.41922908 1.41922908
1.41922908 1.41922908]
它只替换这里要求的第一个和最后一个 NaNs。
不是最佳的,但我发现转换为 pandas Series 然后使用 interpolate() with "method='nearest'" 对我来说是最简单的。但是,有必要确保边缘是非 NaN 的。
data[[0,-1]] = np.flatnonzero(~np.isnan(mapping_read_qual))[[0,-1]]
data = pd.Series(data).interpolate(method='nearest').values