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

Pandas分组后应用函数生成新列遇非唯一多索引报错,求解决方法

解决GroupBy Apply时的"cannot handle a non-unique multi-index"错误

问题背景

有一个包含人口统计特征(年龄、种族、性别)、日期和人口计数的DataFrame,部分日期存在统一的缺失缺口。需要完成以下操作:

  • 按人口统计特征分组
  • 对每组的时间序列应用自定义函数,将缺口后的计数重新分配到缺口时段
  • 在原DataFrame生成新列存储处理后的值

但使用groupby.apply()时触发ValueError: cannot handle a non-unique multi-index!错误。

示例输入数据

age          race     gender         date       population
0       15-24          AAPI       Male   2020-01-01              1.0   
1       15-24          AAPI       Male   2020-01-02              2.0
2       15-24          AAPI       Male   2020-01-03              2.0
... 
7       15-24         Black     Female   2020-01-01              0.0   
8       15-24         Black     Female   2020-01-02              NaN
9       15-24         Black     Female   2020-01-03              3.0     

期望输出

age          race     gender         date       population   interpolated
0       15-24          AAPI       Male   2020-01-01              1.0            1.0   
1       15-24          AAPI       Male   2020-01-02              2.0            2.0
2       15-24          AAPI       Male   2020-01-03              2.0            2.0
... 
7       15-24         Black     Female   2020-01-01              0.0            0.0   
8       15-24         Black     Female   2020-01-02              NaN            1.5
9       15-24         Black     Female   2020-01-03              3.0            1.5     

当前代码及错误

自定义处理函数

gaps = [
    {
        "gap": [2020-01-02],
        "day_after": 2020-01-03,
    }
]

def bfill_pop(gaps, group):
    for el in gaps:
        fill_val = group.loc[group["date"] == el["day_after"], "population"] / (
            len(el["gap"]) + 1
        )
        group.loc[group["date"].isin(el["gap"]), "population"] = fill_val
        group.loc[group["date"] == el["day_after"], "population"] = fill_val
    return group.rename(columns={"population": "interpolated"})["interpolated"]

调用代码(原错误版本)

df["interpolated"] = df.groupby(["age", "race", "gender"]).apply(
    lambda g: bfill_pop(gaps, g)
)

错误信息

ValueError: cannot handle a non-unique multi-index!

错误原因分析

  • 核心问题:apply返回的结果保留了分组键+原行索引的MultiIndex,与原DataFrame的单一索引无法对齐,导致Pandas无法处理非唯一索引的赋值操作。
  • 函数自身问题:直接修改传入的分组对象可能污染原数据;fill_val返回Series而非标量,容易引发广播错误。

解决方案

可以通过两种方式实现需求,且都能规避索引问题:

方案1:使用groupby.transform()

transform要求返回与分组同长度的Series,会自动对齐原DataFrame的索引,不会产生MultiIndex问题,是更适配场景的方式:

df["interpolated"] = df.groupby(["age", "race", "gender"]).transform(
    lambda g: bfill_pop(gaps, g)
)

方案2:修正apply的返回结果

在apply后添加.reset_index(drop=True),去掉分组生成的MultiIndex,确保返回结果的索引与原DataFrame一致:

df["interpolated"] = df.groupby(["age", "race", "gender"]).apply(
    lambda g: bfill_pop(gaps, g)
).reset_index(drop=True)

优化后的完整函数

修正原函数的标量提取和数据复制问题,避免潜在bug:

import pandas as pd

# 先将日期列转为datetime类型
df["date"] = pd.to_datetime(df["date"])

gaps = [
    {
        "gap": [pd.to_datetime("2020-01-02")],
        "day_after": pd.to_datetime("2020-01-03"),
    }
]

def bfill_pop(gaps, group):
    # 复制分组数据,避免修改原DataFrame
    group = group.copy()
    # 初始化新列,默认保留原人口数
    group["interpolated"] = group["population"]
    
    for el in gaps:
        # 提取标量值,避免Series赋值的广播问题
        day_after_pop = group.loc[group["date"] == el["day_after"], "population"].iloc[0]
        fill_val = day_after_pop / (len(el["gap"]) + 1)
        
        # 填充缺口日期
        group.loc[group["date"].isin(el["gap"]), "interpolated"] = fill_val
        # 更新缺口后日期的值
        group.loc[group["date"] == el["day_after"], "interpolated"] = fill_val
    
    return group["interpolated"]

# 使用transform调用
df["interpolated"] = df.groupby(["age", "race", "gender"]).transform(lambda g: bfill_pop(gaps, g))

验证结果

运行优化后的代码,即可得到符合预期的输出:Black Female组的2020-01-02和2020-01-03的interpolated值均为1.5,其他未涉及缺口的行保留原人口数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:20:01