簡體   English   中英

用前幾行的平均值填充 NaN 值?

[英]Fill NaN values wit mean of previous rows?

我必須用前 3 個實例的平均值填充 dataframe 中列的 nan 值。 這是以下示例:

df = pd.DataFrame({'col1': [1, 3, 4, 5, np.NaN, np.NaN, np.NaN, 7]})
df
col1
0   1.0
1   3.0
2   4.0
3   5.0
4   NaN
5   NaN
6   NaN 
7   7.0

這是我需要的 output:

col1
0   1.0
1   3.0
2   4.0
3   5.0
4   4.0
5   4.3
6   4.4 
7   7.0

我嘗試了 pd.rolling,但是當列在滾動中具有多個 NaN 值時,它不會按我想要的方式工作:

df.fillna(df.rolling(3, min_periods=1).mean().shift())


col1
0   1.0
1   3.0
2   4.0
3   5.0
4   4.0 # np.nanmean([3, 4, 5])
5   4.5 # np.nanmean([np.NaN, 4, 5])
6   5.0 # np.nanmean([np.NaN, np.naN ,5])
7   7.0

有人可以幫我嗎? 提前致謝!

可能不是最有效但簡潔並完成工作

from functools import reduce
reduce(lambda d, _: d.fillna(d.rolling(3, min_periods=3).mean().shift()), range(df['col1'].isna().sum()), df)

output


    col1
0   1.000000
1   3.000000
2   4.000000
3   5.000000
4   4.000000
5   4.333333
6   4.444444
7   7.000000

我們基本上使用fillna但要求min_periods=3意味着它一次只會填充一個 NaN,或者更確切地說是那些在其前面有三個非 NaN 數字的 NaN。 然后我們使用reduce重復這個操作的次數與col1中的NaNs一樣多

我嘗試了兩種方法來解決這個問題。 一個是在 dataframe 上的循環,第二個本質上是多次嘗試您建議的方法,以收斂到正確的答案。

循環方法

對於 dataframe 中的每一行,從 col1 中獲取值。 然后,取最后一行的平均值。 (如果我們在 dataframe 的開頭,此列表中可能少於 3 個。)如果值為 NaN,則將其替換為平均值。 然后,將該值保存回 dataframe。 如果最后一行的值列表中的值超過 3 個,則刪除最后一個。

def impute(df2, col_name):
    last_3 = []
    for index in df.index:
        val = df2.loc[index, col_name]
        if len(last_3) > 0:
            imputed = np.nanmean(last_3)
        else:
            imputed = None
        if np.isnan(val):
            val = imputed
        last_3.append(val)
        df2.loc[index, col_name] = val
        if len(last_3) > 3:
            last_3.pop(0)

重復列操作

這里的核心思想是注意在您的 pd.rolling 示例中,第一個 NA 替換值是正確的。 因此,您應用滾動平均值,為每次運行的 NA 值取第一個 NA 值,然后使用該數字。 如果你重復應用這個,你填寫第一個缺失值,然后是第二個缺失值,然后是第三個。 您需要運行此循環的次數與最長的連續 NA 值系列一樣多。

def impute(df2, col_name):
    while df2[col_name].isna().any().any():
        # If there are multiple NA values in a row, identify just
        # the first one
        first_na = df2[col_name].isna().diff() & df2[col_name].isna()
        # Compute mean of previous 3 values
        imputed = df2.rolling(3, min_periods=1).mean().shift()[col_name]
        # Replace NA values with mean if they are very first NA
        # value in run of NA values
        df2.loc[first_na, col_name] = imputed

性能比較

在 80000 行 dataframe 上運行這兩個,我得到以下結果:

Loop approach takes 20.744 seconds
Repeated column operation takes 0.056 seconds

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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