跳到主要内容

DataFrame 合并与拼接

merge 用于按键关联行,concat 用于堆叠或对齐整个对象。 核心不在于语法,而在于明确你预期的数据关系。

明确连接意图

import pandas as pd

orders = pd.DataFrame({"order_id": [1, 2, 3], "customer_id": ["A", "A", "B"]})
customers = pd.DataFrame({"customer_id": ["A", "C"], "name": ["Ada", "Lin"]})
result = orders.merge(
customers,
on="customer_id",
how="left",
validate="many_to_one",
indicator=True,
suffixes=("_order", "_customer"),
)

基数校验

在查看输出前,先通过 validate 参数声明预期的关系契约:

  • one_to_one:两侧键均唯一。
  • one_to_many:左侧键唯一。
  • many_to_one:右侧键唯一。
  • many_to_many:预期存在重复;需显式检查输出规模。

意外的重复键会导致行数倍增,进而破坏聚合结果。若数据关系属于业务契约的一部分,务必在合并前检查键的唯一性。

诊断未匹配键

设置 indicator=True 后,检查 _merge 列的值(left_onlyright_onlyboth)。 不要直接丢弃该指示列,而应解释未匹配记录的原因。

注意:Pandas 在合并时会将空值键(null keys)相互匹配,这与典型 SQL 连接语义不同。 如果这种匹配没有业务意义,请先删除、填充或隔离空键。

拼接

january, february = orders.iloc[:2], orders.iloc[2:]
features = orders[["order_id"]]
labels = pd.Series([1, 0, 1], index=orders.index)
rows = pd.concat([january, february], ignore_index=True)
columns = pd.concat([features, labels.rename("target")], axis="columns")

按行拼接(默认)会对齐列名,按列拼接(axis="columns")会对齐索引。 仅当旧行标签无业务含义时,才使用 ignore_index=True。 若需保留来源分区的可追溯性,请添加 keys= 参数。

时间序列连接

在按连接键排序后,使用 merge_asof 进行最近键时间连接。 务必指定方向(direction)和容差(tolerance),防止“最近匹配”退化为任意匹配。

连接类型与行数膨胀

merge API 返回新的 DataFrame。本例左连接保留三条订单,姓名依次为 Ada、Ada、缺失,_merge 依次为 bothbothleft_only。左连接无法发现没有订单的客户;外连接才能把 C 标为 right_only。内连接只保留匹配键(本例两行),右连接保留右侧键,外连接保留两侧键的并集。

同一键若左侧出现两次、右侧出现三次,就会产生六行。validate="many_to_many" 不检查唯一性,也不限制输出规模。如果 customers 中 A 重复,本例的 many_to_one 校验会抛出 pd.errors.MergeError。显式指定 on= 可避免意外地按所有同名列连接。

参考

探索关联打开关联网络