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

如何在Pandas聚合/映射中访问MultiIndex的索引值?

解决MultiIndex DataFrame按行聚合时访问索引列的问题

问题重现

构造带MultiIndex(层级i1、i2)的DataFrame:

import pandas as pd

df = pd.DataFrame(
    {
        "i1": list("abcde"),
        "i2": range(10, 20, 2),
        "c1": range(1, 11, 2),
        "c2": list("fghij"),
    }
).set_index(["i1", "i2"])

输出结果:

c1 c2
i1 i2       
a  10   1  f
b  12   3  g
c  14   5  h
d  16   7  i
e  18   9  j

尝试通过自定义函数结合agg更新c1列时,因行对象无法访问索引层级i1抛出KeyError;使用reset_index后聚合,又因索引不匹配无法与原DataFrame求和。实际自定义函数包含复杂条件与副作用:

def _agg(row) -> float:
    import os

    if str(row["c2"]).startswith("f"):
        return float(os.environ.get(str(row["i1"]).upper(), -1))
    elif not str(row["c2"]).startswith("j"):
        return float(os.environ.get(f"MY_VAR_{str(row['i1']).upper()}", 0))
    else:
        return 1

可行解决方案

方案1:用assign临时添加索引列(保留原索引)

你提出的df.assign({"i1": df.index.get_level_values("i1")})是完全可行的方案,代码实现如下:

# 临时将i1索引转为列,执行聚合后与原c1列求和
df["c1"] += df.assign(i1=df.index.get_level_values("i1")).agg(_agg, axis="columns")

此方法会完整保留原MultiIndex,确保行匹配准确,无需担心求和时的索引问题。

方案2:修改函数直接从索引取值(可选)

若不想添加临时列,可在函数中通过行的name属性匹配索引值,需将索引转为DataFrame传入:

def _agg_with_idx(row, idx_df):
    import os
    i1_val = idx_df.loc[row.name, "i1"]
    if str(row["c2"]).startswith("f"):
        return float(os.environ.get(str(i1_val).upper(), -1))
    elif not str(row["c2"]).startswith("j"):
        return float(os.environ.get(f"MY_VAR_{str(i1_val).upper()}", 0))
    else:
        return 1

# 传入索引对应的DataFrame,通过行name匹配i1值
df["c1"] += df.apply(_agg_with_idx, axis="columns", idx_df=df.index.to_frame())

不过该方法逻辑稍复杂,不如assign直观易懂。

关于assign添加列的性能成本

对于大规模数据集,assign的内存与性能开销极低:

  • get_level_values返回的是原索引层级的引用,而非复制数据;
  • assign只是将该引用关联到DataFrame,不会创建新的大规模数据结构;
  • 整体操作几乎不会额外占用内存,执行效率与直接操作原DataFrame接近,完全适用于大数据场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:13:17