You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas多索引:按组特定条件删行及模式行添加优化

问题描述

我创建了带MultiIndex的DataFrame:

np.random.seed(0)
lvl0 = ['A','B']
lvl1 = ['x', 'y', 'z']
idx = pd.MultiIndex.from_product([lvl0, lvl1])
cols = ['c1', 'c2']
df = pd.DataFrame(index=idx, columns=cols)
df.loc[:] = np.random.randint(0,2, size=df.shape)

for v in lvl0:
    df.loc[(v, 'mode'), :] = np.nan
df.sort_index(inplace=True)

生成的DataFrame:

c1   c2
A mode  NaN  NaN
  x       0    1
  y       1    0
  z       1    1
B mode  NaN  NaN
  x       1    1
  y       1    1
  z       1    0

随后计算A、B组各列的众数并填充到对应行:

for c in cols:
    modes = df.groupby(level=0)[c].agg(pd.Series.mode)
    df.loc[(lvl0, 'mode'), c] = modes.values

得到结果:

c1 c2
A mode  1  1
  x     0  1
  y     1  0
  z     1  1
B mode  1  1
  x     1  1
  y     1  1
  z     1  0

现在有两个需求:

  1. 删除所有与对应一级分组(A、B)众数完全相同的行,期望结果如下:
c1 c2
A mode  1  1
  x     0  1
  y     1  0
B mode  1  1
  z     1  0

我可以通过循环lvl0实现,但想找更优雅的方案(比如用groupby)。
2. 能否在计算众数时直接添加mode行,而非预先添加空(NaN)行?因为不预先添加会触发KeyError。

解决方案

1. 优雅删除与分组众数完全一致的行

不用循环分组,直接用groupby+transform就能搞定:

# 先计算每组的众数,取第一个结果(避免多众数情况)
group_modes = df.groupby(level=0).agg(lambda x: pd.Series.mode(x).iloc[0])

# 把每组的众数广播到组内每一行
mode_broadcast = df.groupby(level=0)[cols].transform(lambda x: group_modes.loc[x.name])

# 生成筛选掩码:保留mode行,或者行数据不全等于组众数的行
mask = (df.index.get_level_values(1) == 'mode') | (~df[cols].eq(mode_broadcast).all(axis=1))
df_filtered = df[mask]

运行后df_filtered就是你要的结果。

2. 直接添加mode行(无需预先创建空行)

完全可以不用提前加空行,先算出每组众数,构造新的mode行DataFrame,再和原数据合并排序:

# 生成原始DataFrame(不提前加mode行)
np.random.seed(0)
lvl0 = ['A','B']
lvl1 = ['x', 'y', 'z']
idx = pd.MultiIndex.from_product([lvl0, lvl1])
cols = ['c1', 'c2']
df = pd.DataFrame(index=idx, columns=cols)
df.loc[:] = np.random.randint(0,2, size=df.shape)

# 计算每组众数,构造mode行数据
mode_rows = df.groupby(level=0).agg(lambda x: pd.Series.mode(x).iloc[0])
# 调整索引,把level1设为'mode'
mode_rows = mode_rows.reset_index().assign(level1='mode').set_index(['level0', 'level1'])
mode_rows.index.names = df.index.names  # 保持索引名和原数据一致

# 合并原数据和mode行,再排序索引
df_with_mode = pd.concat([df, mode_rows]).sort_index()

之后要删除重复行的话,直接用第一部分的筛选逻辑就行。

内容的提问来源于stack exchange,提问作者MDescamps

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:01:36