
本文介绍如何使用 pd.crosstab 将 dataframe 按 image_id 分组,以 category_id 为列名、chessboard_position 为值,对重复类别进行集合聚合,实现结构化透视表。
本文介绍如何使用 pd.crosstab 将 dataframe 按 image_id 分组,以 category_id 为列名、chessboard_position 为值,对重复类别进行集合聚合,实现结构化透视表。
在 Pandas 数据分析中,常需将长格式(long-form)数据转换为宽格式(wide-form),尤其当目标是将某一列的唯一值作为新列名,并填充对应行的聚合结果时。本例中,原始 DataFrame 包含三列:image_id(图像标识)、category_id(类别编号)和 chessboard_position(棋盘位置)。需求是:以 image_id 为行索引,category_id 的每个唯一值为列名,将对应的所有 chessboard_position 值聚合为集合(set)填入单元格;若某 image_id 下某 category_id 无记录,则显示 NaN。
直接使用 groupby().unstack() 或 pivot() 难以满足“按 category_id 值动态创建列 + 自定义聚合逻辑(如去重集合)”的组合需求。此时,pd.crosstab 是更简洁、语义更清晰的解决方案——它本质是专用于构建交叉频数表的工具,但通过传入 values 和自定义 aggfunc,可扩展为通用透视聚合函数。
以下是推荐实现代码:
import pandas as pd
# 示例数据
df = pd.DataFrame({
"image_id": [0, 0, 0, 1, 1, 1, 1, 1],
"category_id": [7, 8, 9, 6, 6, 6, 0, 0],
"chessboard_position": ["a8", "b8", "c8", "f7", "g7", "h7", "b4", "a2"]
})
out = (
pd.crosstab(
df["image_id"],
df["category_id"],
values=df["chessboard_position"],
aggfunc=lambda v: v.iloc[0] if len(v) == 1 else set(v.tolist())
)
.reset_index()
.rename_axis(columns=None)
)
print(out)输出结果为:
image_id 0 6 7 8 9
0 0 NaN NaN a8 b8 c8
1 1 {a2, b4} {f7, g7, h7} NaN NaN NaN✅ 关键说明:
- values 参数指定待聚合的值列(chessboard_position);
- aggfunc 接收每组的 Series,此处用 lambda 判断长度:单值直接取 .iloc[0](避免返回标量导致类型不一致),多值转为 set 以自动去重并体现集合语义;
- reset_index() 将 image_id 从行索引恢复为普通列;
- rename_axis(columns=None) 清除列索引名称,使输出更整洁。
⚠️ 注意事项:
- crosstab 默认对 values 执行 len 统计(即计数),必须显式传入 values 和 aggfunc 才能做值聚合,否则会报错或返回错误结果;
- 若 chessboard_position 含空值(NaN),set(v.tolist()) 会引发异常,建议提前清洗:df = df.dropna(subset=["chessboard_position"]);
- 如需保持插入顺序(如 f7, g7, h7 而非集合乱序),可用 list(v) 替代 set(v.tolist()),但需注意重复项保留问题。
该方法兼具可读性与鲁棒性,是处理“分类列→新列 + 自定义聚合”场景的标准实践。

















