【问题标题】:Pandas ffill limit groups of nan less than limit onlyPandas 仅填充 nan 小于 limit 的限制组
【发布时间】:2018-04-22 03:52:50
【问题描述】:

我希望在我的 pandas 数据框中转发填充一些值,但有限制。但是,限制只需要填充连续 nan 计数小于或等于限制的 nan 组。这是一个例子,

创建一个缺少数据的df,

import numpy as np
import pandas as pd

df = pd.DataFrame(
        {'val': [1, 1, np.nan, np.nan, 2, 3, np.nan, np.nan, np.nan, np.nan, 1, 1]}
)

print(df)

    val
0     1.0
1     1.0
2     NaN
3     NaN
4     2.0
5     3.0
6     NaN
7     NaN
8     NaN
9     NaN
10    1.0
11    1.0

现在,如果我们将它填充到所有 nan 中,最多向前 2 步,

print(df.ffill(limit=2))

    val
0     1.0
1     1.0
2     1.0
3     1.0
4     2.0
5     3.0
6     3.0 #
7     3.0 #
8     NaN
9     NaN
10    1.0
11    1.0

当我不希望它们时,那​​些用#fill 读取(上图)。我想要的是得到以下,

print(df.ffill(limit=2, dont_fill_any_nan_gaps_bigger_than_limit=True))

    val
0     1.0
1     1.0
2     1.0
3     1.0
4     2.0
5     3.0
6     NaN # 
7     NaN #
8     NaN 
9     NaN 
10    1.0
11    1.0

当然这不需要是ffill中的单个参数,而是具有相同效果的东西。

【问题讨论】:

  • 刚刚意识到我完成了一半的标题。我添加了一个标题,但请提出一个更好的标题。不知道怎么写...

标签: python pandas nan


【解决方案1】:

我从出色的答案以及来自 jezral 的 answer 到我之前的 question 都获得了灵感,并将其应用于整个 DataFrame。

我这样做是为了一次性处理整个 DataFrame,并且每一列都会ffill 各自列中的缺失值(带有我的额外约束)。

设置一个数据框,

df = pd.DataFrame(
    {'val1': [1, 1, np.nan, np.nan, 2, 3, np.nan, np.nan, np.nan, np.nan, 1, 1],
     'val2': [1, 2, np.nan, np.nan, 2, 4, 4, np.nan, np.nan, np.nan, np.nan, 2]}
)

print(df)

    val1    val2
0   1.0     1.0
1   1.0     2.0
2   NaN     NaN
3   NaN     NaN
4   2.0     2.0
5   3.0     4.0
6   NaN #   4.0
7   NaN #   NaN #
8   NaN #   NaN #
9   NaN #   NaN #
10  1.0     NaN #
11  1.0     2.0

现在做maskffill

mask_df = (
    df.isnull()
      .groupby([df.notnull().all(axis=1).cumsum()])
      .rank(method='max') - 1
).gt(2)

df = df.ffill().mask(mask_df)
print(df)

    val1    val2
0   1.0     1.0
1   1.0     2.0
2   1.0     2.0
3   1.0     2.0
4   2.0     2.0
5   3.0     4.0
6   NaN #   4.0
7   NaN #   NaN #
8   NaN #   NaN #
9   NaN #   NaN #
10  1.0     NaN #
11  1.0     2.0

说明

我们groupby 使用非nan 值的累积和。这意味着nan 值组合在一起。如果我们取其中最大的rank,我们得到nan 序列的长度+ 1。现在我们只需使用mask 函数,如cᴏʟᴅsᴘᴇᴇᴅ 的answer 所示。

【讨论】:

  • 如果设置df.iloc[9,1] = 23,如果您想分别处理df 的每一列,这将停止工作。我目前的解决方法是定义一个掩码函数def masker(df, limit): return (df.isna().groupby([df.notna().cumsum()]).rank(method='max') - 1).gt(limit) 并沿列应用此函数:mask_df = df_ext.apply(masker, axis=0, args=(limit,))。我想这对于大数据帧来说相当慢。有没有什么方法可以不用申请直接做到这一点?
  • @Scotty1- 我不明白为什么df.iloc[9,1] = 23 会破坏这一点。治疗原因可能比治疗症状更好,但我正在研究这个。
  • @josh [df.notnull().all(axis=1).cumsum()] with axis=1 as groupby 参数只考虑全是nan的行。但是我无法对此进行矢量化处理,只能使用 apply 为我工作。不幸的是,在中等大小的数据帧上使用这种屏蔽方法非常慢。但老实说:我不擅长 groupby 等,因此我很确定有更好的方法。
  • @Scotty1- 抱歉,我现在关注。这会很棘手,但这是一个非常好的问题,因此值得为它创建一个新帖子。
  • @josh 好的,我明天会打开一个后续问题并将链接发布在 cmets 中。
【解决方案2】:

您可以无条件ffill,前提是您在使用pd.Series.mask/pd.Series.where/np.where后取消填写。

v = df.value.isna()  # df.value.isnull()
df = df.ffill(limit=2).mask(
     v.groupby(v.ne(v.shift()).cumsum()).transform('size').gt(2)
)

    value
0     1.0
1     1.0
2     1.0
3     1.0
4     2.0
5     3.0
6     NaN
7     NaN
8     NaN
9     NaN
10    1.0
11    1.0

【讨论】:

  • 我看到isna被介绍了。是否推荐代替isnull
  • @josh 不,它们是不同的名称,但都做同样的事情。
  • 对不起,我知道他们做同样的事情。我的意思是问是否建议使用isna 澄清/是否不推荐使用isnull
  • @josh 为了可移植性,使用 isnull,我认为 isna 仅在 0.21 中引入。目前两者都没有被弃用。
【解决方案3】:

创建掩码以过滤出所有具有NaNs 且大小高于groupbytransform size 的行,并仅将ffill 应用于具有反转条件的过滤行~

a = df['value'].isna()
a = a.ne(a.shift()).cumsum()
m = (a.groupby(a).transform('size') > 2)
df[~m] = df[~m].ffill(limit=2)
print (df)
    value
0     1.0
1     1.0
2     1.0
3     1.0
4     2.0
5     3.0
6     NaN
7     NaN
8     NaN
9     NaN
10    1.0
11    1.0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-08
    • 2015-02-14
    • 1970-01-01
    • 2019-03-21
    • 1970-01-01
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多