在 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 的元素。