在 pandas 中有选择地使用 fillna()
Using fillna() selectively in pandas
我想有选择地在 DataFrame 中填充 N/A 值。特别是,如果列中有一系列连续的 nan,我希望它们由前面的非 nan 值填充,但前提是 nan 序列的长度低于指定的阈值。例如,如果阈值为 3,则 3 或更少的列内序列将填充前面的非 nan 值,而 4 或更多 nans 的序列将保持原样。
也就是说,如果输入的DataFrame是
2 5 4
nan nan nan
nan nan nan
5 nan nan
9 3 nan
7 9 1
我希望输出为:
2 5 4
2 5 nan
2 5 nan
5 5 nan
9 3 nan
7 9 1
fillna
函数,当应用于 DataFrame 时,有 method 和 limit 选项。但不幸的是,这些还不足以完成任务。我试图指定 method='ffill'
和 limit=3
,但它填充了任何序列的前 3 个纳米,而不是如上所述选择性地填充。
我想这可以通过使用一些条件语句逐列进行编码,但我怀疑一定有更多 Pythonic 的东西。任何关于实现此目标的有效方法的建议?
在 pandas 中与相邻的组一起工作仍然有点尴尬..或者至少我不知道这样做的巧妙方法,这根本不是一回事。 :-)
获得所需内容的一种方法是使用 compare-cumsum-groupby 模式:
In [68]: nulls = df.isnull()
...: groups = (nulls != nulls.shift()).cumsum()
...: to_fill = groups.apply(lambda x: x.groupby(x).transform(len) <= 3)
...: df.where(~to_fill, df.ffill())
...:
Out[68]:
0 1 2
0 2.0 5.0 4.0
1 2.0 5.0 NaN
2 2.0 5.0 NaN
3 5.0 5.0 NaN
4 9.0 3.0 NaN
5 7.0 9.0 1.0
好的,另一个我不喜欢的替代方案,因为它太棘手了:
def method_2(df):
nulls = df.isnull()
filled = df.ffill(limit=3)
unfilled = nulls & (~filled.notnull())
nf = nulls.replace({False: 2.0, True: np.nan})
do_not_fill = nf.combine_first(unfilled.replace(False, np.nan)).bfill() == 1
return df.where(do_not_fill, df.ffill())
这不使用任何 groupby
工具,因此应该会更快。请注意,另一种方法是手动(使用班次)确定要填充哪些元素,因为它们是一组长度为 1、2 或 3 的元素。
我想有选择地在 DataFrame 中填充 N/A 值。特别是,如果列中有一系列连续的 nan,我希望它们由前面的非 nan 值填充,但前提是 nan 序列的长度低于指定的阈值。例如,如果阈值为 3,则 3 或更少的列内序列将填充前面的非 nan 值,而 4 或更多 nans 的序列将保持原样。
也就是说,如果输入的DataFrame是
2 5 4
nan nan nan
nan nan nan
5 nan nan
9 3 nan
7 9 1
我希望输出为:
2 5 4
2 5 nan
2 5 nan
5 5 nan
9 3 nan
7 9 1
fillna
函数,当应用于 DataFrame 时,有 method 和 limit 选项。但不幸的是,这些还不足以完成任务。我试图指定 method='ffill'
和 limit=3
,但它填充了任何序列的前 3 个纳米,而不是如上所述选择性地填充。
我想这可以通过使用一些条件语句逐列进行编码,但我怀疑一定有更多 Pythonic 的东西。任何关于实现此目标的有效方法的建议?
在 pandas 中与相邻的组一起工作仍然有点尴尬..或者至少我不知道这样做的巧妙方法,这根本不是一回事。 :-)
获得所需内容的一种方法是使用 compare-cumsum-groupby 模式:
In [68]: nulls = df.isnull()
...: groups = (nulls != nulls.shift()).cumsum()
...: to_fill = groups.apply(lambda x: x.groupby(x).transform(len) <= 3)
...: df.where(~to_fill, df.ffill())
...:
Out[68]:
0 1 2
0 2.0 5.0 4.0
1 2.0 5.0 NaN
2 2.0 5.0 NaN
3 5.0 5.0 NaN
4 9.0 3.0 NaN
5 7.0 9.0 1.0
好的,另一个我不喜欢的替代方案,因为它太棘手了:
def method_2(df):
nulls = df.isnull()
filled = df.ffill(limit=3)
unfilled = nulls & (~filled.notnull())
nf = nulls.replace({False: 2.0, True: np.nan})
do_not_fill = nf.combine_first(unfilled.replace(False, np.nan)).bfill() == 1
return df.where(do_not_fill, df.ffill())
这不使用任何 groupby
工具,因此应该会更快。请注意,另一种方法是手动(使用班次)确定要填充哪些元素,因为它们是一组长度为 1、2 或 3 的元素。