簡體   English   中英

Numpy:用前一行的值填充 NaN

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

Output

[[ 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM