Замените NaN в массиве NumPy с ближайшим значением, отличным от NaN

У меня есть массив 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 в середине чисел). Любые идеи?

Я могу найти NaN достаточно легко с помощью np.isnan(), но я не могу понять, как получить самое близкое значение для каждого NaN.

Ответы

Ответ 1

Я хочу заменить каждое 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, без рекурсии, без учета списков и т.д.

Ответ 2

В качестве альтернативного решения (это будет линейно интерполировать для массивов NaN в середине):

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]

Ответ 3

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

изменить

Ouch, исходящий из С++, я всегда забываю о диапазонах диапазонов... @aix-решение является более элегантным и эффективным, чем мои циклы С++, используйте это вместо моего.

Ответ 4

Рекурсивное решение!

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]

Ответ 5

У меня есть что-то вроде этого

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

Это немного неудобно, хотя он разделен на две строки с вложенным встроенным, если в одном из них.

Ответ 6

Я столкнулся с проблемой и должен был найти собственное решение для разбросанных 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])