[英]Numpy: Fill NaN with values from previous row
我需要用前一行中的值替換 NaN,但第一行中的 NaN 值被替換為零。 什么是最有效的解決方案?
樣品輸入,output -
In [179]: arr
Out[179]:
array([[ 5., nan, nan, 7., 2., 6., 5.],
[ 3., nan, 1., 8., nan, 5., nan],
[ 4., 9., 6., nan, nan, nan, 7.]])
In [180]: out
Out[180]:
array([[ 5., 0, 0., 7., 2., 6., 5.],
[ 3., 0, 1., 8., 2., 5., 5.],
[ 4., 9., 6., 8., 2., 6., 7.]])
(編輯包括(部分?)矢量化方法)
( EDIT2包括一些時間)
匹配所需輸入/輸出的最簡單解決方案是遍歷行:
import numpy as np
def ffill_loop(arr, fill=0):
mask = np.isnan(arr[0])
arr[0][mask] = fill
for i in range(1, len(arr)):
mask = np.isnan(arr[i])
arr[i][mask] = arr[i - 1][mask]
return arr
print(ffill_loop(arr.copy()))
# [[5. 0. 0. 7. 2. 6. 5.]
# [3. 0. 1. 8. 2. 5. 5.]
# [4. 9. 6. 8. 2. 5. 7.]]
您還可以使用矢量化方法,對於較大的輸入可能會更快(彼此下方的nan
越少越好):
import numpy as np
def ffill_roll(arr, fill=0, axis=0):
mask = np.isnan(arr)
replaces = np.roll(arr, 1, axis)
slicing = tuple(0 if i == axis else slice(None) for i in range(arr.ndim))
replaces[slicing] = fill
while np.count_nonzero(mask) > 0:
arr[mask] = replaces[mask]
mask = np.isnan(arr)
replaces = np.roll(replaces, 1, axis)
return arr
print(ffill_roll(arr.copy()))
# [[5. 0. 0. 7. 2. 6. 5.]
# [3. 0. 1. 8. 2. 5. 5.]
# [4. 9. 6. 8. 2. 5. 7.]]
計時這些 function 會得到(包括@Divakar的答案中提出的無循環解決方案):
import numpy as np
from numpy import nan
funcs = ffill_loop, ffill_roll, ffill_cols
sep = ' ' * 4
print(f'{"shape":15s}', end=sep)
for func in funcs:
print(f'{func.__name__:>15s}', end=sep)
print()
for n in (1, 5, 10, 50, 100, 500, 1000, 2000):
k = l = n
arr = np.array([[ 5., nan, nan, 7., 2., 6., 5.] * k,
[ 3., nan, 1., 8., nan, 5., nan] * k,
[ 4., 9., 6., nan, nan, nan, 7.] * k] * l)
print(f'{arr.shape!s:15s}', end=sep)
for func in funcs:
result = %timeit -q -o func(arr.copy())
print(f'{result.best * 1e3:12.3f} ms', end=sep)
print()
shape ffill_loop ffill_roll ffill_cols
(3, 7) 0.009 ms 0.063 ms 0.026 ms
(15, 35) 0.043 ms 0.074 ms 0.034 ms
(30, 70) 0.092 ms 0.098 ms 0.055 ms
(150, 350) 0.783 ms 0.939 ms 0.786 ms
(300, 700) 2.409 ms 4.060 ms 3.829 ms
(1500, 3500) 49.447 ms 105.379 ms 169.649 ms
(3000, 7000) 169.799 ms 340.548 ms 759.854 ms
(6000, 14000) 656.982 ms 1369.651 ms 1610.094 ms
表明ffill_loop()
在大多數情況下實際上是給定輸入最快的。 相反,隨着輸入大小的增加, ffill_cols()
逐漸成為最慢的方法。
這是一個矢量化的 NumPy,其靈感來自Most efficient way to forward-fill NaN values in numpy array's answer post
-
def ffill_cols(a, startfillval=0):
mask = np.isnan(a)
tmp = a[0].copy()
a[0][mask[0]] = startfillval
mask[0] = False
idx = np.where(~mask,np.arange(mask.shape[0])[:,None],0)
out = np.take_along_axis(a,np.maximum.accumulate(idx,axis=0),axis=0)
a[0] = tmp
return out
樣品運行 -
In [2]: a
Out[2]:
array([[ 5., nan, nan, 7., 2., 6., 5.],
[ 3., nan, 1., 8., nan, 5., nan],
[ 4., 9., 6., nan, nan, nan, 7.]])
In [3]: ffill_cols(a)
Out[3]:
array([[5., 0., 0., 7., 2., 6., 5.],
[3., 0., 1., 8., 2., 5., 5.],
[4., 9., 6., 8., 2., 5., 7.]])
import numpy as np
arr = np.array([[ 5., np.nan, np.nan, 7., 2., 6., 5.],
[ 3., np.nan, 1., 8., np.nan, 5., np.nan],
[ 4., 9., 6., np.nan, np.nan, np.nan, 7.]])
nan_indices = np.isnan(arr)
nan_indices 給你的地方:
array([[False, True, True, False, False, False, False],
[False, True, False, False, True, False, True],
[False, False, False, True, True, True, False]])
現在只需使用您在問題中提到的邏輯替換值即可:
arr[0, nan_indices[0, :]] = 0
for row in range(1, np.shape(arr)[0]):
arr[row, nan_indices[row, :]] = arr[row - 1, nan_indices[row, :]]
現在 arr 是:
array([[5., 0., 0., 7., 2., 6., 5.],
[3., 0., 1., 8., 2., 5., 5.],
[4., 9., 6., 8., 2., 5., 7.]])
這個怎么樣?
import numpy as np
x = np.array([[ 5., np.nan, np.nan, 7., 2., 6., 5.],
[ 3., np.nan, 1., 8., np.nan, 5., np.nan],
[ 4., 9., 6., np.nan, np.nan, np.nan, 7.]])
def fillnans(a):
a[0, np.isnan(a[0,:])] = 0
while np.any(np.isnan(a)):
a[np.isnan(a)] = np.roll(a, 1, 0)[np.isnan(a)]
return a
print(x)
print(fillnans(x))
[[ 5. nan nan 7. 2. 6. 5.]
[ 3. nan 1. 8. nan 5. nan]
[ 4. 9. 6. nan nan nan 7.]]
[[5. 0. 0. 7. 2. 6. 5.]
[3. 0. 1. 8. 2. 5. 5.]
[4. 9. 6. 8. 2. 5. 7.]]
我希望這有幫助!
from numpy import *
a = array([[5., nan, nan, 7., 2., 6., 5.],
[3., nan, 1., 8., nan, 5., nan],
[4., 9., 6., nan, nan, nan, 7.]])
在第一行用零替換 nan
where_are_NaNs = isnan(a[0])
a[0][where_are_NaNs] = 0
替換其他行中的 nan
where_are_NaNs = isnan(a)
for i in range(len(where_are_NaNs)):
for j in range(len(where_are_NaNs[0])):
if(where_are_NaNs[i][j]):
a[i][j] = a[i-1][j]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.