Замените 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])