登录
首页 >  文章 >  python教程

Pandas合并两个DataFrame的几种方法

时间:2025-09-14 16:03:38 457浏览 收藏

在Python数据分析中,`pandas`库的`pd.merge()`函数是合并DataFrame的强大工具,它如同SQL中的JOIN操作,基于共同列或索引实现数据的内、左、右、外连接。通过`on`参数指定连接键,支持单列或多列匹配,`left_on`和`right_on`处理列名不同的情况,`suffixes`自定义重复列名后缀。`pd.concat()`则擅长沿轴堆叠结构相似的数据。索引合并需设置`left_index`和`right_index`,冲突时可用`reset_index`或`ignore_index`解决。本文将深入探讨`pd.merge()`的用法,多列匹配场景,以及`pd.concat()`与`pd.merge()`的本质区别,并提供有效处理重复列名和索引冲突的实用技巧,助力读者高效完成数据整合任务。

最核心的合并方法是pd.merge(),它基于共同列或索引进行内、左、右、外连接;on参数指定连接键,支持单列或多列匹配;当列名不同时可用left_on和right_on;重复列名通过suffixes自定义后缀区分;pd.concat()用于沿轴堆叠数据,适合结构相似的数据拼接;基于索引合并需设置left_index和right_index,索引冲突可通过reset_index或ignore_index处理。

python中pandas怎么合并两个DataFrame?

在Python中,合并两个pandas DataFrame最常用的方法是使用pd.merge()函数,它能够根据一个或多个共同的键(列或索引)将两个DataFrame连接起来,就像SQL中的JOIN操作一样。此外,pd.concat()用于沿着某个轴堆叠或拼接DataFrame,而df.join()则是pd.merge()的一种简化形式,主要用于基于索引的连接。

解决方案

要合并两个DataFrame,最核心且功能最强大的工具就是pd.merge()。它允许我们以多种方式(内连接、左连接、右连接、外连接)将数据基于共同的列或索引进行整合。

假设我们有两个DataFrame,df1df2

import pandas as pd

df1 = pd.DataFrame({
    'ID': [1, 2, 3, 4],
    'Name': ['Alice', 'Bob', 'Charlie', 'David'],
    'City': ['New York', 'London', 'Paris', 'Tokyo']
})

df2 = pd.DataFrame({
    'ID': [1, 2, 5, 6],
    'Age': [25, 30, 22, 35],
    'Occupation': ['Engineer', 'Doctor', 'Artist', 'Teacher']
})

# 默认情况下,pd.merge()会尝试找到两个DataFrame中共同的列名作为键进行内连接
# 比如这里,'ID'是共同的列
merged_df_inner = pd.merge(df1, df2, on='ID', how='inner')
print("内连接结果 (inner join):\n", merged_df_inner)

# 左连接 (left join): 保留左边DataFrame的所有行,匹配右边DataFrame的数据
merged_df_left = pd.merge(df1, df2, on='ID', how='left')
print("\n左连接结果 (left join):\n", merged_df_left)

# 右连接 (right join): 保留右边DataFrame的所有行,匹配左边DataFrame的数据
merged_df_right = pd.merge(df1, df2, on='ID', how='right')
print("\n右连接结果 (right join):\n", merged_df_right)

# 外连接 (outer join): 保留所有行的信息,无论是否匹配
merged_df_outer = pd.merge(df1, df2, on='ID', how='outer')
print("\n外连接结果 (outer join):\n", merged_df_outer)

pd.merge()的关键参数包括:

  • left, right: 要合并的两个DataFrame。
  • on: 用于连接的列名(或列名列表),这些列名必须在两个DataFrame中都存在。
  • left_on, right_on: 当左右DataFrame的连接列名不同时使用。
  • how: 连接类型,可以是'inner' (默认), 'left', 'right', 'outer'
  • suffixes: 当合并后出现重复列名时,用于区分左右DataFrame的后缀。
  • left_index, right_index: 如果想基于索引进行连接,可以将它们设置为True

pd.merge()on参数到底怎么用?处理多列匹配的复杂场景?

pd.merge()中的on参数是指定连接键的核心。它既可以接受单个列名,也可以接受一个列名列表,这在处理需要多条件匹配的复杂数据时尤其有用。我个人觉得,理解on参数的灵活性是掌握pd.merge()的关键一步。

on参数是一个字符串时,比如on='ID',这意味着两个DataFrame都会使用名为'ID'的列作为连接键。如果两个DataFrame中连接的列名不一样,例如df1中有'CustomerID',而df2中有'ClientID',但它们代表的是同一个实体,这时就不能直接用on了。我们需要分别指定left_on='CustomerID'right_on='ClientID'

来看一个多列匹配的例子,这在处理复合主键或者需要更精确匹配的场景下非常常见。比如,你可能有一个销售订单表,需要通过'订单号''商品ID'的组合来匹配商品详情。

import pandas as pd

orders = pd.DataFrame({
    'OrderID': ['A001', 'A001', 'A002', 'A003'],
    'ProductID': [101, 102, 101, 103],
    'Quantity': [1, 2, 1, 3]
})

product_details = pd.DataFrame({
    'ProductID': [101, 102, 103, 104],
    'ProductName': ['Laptop', 'Mouse', 'Keyboard', 'Monitor'],
    'Price': [1200, 25, 75, 300]
})

# 如果我们想把订单信息和商品详情合并,基于 ProductID 就可以
merged_by_product = pd.merge(orders, product_details, on='ProductID', how='left')
print("基于ProductID的合并:\n", merged_by_product)

# 假设我们还有一个订单状态表,需要根据 OrderID 和 ProductID 来更新特定商品的订单状态
order_status = pd.DataFrame({
    'OrderID': ['A001', 'A001', 'A002'],
    'ProductID': [101, 102, 101],
    'Status': ['Shipped', 'Processing', 'Delivered']
})

# 这时候就需要多列匹配了,使用 on=['OrderID', 'ProductID']
final_merged_df = pd.merge(merged_by_product, order_status, on=['OrderID', 'ProductID'], how='left')
print("\n基于多列匹配的最终合并:\n", final_merged_df)

这里,on=['OrderID', 'ProductID']告诉pd.merge(),只有当两个DataFrame的OrderIDProductID都完全一致时,才认为它们是匹配的。这种精确匹配避免了只凭单一列可能导致的错误关联。我在实际工作中,遇到过很多数据清洗的场景,往往就需要这种多列匹配来确保数据合并的准确性,不然结果可能就南辕北辙了。

pd.concat()pd.merge()有什么本质区别?什么时候该用哪个?

这确实是初学者,甚至是一些有经验的用户也容易混淆的地方。我常看到有人试图用pd.merge()去堆叠数据,或者用pd.concat()去连接数据,结果可想而知,要么报错,要么得到一堆NaN。它们的核心设计目的完全不同。

pd.merge()的核心是连接(Joining)。它基于一个或多个键(列或索引)来查找两个DataFrame中匹配的行,然后将这些行横向(按列)拼接起来。可以把它想象成数据库中的JOIN操作,它关注的是“哪些行是相关的?”以及“如何把相关行的信息放到一起?”。

pd.concat()的核心是堆叠(Concatenating)拼接(Appending)。它将多个DataFrame沿着某个轴(行或列)直接堆叠起来,就像乐高积木一样。它不关心数据内容是否匹配,只关心形状是否能对齐。它关注的是“如何把多个数据块简单地堆起来?”。

什么时候用哪个?

  • 使用pd.merge()的场景:

    • 当你需要根据共同的ID、日期、名称等键将两个来源不同的数据集关联起来时。
    • 当你需要查找一个DataFrame中哪些记录在另一个DataFrame中存在或缺失时(通过不同的how参数)。
    • 例如,将客户信息表与订单表根据CustomerID进行关联。
  • 使用pd.concat()的场景:

    • 当你有一些相同结构(或类似结构)的数据,想把它们按行堆叠起来,形成一个更大的数据集时。比如,你每天都有一个CSV文件记录当天的销售数据,你想把一个月的销售数据都放到一个DataFrame里进行分析。
    • 当你有一些具有相同索引但列不同的DataFrame,想把它们按列拼接起来时。比如,你有一个DataFrame记录了用户的基本信息,另一个DataFrame记录了用户的联系方式,它们都以UserID作为索引,你想把它们横向拼接。
# pd.concat() 示例:按行堆叠
df_q1 = pd.DataFrame({'Month': ['Jan', 'Feb'], 'Sales': [100, 120]})
df_q2 = pd.DataFrame({'Month': ['Mar', 'Apr'], 'Sales': [150, 110]})

all_sales = pd.concat([df_q1, df_q2], ignore_index=True)
print("按行堆叠 (pd.concat):\n", all_sales)

# pd.concat() 示例:按列拼接 (需要索引对齐)
df_info = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Age': [25, 30]}, index=[1, 2])
df_contact = pd.DataFrame({'Email': ['a@example.com', 'b@example.com'], 'Phone': ['111', '222']}, index=[1, 2])

combined_info = pd.concat([df_info, df_contact], axis=1)
print("\n按列拼接 (pd.concat, axis=1):\n", combined_info)

可以看到,pd.concat()在按行堆叠时,默认会保留原始索引,如果你不希望这样,需要设置ignore_index=True。而按列拼接时,它会尝试根据索引对齐。这种行为与pd.merge()基于列值进行匹配的逻辑截然不同。所以,在选择函数之前,先问问自己:我是想“关联”数据,还是想“堆叠”数据?

合并DataFrame时,如何有效处理重复列名和索引冲突?

在实际数据处理中,合并DataFrame常常会遇到列名冲突和索引冲突的问题。如果不妥善处理,轻则数据混乱,重则结果错误。我个人觉得,这些细节的处理能力,很大程度上决定了数据分析的可靠性。

1. 处理重复列名:suffixes参数

当两个DataFrame有共同的非连接列名时,pd.merge()默认会在这些重复的列名后面加上_x_y后缀。但很多时候,这种默认的后缀并不够清晰,或者与你的命名规范不符。这时,suffixes参数就派上用场了。它接受一个包含两个字符串的元组,分别作为左右DataFrame重复列名的后缀。

import pandas as pd

df_user_info = pd.DataFrame({
    'ID': [1, 2, 3],
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Score': [85, 90, 78]
})

df_exam_results = pd.DataFrame({
    'ID': [1, 2, 4],
    'Score': [92, 88, 95], # 注意这里也有一个 'Score' 列
    'Grade': ['A', 'B', 'A']
})

# 不指定 suffixes,默认会是 _x 和 _y
merged_default_suffix = pd.merge(df_user_info, df_exam_results, on='ID', how='left')
print("默认后缀处理重复列名:\n", merged_default_suffix)

# 使用自定义 suffixes
merged_custom_suffix = pd.merge(
    df_user_info, df_exam_results, on='ID', how='left',
    suffixes=('_userInfo', '_examResult')
)
print("\n自定义后缀处理重复列名:\n", merged_custom_suffix)

通过suffixes=('_userInfo', '_examResult'),我们可以清晰地知道Score_userInfo是来自用户信息表的,而Score_examResult是来自考试结果表的。这种命名方式能极大提高代码的可读性和数据的可追溯性。

2. 处理索引冲突/基于索引合并:left_index, right_indexreset_index()

  • 基于索引合并: 如果你的DataFrame的索引本身就是你想要用来连接的键,那么可以使用left_index=Trueright_index=True参数。

    df_a = pd.DataFrame({'ValueA': [10, 20]}, index=['K1', 'K2'])
    df_b = pd.DataFrame({'ValueB': [30, 40]}, index=['K1', 'K3'])
    
    merged_by_index = pd.merge(df_a, df_b, left_index=True, right_index=True, how='outer')
    print("基于索引的合并:\n", merged_by_index)
  • 索引冲突和重置:

    • 当使用pd.concat()按行堆叠时,如果原始DataFrame的索引不是唯一的,或者你不想保留它们,ignore_index=True会非常有用。它会生成一个全新的、从0开始的整数索引。
    • 在使用pd.merge()时,如果你的连接键是某个列,但你又担心原始索引会干扰后续操作,或者想把索引也作为普通列进行合并,可以先用df.reset_index()将索引转换为普通列,然后再进行合并。
    # 假设df1的索引有意义,但我们想把它作为列参与合并
    df1_with_index_as_col = df1.reset_index().rename(columns={'index': 'OriginalIndex'})
    # 然后再与df2合并,如果需要的话
    # pd.merge(df1_with_index_as_col, df2, on='ID')

    处理索引问题时,我经常会先停下来思考:这个索引对我来说是数据的一部分(需要参与合并或保留),还是仅仅一个行标识(可以忽略或重置)?这决定了我应该使用left_index/right_index,还是reset_index(),或者干脆ignore_index。忽略这些看似细微的索引问题,可能会在后续的数据聚合或分析中埋下隐患,导致意想不到的错误结果。

今天带大家了解了的相关知识,希望对你有所帮助;关于文章的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~

相关阅读
更多>
最新阅读
更多>
课程推荐
更多>