pandas fillna 使用 dict map 和 groupby

pandas fillna using dict map and groupby

我有一个数据如下图:

qty_min qty_max region_min region_max subj region
11            1         10         10   ab     UK
21            1        nan         20   ab     UK
nan         nan        nan         30   ab     UK
nan           2        nan         34   bc     US
nan           2         20        nan   bc     US
10          nan        nan        nan   bc     TZ
11          nan        nan         47   de     TZ
13            3        109         31   de     TZ

df = pd.read_clipboard()
print(df)

我想 fillna() 在每个列中:qty_minqty_maxregion_minregion_max 基于模式。

例如:如果qty_minqty_max列中有NaN,我们需要fillna()使用groupby of subjffill().bfill().

同理,如果region_maxregion_min中有NaN,我们需要fillna()利用regiongroupbyffill().bfill()

所以,我尝试了以下方法:

df['qty_min'] = df.groupby(['subj'], sort=False)['qty_min'].apply(lambda x: x.ffill().bfill())
df['qty_max'] = df.groupby(['subj'], sort=False)['qty_max'].apply(lambda x: x.ffill().bfill())
df['region_min'] = df.groupby(['region'], sort=False)['region_min'].apply(lambda x: x.ffill().bfill())
df['region_max'] = df.groupby(['region'], sort=False)['region_max'].apply(lambda x: x.ffill().bfill())

如您所见,这并不优雅。此外,我在实际数据中有 20 多个像这样的列,我想用相同的方式填充它们(groupby 列和 ffill.bfill()

我已经手动创建了一个如下所示的 dict 来识别相应的 groupby 列来填充 NaN

我愿意修改我们存储此信息的方式。您可以使用任何简单的数据结构。

fillna_dict= {
  "subj": ['qty_min','qty_max'],
  "region": ['region_min','region_max']
}

有什么优雅高效的方法可以做到这一点吗?

我希望我的输出如下所示:

因为你有不同的条件,你需要有几行。

您要做的是重构代码以重用组和单个函数:

f = lambda x: x.ffill().bfill()

g1 = df.groupby(['subj'], sort=False)
g2 = df.groupby(['region'], sort=False)

df['qty_min'] = g1['qty_min'].apply(f)
df['qty_max'] = g1['qty_max'].apply(f)
df['region_min'] = g2['region_min'].apply(f)
df['region_max'] = g2['region_max'].apply(f)

使用你的词典:

f = lambda x: x.ffill().bfill()

fillna_dict= {
  "subj": ['qty_min','qty_max'],
  "region": ['region_min','region_max']
}

for k, cols in fillna_dict.items():
    df[cols] = df.groupby(df[k])[cols].apply(f)

输出:

   qty_min  qty_max  region_min  region_max subj region
0     11.0      1.0        10.0        10.0   ab     UK
1     21.0      1.0        10.0        20.0   ab     UK
2     21.0      1.0        10.0        30.0   ab     UK
3     10.0      2.0        20.0        34.0   bc     US
4     10.0      2.0        20.0        34.0   bc     US
5     10.0      2.0       109.0        47.0   bc     TZ
6     11.0      3.0       109.0        47.0   de     TZ
7     13.0      3.0       109.0        31.0   de     TZ

尝试在函数中实现:

for k,v in fillna_dict.items():
     df[v] = df.groupby([k], sort=False)[v].apply(lambda x: x.ffill().bfill())

输出:

   qty_min  qty_max  region_min  region_max subj region
0     11.0      1.0        10.0        10.0   ab     UK
1     21.0      1.0        10.0        20.0   ab     UK
2     21.0      1.0        10.0        30.0   ab     UK
3     10.0      2.0        20.0        34.0   bc     US
4     10.0      2.0        20.0        34.0   bc     US
5     10.0      2.0       109.0        47.0   bc     TZ
6     11.0      3.0       109.0        47.0   de     TZ
7     13.0      3.0       109.0        31.0   de     TZ

重组你的字典并尝试:

fillna_dict= {"qty_min": "subj",
              "qty_max": "subj",
              "region_min": "region",
              "region_max": "region"
              }

df[list(fillna_dict.keys())] = df[list(fillna_dict.keys())].apply(lambda x: df.groupby(fillna_dict[x.name], sort=False)[x.name].ffill().bfill())

>>> df
   qty_min  qty_max  region_min  region_max subj region
0     11.0      1.0        10.0        10.0   ab     UK
1     21.0      1.0        10.0        20.0   ab     UK
2     21.0      1.0        10.0        30.0   ab     UK
3     10.0      2.0        20.0        34.0   bc     US
4     10.0      2.0        20.0        34.0   bc     US
5     10.0      2.0       109.0        47.0   bc     TZ
6     11.0      3.0       109.0        47.0   de     TZ
7     13.0      3.0       109.0        31.0   de     TZ