课程库 › AI 主线 › pandas 数据处理 › 第 7 课
合并数据表 —— merge/concat:多来源数据合体
普通课约 60 分钟
本课导读
真实数据从不住在一张表里:订单在订单表、客户资料在客户表、上半年和下半年各一个文件。分析前先"合体"——本课两件兵器:merge(横着接:按共同的键把两张表的列拼到一起,Excel 的 VLOOKUP 完全体)和 concat(竖着摞:同结构的表首尾相接)。学完你就能回答"上海的 VIP 客户贡献了多少销售额"这种单表答不了的问题。预计 60 分钟。
merge:按键横着接
订单表里只有 customer_id,客户名和等级在另一张表——merge 用共同的键把它们缝起来:
拆解 pd.merge(左表, 右表, on="键"):
- on 指定"亲家凭证"——两张表都有的那一列(customer_id)
- 每行订单按自己的 customer_id 去客户表认领对应行,把 name、level 接到自己屁股后面
- c01 出现两次?客户信息就被复制两份——一对多关系自动展开,这正是想要的
提示
类比记忆:merge 就是凭工牌号接亲家——订单表拿着工牌号(customer_id)去人事表把姓名、级别抄过来。你在 Excel 里手写的 VLOOKUP 就是它的手动挡;merge 是自动挡,还能一次接多列。
合完体,上节课的功夫立刻增值——单表答不了的问题现在能答了:
merge + groupby 是数据分析的黄金组合:合体解锁维度,分组产出结论。
对不上的行怎么办:how 参数
现实的残酷:订单里有个新客户还没进客户表,客户表里有人从没下过单——接不上的行听谁的?how 参数定规矩:
三种姿势一句话各记一条:
- inner(默认):只要"两边都认识"的——最严格,但会悄悄丢行
- left:左表一行不丢,右边接不上就空着——分析订单时的常用款(订单一条不能少,客户信息缺了补查)
- outer:谁都不丢,空位全 NaN——盘点差异时用
说明
merge 头号事故:合完不数行数。inner 悄悄丢了 200 行没人发现,报表少算 200 单。职业习惯:merge 前后各 len() 一下,行数变化必须能解释——又是"彩排/核对"精神,第三次换舞台了。
concat:同结构竖着摞
另一种合体:上半年、下半年两个文件,列一模一样,只是行接着往下长——concat 首尾相接:
concat([表1, 表2, ...])收一个列表,几张摞几张(分月导出的 12 个文件一次摞齐)ignore_index=True:重新编行号——不加的话行标签会是 0,1,2,0,1(各表原编号),后患无穷,基本永远带上
选型口诀收尾:列不同、按键接 → merge(横向找亲家);列相同、往下摞 → concat(纵向续长队)。
✍️ 练习
练习 1:接亲家
把成绩表和学生表按 student_id 合体,回答:每个班的平均分是多少?
✅ 参考答案
import pandas as pd
scores = pd.DataFrame({
"student_id": ["s1", "s2", "s3", "s4"],
"score": [92, 88, 75, 96],
})
students = pd.DataFrame({
"student_id": ["s1", "s2", "s3", "s4"],
"name": ["小雨", "阿杰", "老王", "丽丽"],
"class": ["一班", "二班", "一班", "二班"],
})
full = pd.merge(scores, students, on="student_id")
print(full.groupby("class")["score"].mean())
merge 解锁 class 维度,groupby 出结论——黄金组合最小示范。
练习 2:how 的抉择
商品表里有个 p9 查无此价,价格表里有个 p4 已下架。① 用合适的 how 保证订单一行不丢地接上价格 ② 数一数哪些行没接到价格。
💡 提示
"左表一行不丢" → how="left"。没接到的行,price 列是 NaN → isna 做筛子。
✅ 参考答案
import pandas as pd
order_items = pd.DataFrame({
"order_id": [1, 2, 3, 4],
"product_id": ["p1", "p2", "p9", "p1"],
"qty": [1, 2, 1, 3],
})
prices = pd.DataFrame({
"product_id": ["p1", "p2", "p4"],
"price": [299, 89, 459],
})
full = pd.merge(order_items, prices, on="product_id", how="left")
print(full)
print(f"合并前 {len(order_items)} 行,合并后 {len(full)} 行") # 核对!
print("没接到价格的订单:")
print(full[full["price"].isna()])
how="left" 保住了 p9 那单(inner 会把它悄悄吞掉);NaN 筛出来交人工补价——丢数据和编数据都不行,亮出来才专业。
练习 3:摞完再问(挑战)
两个季度的销售文件 + 一张门店表:先 concat 摞成半年表,再 merge 接上门店城市,最后回答"哪个城市半年销售额最高?"——三种兵器一条龙。
✅ 参考答案
import pandas as pd
q1 = pd.DataFrame({
"shop_id": ["a1", "a2", "a1"],
"sales": [1200, 800, 950],
})
q2 = pd.DataFrame({
"shop_id": ["a2", "a3", "a1"],
"sales": [1100, 700, 1300],
})
shops = pd.DataFrame({
"shop_id": ["a1", "a2", "a3"],
"city": ["上海", "北京", "上海"],
})
half = pd.concat([q1, q2], ignore_index=True) # 竖着摞
full = pd.merge(half, shops, on="shop_id", how="left") # 横着接
result = full.groupby("city")["sales"].sum().sort_values(ascending=False)
print(result)
print(f"冠军:{result.index[0]}")
concat 续长队 → merge 接亲家 → groupby 出结论:这条流水线就是下一课阶段项目的主干。
📝 随堂测验
1. merge 和 concat 的选型口诀是?
2. pd.merge(orders, customers, on="customer_id") 里 on 的作用是?
3. how="inner"(默认)最需要警惕的行为是?
4. how="left" 时,左表某行在右表找不到对应,结果是?
5. concat 时 ignore_index=True 是为了?
本课小结
- merge:按共同键横向接亲家(VLOOKUP 完全体);
how=定对不上的规矩——inner 严格但丢行、left 保左表、outer 全保留 - 职业习惯:merge 前后核对行数;接不上的 NaN 筛出来人工处理
- concat:同结构纵向摞,
ignore_index=True基本必带 - 黄金流水线:concat 续长队 → merge 接亲家 → groupby 出结论
八门兵器全部到手。下一课单元关卡:综合测验 + 一份真刀真枪的脏数据清洗挑战——把整单元的功夫一次亮完。
划选正文任意文字可高亮、批注或加入复习卡
讨论
载入中…