python 用最接近的非NaN值替换NumPy数组中的NaN

wpx232ag  于 2023-03-21  发布在  Python
关注(0)|答案(8)|浏览(198)

我有一个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最接近的值。

dzhpxtsq

dzhpxtsq1#

作为替代解决方案(这也将对中间的阵列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]
mum43rcc

mum43rcc2#

我想用最接近的非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循环,不需要递归,不需要列表解析等。

ccgok5k5

ccgok5k53#

NaN s有一个有趣的特性,可以比较不同的元素,因此我们可以快速找到非nan元素的索引:

idx = np.nonzero(a==a)[0]

现在很容易用所需的值替换NAN:

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的解决方案比我的Cish循环更优雅和高效,使用它而不是我的。

fd3cxomn

fd3cxomn4#

递归解决方案!

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]
2w2cym1i

2w2cym1i5#

我遇到了这个问题,不得不为分散的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])
o4hqfura

o4hqfura6#

我有这样的东西

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

它有点笨拙,因为它被分成两行,其中一行嵌套了内联if。

l2osamch

l2osamch7#

这里是一个使用简单的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]

它只替换第一个和最后一个NaN,就像这里要求的那样。

kyks70gy

kyks70gy8#

不是最优的,但我发现转换到pandas Series,然后使用interpolate()和“method='nearest'”对我来说是最简单的。但是,有必要确保边缘不是NaN。

data[[0,-1]] = np.flatnonzero(~np.isnan(mapping_read_qual))[[0,-1]]
data = pd.Series(data).interpolate(method='nearest').values

相关问题