
本文介绍如何使用 Pandas 将同一分组(如 Sample)下分散在 Subject 列中的不同语义值(人名、地名、物品名),按固定顺序分别填充至对应的语义列(Person、Place、Thing),实现结构化重组。
本文介绍如何使用 pandes 将同一分组(如 sample)下分散在 `subject` 列中的不同语义值(人名、地名、物品名),按固定顺序分别填充至对应的语义列(`person`、`place`、`thing`),实现结构化重组。
在实际数据清洗中,常遇到“扁平化存储但语义分层”的情况:原始数据将不同类型实体(如人、地点、物品)统一存于一列,而它们在逻辑上应归属不同字段。本例中,每个 Sample 组含恰好三条记录,依次对应 Person → Place → Thing,需将其有序“映射”到对应列。
核心思路是:按 Sample 分组 → 提取每组 Subject 值的有序列表 → 将该列表解包为带索引的 Series(索引即目标列名)→ 展开为宽表。
以下是完整、健壮的实现代码:
import pandas as pd
# 示例数据构建(注意:原始 Subject 列含空字符串或 NaN,此处用空字符串模拟)
df = pd.DataFrame({
"Sample": ["1-1", "1-1", "1-1", "1-2", "1-2", "1-2"],
"Subject": ["Janet", "Boston", "Hat", "Chris", "Austin", "Scarf"],
"Person": ["", "", "", "", "", ""],
"Place": ["", "", "", "", "", ""],
"Thing": ["", "", "", "", "", ""]
})
# ✅ 关键步骤:分组 + 有序映射 + 展开
out = (
df.groupby("Sample")["Subject"]
.apply(lambda x: pd.Series(x.tolist(), index=["Person", "Place", "Thing"]))
.reset_index()
)
# 若需保留原始 Subject 列(通常为首条记录,即 Person 值),可显式添加:
out["Subject"] = out["Person"]
print(out)
输出结果:
Sample Person Place Thing Subject 0 1-1 Janet Boston Hat Janet 1 1-2 Chris Austin Scarf Chris
✅ 注意事项与最佳实践:
- 顺序敏感性:该方法严格依赖每组内 Subject 值的出现顺序(第1个→Person,第2个→Place,第3个→Thing)。请确保原始数据已按语义顺序排序,或先用 sort_values() 预处理。
-
组内数量校验:若某 Sample 组不足3条记录,pd.Series(...) 会自动补 NaN;若超过3条,仅取前3个。建议增加校验逻辑:
assert all(df.groupby("Sample")["Subject"].count() == 3), "每组必须恰好含3个Subject值" - 空值/缺失处理:若原始 Subject 含 NaN,x.tolist() 会保留 nan,后续可链式调用 .fillna("") 清洗。
- 扩展性提示:如语义列更多(如加 Action、Time),只需同步扩展 index=["Person","Place","Thing","Action"] 及确保输入顺序一致。
此方案避免了 pivot(需唯一行列组合)和 transpose(破坏分组结构)的局限,以简洁的函数式链式操作完成语义列对齐,是 Pandas 数据重塑的经典范式之一。










