Pandas 动态替换 nan 值
Pandas dynamically replace nan values
我有一个如下所示的 DataFrame:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
a b
0 1.0 4.0
1 2.0 2.0
2 NaN 3.0
3 1.0 NaN
4 NaN NaN
5 NaN 1.0
6 4.0 5.0
7 2.0 NaN
8 3.0 5.0
9 NaN 8.0
我想动态替换 nan 值。我试过 (df.ffill()+df.bfill())/2
但这不会产生所需的输出,因为它会立即将填充值投射到整列,而不是动态投射。我试过 interpolate
,但它不适用于非线性数据。
我看过 但没有完全理解它,不确定它是否有效。
值计算更新
我希望每个 nan 值都是前一个和下一个非 nan 值的平均值。如果序列中有超过 1 个 nan 值,我想一次替换一个,然后计算平均值,例如,如果有 1,np.nan,np.nan,4,我首先想要第一个 nan 值的 1 和 4 (2.5) 的平均值 - 获得 1,2.5,np.nan,4 - 然后第二个 nan 将是 2.5 和 4 的平均值,得到 1,2.5,3.25, 4
期望的输出是
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 2.0
4 2.50 1.5
5 3.25 1.0
6 4.00 5.0
7 2.00 5.0
8 3.00 5.0
9 1.50 8.0
也许不是最优化的,但它有效(注意:从你的例子中,我假设如果在 NaN 之前或之后没有有效值,比如 a 列的最后一行,0 被用作替换):
import pandas as pd
def fill_dynamically(s: pd.Series):
for i in range(len(s)):
s[i] = (
(0 if s[i:].first_valid_index() is None else s[i:][s[i:].first_valid_index()]) +
(0 if s[:i+1].last_valid_index() is None else s[:i+1][s[:i+1].last_valid_index()])
) / 2
像这样使用完整的数据框:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
df.apply(fill_dynamically)
df 申请后:
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 2.0
4 2.50 1.5
5 3.25 1.0
6 4.00 5.0
7 2.00 5.0
8 3.00 5.0
9 1.50 8.0
如果您有其他列并且不想将其应用于整个数据框,您当然可以在单个列上使用它,如下所示:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
fill_dynamically(df['a'])
在这种情况下,df 看起来像这样:
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 NaN
4 2.50 NaN
5 3.25 1.0
6 4.00 5.0
7 2.00 NaN
8 3.00 5.0
9 1.50 8.0
受到@ye olde noobe 回答的启发(感谢他!):
我已经对其进行了优化,使其速度提高了 ≃ 100 倍(下面比较的倍数):
def custom_fillna(s:pd.Series):
for i in range(len(s)):
if pd.isna(s[i]):
last_valid_number = (s[s[:i].last_valid_index()] if s[:i].last_valid_index() is not None else 0)
next_valid_numer = (s[s[i:].first_valid_index()] if s[i:].first_valid_index() is not None else 0)
s[i] = (last_valid_number+next_valid_numer)/2
custom_fillna(df['a'])
df
时间比较:
我有一个如下所示的 DataFrame:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
a b
0 1.0 4.0
1 2.0 2.0
2 NaN 3.0
3 1.0 NaN
4 NaN NaN
5 NaN 1.0
6 4.0 5.0
7 2.0 NaN
8 3.0 5.0
9 NaN 8.0
我想动态替换 nan 值。我试过 (df.ffill()+df.bfill())/2
但这不会产生所需的输出,因为它会立即将填充值投射到整列,而不是动态投射。我试过 interpolate
,但它不适用于非线性数据。
我看过
值计算更新
我希望每个 nan 值都是前一个和下一个非 nan 值的平均值。如果序列中有超过 1 个 nan 值,我想一次替换一个,然后计算平均值,例如,如果有 1,np.nan,np.nan,4,我首先想要第一个 nan 值的 1 和 4 (2.5) 的平均值 - 获得 1,2.5,np.nan,4 - 然后第二个 nan 将是 2.5 和 4 的平均值,得到 1,2.5,3.25, 4
期望的输出是
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 2.0
4 2.50 1.5
5 3.25 1.0
6 4.00 5.0
7 2.00 5.0
8 3.00 5.0
9 1.50 8.0
也许不是最优化的,但它有效(注意:从你的例子中,我假设如果在 NaN 之前或之后没有有效值,比如 a 列的最后一行,0 被用作替换):
import pandas as pd
def fill_dynamically(s: pd.Series):
for i in range(len(s)):
s[i] = (
(0 if s[i:].first_valid_index() is None else s[i:][s[i:].first_valid_index()]) +
(0 if s[:i+1].last_valid_index() is None else s[:i+1][s[:i+1].last_valid_index()])
) / 2
像这样使用完整的数据框:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
df.apply(fill_dynamically)
df 申请后:
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 2.0
4 2.50 1.5
5 3.25 1.0
6 4.00 5.0
7 2.00 5.0
8 3.00 5.0
9 1.50 8.0
如果您有其他列并且不想将其应用于整个数据框,您当然可以在单个列上使用它,如下所示:
df = pd.DataFrame({'a':[1,2,np.nan,1,np.nan,np.nan,4,2,3,np.nan],
'b':[4,2,3,np.nan,np.nan,1,5,np.nan,5,8]
})
fill_dynamically(df['a'])
在这种情况下,df 看起来像这样:
a b
0 1.00 4.0
1 2.00 2.0
2 1.50 3.0
3 1.00 NaN
4 2.50 NaN
5 3.25 1.0
6 4.00 5.0
7 2.00 NaN
8 3.00 5.0
9 1.50 8.0
受到@ye olde noobe 回答的启发(感谢他!):
我已经对其进行了优化,使其速度提高了 ≃ 100 倍(下面比较的倍数):
def custom_fillna(s:pd.Series):
for i in range(len(s)):
if pd.isna(s[i]):
last_valid_number = (s[s[:i].last_valid_index()] if s[:i].last_valid_index() is not None else 0)
next_valid_numer = (s[s[i:].first_valid_index()] if s[i:].first_valid_index() is not None else 0)
s[i] = (last_valid_number+next_valid_numer)/2
custom_fillna(df['a'])
df
时间比较: