Pandas数据合并完全指南:merge、concat、join从入门到精通

张开发
2026/4/15 22:38:23 15 分钟阅读

分享文章

Pandas数据合并完全指南:merge、concat、join从入门到精通
目录一、为什么需要数据合并二、merge最强大的数据关联工具2.1 merge的核心定位2.2 merge的基本语法2.3 四种连接类型详解2.4 实战代码示例2.5 merge的高级技巧2.6 merge的最佳实践三、concat最简单直接的数据拼接3.1 concat的核心定位3.2 concat的基本语法3.3 纵向拼接 vs 横向拼接3.4 处理索引冲突3.5 concat的高级技巧四、join基于索引的便捷合并4.1 join的核心定位4.2 join的基本语法4.3 join的使用场景4.4 merge vs join到底该用哪个五、三剑客终极对比5.1 核心差异一览表5.2 选择策略速查表六、实战案例综合运用三种方法七、常见坑点与避坑指南一、为什么需要数据合并在实际的数据分析工作中我们经常需要处理来自多个来源的数据集。比如将订单数据与客户信息合并、将多个月的销售记录拼接成一个完整的数据集、将用户基本信息和行为日志整合在一起。Pandas作为Python数据分析的基石提供了三种核心的数据合并方法merge基于列值的关联性合并类似于SQL中的JOIN操作。concat沿特定轴进行结构性堆叠适合拼接相似结构的数据。join基于索引的便捷合并本质上是merge的一种特化形式。理解这三者的区别和适用场景是写出高效、清晰的数据处理代码的关键。二、merge最强大的数据关联工具2.1 merge的核心定位pd.merge()是Pandas中最强大、最常用的数据合并方法。它用于根据一个或多个键key将两个DataFrame的行连接起来类似于SQL中的JOIN操作。merge的核心思想是找到两个表中共同的字段键然后将它们像拼接乐高积木一样组合在一起。当一个表有用户信息另一个表有订单信息你想知道每个订单对应的用户叫什么名字时merge就是最佳选择。2.2 merge的基本语法python pd.merge( left, # 左侧DataFrame right, # 右侧DataFrame howinner, # 连接方式: inner/outer/left/right onNone, # 连接键列名 left_onNone, # 左侧连接键 right_onNone, # 右侧连接键 left_indexFalse, # 是否使用左侧索引作为连接键 right_indexFalse, # 是否使用右侧索引作为连接键 suffixes(_x, _y) # 重复列名的后缀 )2.3 四种连接类型详解连接类型作用适用场景inner内连接只保留两个表中键匹配的行需要精确匹配的数据如订单与发货记录left左连接保留左表所有行右表无匹配则填充NaN需要保留主表完整信息如所有客户及其订单right右连接保留右表所有行左表无匹配则填充NaN优先保留右侧数据完整性outer外连接保留两个表的所有行无匹配填充NaN数据一致性检测查找不匹配记录2.4 实战代码示例inner join内连接python import pandas as pd df1 pd.DataFrame({ user_id: [1, 2, 3], name: [张三, 李四, 王五] }) df2 pd.DataFrame({ user_id: [1, 2, 4], age: [25, 30, 28] }) result pd.merge(df1, df2, onuser_id, howinner) print(result) # user_id name age # 0 1 张三 25 # 1 2 李四 30 # 只保留了user_id为1和2的行因为user_id3和4不匹配left join左连接python result_left pd.merge(df1, df2, onuser_id, howleft) print(result_left) # user_id name age # 0 1 张三 25.0 # 1 2 李四 30.0 # 2 3 王五 NaN # 保留了左表的所有行王五没有年龄信息填充为NaNouter join外连接python result_outer pd.merge(df1, df2, onuser_id, howouter, indicatorTrue) print(result_outer) # user_id name age _merge # 0 1 张三 25.0 both # 1 2 李四 30.0 both # 2 3 王五 NaN left_only # 3 4 NaN 28.0 right_only # indicator参数可以标识每条记录的来源2.5 merge的高级技巧多键合并当单个字段不足以唯一标识一条记录时可以使用多个列作为连接键。python df1 pd.DataFrame({ year: [2023, 2023, 2024], month: [1, 2, 1], sales: [100, 150, 200] }) df2 pd.DataFrame({ year: [2023, 2023, 2024], month: [1, 2, 2], target: [120, 140, 180] }) result pd.merge(df1, df2, on[year, month])处理列名冲突当两个DataFrame有相同的非键列名时使用suffixes参数添加后缀区分。python df1 pd.DataFrame({id: [1, 2], value: [A, B]}) df2 pd.DataFrame({id: [1, 3], value: [X, Y]}) result pd.merge(df1, df2, onid, suffixes(_left, _right)) print(result) # id value_left value_right # 0 1 A X # 1 2 B NaN2.6 merge的最佳实践在使用merge时有几个注意事项能帮你避免踩坑1. 验证键的唯一性如果连接键存在重复值可能导致笛卡尔积行数意外增加。合并前先执行df[key].is_unique检查。2. 大数据集性能优化当处理百万级以上的数据时可以考虑先将连接键设为索引再使用join方法后者在某些场景下效率更高。3. 善用indicator参数设置为True会添加一个特殊列标明每行数据的来源both/left_only/right_only这对于数据质量审计非常有用。三、concat最简单直接的数据拼接3.1 concat的核心定位pd.concat()是Pandas中最简单直接的数据合并方式。它用于沿着某个轴行或列将多个DataFrame或Series堆叠在一起类似于在物理上把多个表格上下摞起来或左右并排放置。concat不关心列值之间的匹配关系它只是纯粹的结构性拼接。当你有多个月份的销售数据它们的列结构完全一致想把它们拼成一个完整的数据集时concat就是最合适的选择。3.2 concat的基本语法python pd.concat( objs, # 要拼接的DataFrame列表 axis0, # 0表示纵向拼接行1表示横向拼接列 joinouter, # 处理索引对齐的方式outer保留所有inner保留交集 ignore_indexFalse, # 是否重置索引 keysNone, # 添加多级索引用于标识数据来源 sortFalse # 是否对非连接轴进行排序 )3.3 纵向拼接 vs 横向拼接纵向拼接axis0默认相当于把两个表格上下叠在一起要求列名对齐。这是最常见的用法。python # 多个月份的销售数据 df_jan pd.DataFrame({product: [A, B], sales: [100, 150]}) df_feb pd.DataFrame({product: [A, B], sales: [120, 140]}) df_mar pd.DataFrame({product: [A, C], sales: [110, 130]}) # 纵向拼接 result pd.concat([df_jan, df_feb, df_mar], ignore_indexTrue) print(result) # product sales # 0 A 100 # 1 B 150 # 2 A 120 # 3 B 140 # 4 A 110 # 5 C 130横向拼接axis1相当于把两个表格左右并排放置要求索引对齐。python df_left pd.DataFrame({A: [1, 2, 3]}, index[0, 1, 2]) df_right pd.DataFrame({B: [4, 5, 6]}, index[0, 1, 2]) result pd.concat([df_left, df_right], axis1) print(result) # A B # 0 1 4 # 1 2 5 # 2 3 63.4 处理索引冲突纵向拼接时原始索引会被保留可能导致重复的索引值。设置ignore_indexTrue可以重新生成0,1,2...的连续索引。python # 不重置索引索引会重复 result pd.concat([df1, df2]) # 索引可能是 [0,1,0,1] 这样 # 重置索引推荐做法 result pd.concat([df1, df2], ignore_indexTrue) # 索引变成 [0,1,2,3]3.5 concat的高级技巧使用keys参数标识数据来源当拼接多个数据源后想追踪每行来自哪个原始表可以使用keys添加多级索引。python result pd.concat([df_jan, df_feb], keys[Jan, Feb]) # 生成的多级索引可以清晰地标识数据来源 print(result.loc[Jan]) # 只查看1月份的数据处理列名不一致的情况使用joininner可以只保留所有表共有的列。python df1 pd.DataFrame({A: [1, 2], B: [3, 4]}) df2 pd.DataFrame({A: [5, 6], C: [7, 8]}) # joinouter默认保留所有列缺失填充NaN result_outer pd.concat([df1, df2], ignore_indexTrue) # A B C # 0 1 3.0 NaN # 1 2 4.0 NaN # 2 5 NaN 7.0 # 3 6 NaN 8.0 # joininner只保留共有的列A列 result_inner pd.concat([df1, df2], ignore_indexTrue, joininner) # A # 0 1 # 1 2 # 2 5 # 3 6四、join基于索引的便捷合并4.1 join的核心定位DataFrame.join()是merge的一种特化形式专门用于基于索引的数据合并。它的设计目标是提供一个更简洁、更高效的API来处理索引对齐的场景。当你的数据是时间序列以日期为索引或者两个DataFrame共享同一套索引时join会比merge更简洁在某些大数据场景下性能也更好。4.2 join的基本语法python df_left.join( df_right, # 右侧DataFrame howleft, # 连接方式默认left onNone, # 左侧用于对齐的列而非索引 lsuffix, # 左侧重复列名的后缀 rsuffix, # 右侧重复列名的后缀 sortFalse # 是否排序 )4.3 join的使用场景场景一基于索引合并python df1 pd.DataFrame({name: [张三, 李四, 王五]}, index[1, 2, 3]) df2 pd.DataFrame({age: [25, 30, 28]}, index[1, 2, 4]) # 基于索引进行左连接 result df1.join(df2, howleft) print(result) # name age # 1 张三 25.0 # 2 李四 30.0 # 3 王五 NaN场景二用on参数基于列连接join也可以基于列进行连接但此时该列会被用作索引python df1 pd.DataFrame({user_id: [1, 2, 3], name: [张三, 李四, 王五]}) df2 pd.DataFrame({user_id: [1, 2, 4], age: [25, 30, 28]}) # 先将df2的user_id设为索引 df2_indexed df2.set_index(user_id) result df1.join(df2_indexed, onuser_id, howleft)场景三同时合并多个DataFramejoin支持一次性合并多个DataFrame这是它的一大优势python df_main pd.DataFrame({id: [1, 2, 3]}, index[1, 2, 3]) df_info1 pd.DataFrame({age: [25, 30, 28]}, index[1, 2, 3]) df_info2 pd.DataFrame({city: [北京, 上海, 广州]}, index[1, 2, 3]) df_info3 pd.DataFrame({score: [85, 92, 78]}, index[1, 2, 3]) # 一次性合并多个 result df_main.join([df_info1, df_info2, df_info3])4.4 merge vs join到底该用哪个维度mergejoin默认对齐方式基于列值基于索引支持多个DataFrame一次只能两个可以同时多个语法复杂度参数较多功能强大简洁直观大数据性能中等略优索引对齐更快适用场景通用场景基于列关联索引对齐场景选择指南如果你的数据已经在索引上对齐或者你想基于索引合并优先选择join如果你需要基于列进行复杂的关联操作或者需要outer/right等连接方式使用merge。五、三剑客终极对比5.1 核心差异一览表对比维度mergeconcatjoin合并依据基于列值键基于位置/轴基于索引连接类型inner/left/right/outer仅沿轴堆叠left/inner默认left同时合并数2个DataFrameN个DataFrameN个DataFrame是否支持SQL风格JOIN完美支持不支持部分支持处理重复列名使用suffixes参数自动处理使用lsuffix/rsuffix典型场景表关联查询数据堆叠拼接索引对齐合并底层逻辑哈希/排序归并内存拼接索引对齐5.2 选择策略速查表你的需求推荐方法说明根据共同字段关联数据merge如订单表与用户表通过user_id关联上下追加相似结构数据concat(axis0)如多个月份的销售记录合并左右拼接宽表concat(axis1)需确保索引对齐基于索引快速合并join如时间序列数据合并需要识别数据来源merge(indicatorTrue) 或 concat(keys)一次性合并多个DataFrameconcat 或 joinmerge一次只能两个大数据集合并百万级以上join基于索引时效率更优实测性能差异可达5倍六、实战案例综合运用三种方法案例一电商数据分析假设我们有三张表用户表usersuser_id, name, register_date订单表ordersorder_id, user_id, order_amount, order_date产品表productsproduct_id, product_name, categorypython import pandas as pd # 构造示例数据 users pd.DataFrame({ user_id: [1, 2, 3, 4], name: [张三, 李四, 王五, 赵六], register_date: pd.date_range(2024-01-01, periods4) }) orders pd.DataFrame({ order_id: [101, 102, 103, 104], user_id: [1, 1, 2, 5], # user_id5的用户不存在 order_amount: [299, 450, 128, 899], order_date: pd.date_range(2024-06-01, periods4) }) # 1. 使用merge将订单与用户信息关联 orders_with_user pd.merge(orders, users, onuser_id, howleft) print(订单关联用户信息) print(orders_with_user[[order_id, user_id, name, order_amount]]) # 2. 使用merge(indicator)找出没有对应用户的订单数据质量检查 merged_with_indicator pd.merge(orders, users, onuser_id, howouter, indicatorTrue) bad_orders merged_with_indicator[merged_with_indicator[_merge] right_only] print(\n用户表中没有对应记录的订单) print(bad_orders) # 3. 使用concat合并多个月份的销售数据 jan_sales pd.DataFrame({product: [A, B], sales: [1000, 800]}) feb_sales pd.DataFrame({product: [A, B, C], sales: [1200, 900, 400]}) mar_sales pd.DataFrame({product: [A, B], sales: [1100, 850]}) all_sales pd.concat([jan_sales, feb_sales, mar_sales], keys[Jan, Feb, Mar], ignore_indexFalse) print(\n全年销售汇总) print(all_sales)案例二多数据源整合python # 场景整合来自不同部门的报表 # 销售部门销售额报表按月份索引 sales_report pd.DataFrame({ sales_amount: [10000, 15000, 12000, 18000] }, index[Jan, Feb, Mar, Apr]) # 市场部门广告费用报表按月份索引 marketing_report pd.DataFrame({ ad_cost: [2000, 2500, 1800, 3000] }, index[Jan, Feb, Mar, Apr]) # 财务部门利润报表索引可能不完全一致 finance_report pd.DataFrame({ profit: [3000, 4500, 3600, 5400] }, index[Jan, Feb, Mar, May]) # 使用join一次性合并所有报表基于索引 full_report sales_report.join([marketing_report, finance_report], howouter) print(整合后的完整报表) print(full_report) # 计算利润率 full_report[profit_margin] full_report[profit] / full_report[sales_amount] * 100 print(\n各月利润率) print(full_report[profit_margin])七、常见坑点与避坑指南坑点1merge时键重复导致笛卡尔积问题当连接键在某一表中存在重复值时merge会生成笛卡尔积导致行数暴增。解决方案合并前检查键的唯一性或使用validate参数进行验证。python # 检查键是否唯一 print(df[key].is_unique) # 应返回True # 使用validate参数限制合并类型 pd.merge(df1, df2, onkey, validateone_to_one) # 确保一对一 pd.merge(df1, df2, onkey, validateone_to_many) # 允许一对多坑点2concat时索引重复问题纵向拼接后索引重复可能导致后续操作出现意外结果。解决方案始终设置ignore_indexTrue或在拼接前重置索引。python # 推荐做法 result pd.concat([df1, df2, df3], ignore_indexTrue)坑点3列名冲突导致数据被覆盖问题merge或join时如果左右表有相同的列名后合并的列会覆盖先前的列。解决方案使用suffixesmerge或lsuffix/rsuffixjoin参数。python # merge中使用suffixes result pd.merge(df1, df2, onkey, suffixes(_left, _right)) # join中使用lsuffix/rsuffix result df1.join(df2, lsuffix_left, rsuffix_right)坑点4concat时列不一致导致大量NaN问题纵向拼接时如果各表的列名不完全一致默认会用NaN填充缺失的列。解决方案根据业务需求选择使用joininner只保留共有列或先统一列结构。python # 只保留所有表共有的列 result pd.concat([df1, df2], joininner)坑点5忽略数据类型不一致导致的合并失败问题连接键的数据类型不一致如一列是int另一列是float或字符串会导致合并结果不符合预期。解决方案合并前确保连接键的数据类型一致。python # 统一数据类型 df1[key] df1[key].astype(str) df2[key] df2[key].astype(str) result pd.merge(df1, df2, onkey)希望这篇文章能帮助到你如果觉得有用欢迎点赞、收藏、关注

更多文章