Pandas merge() 函数详解: 连接你的数据,玩转数据世界的魔术师
Pandas merge() 函数详解: 连接你的数据,玩转数据世界的魔术师
1. 为什么需要 merge()?
2. merge() 函数的基本用法
2.1 举个例子: 简单的 merge()
3. how 参数: 连接方式详解
3.1 inner 连接 (内连接)
3.2 left 连接 (左连接)
3.3 right 连接 (右连接)
3.4 outer 连接 (外连接)
4. on, left_on, right_on 参数: 指定连接键
5. 多列连接
6. 处理重复列名
7. 在不同场景下如何选择连接方式
8. 实战案例: 电商数据分析
9. 总结
10. 进阶技巧: 性能优化
11. 常见问题及解答
Pandas merge() 函数详解: 连接你的数据,玩转数据世界的魔术师
嘿,码农小伙伴们!
在数据分析的浩瀚海洋里,Pandas 是我们手中的利器。今天,咱们就来聊聊 Pandas 里一个超好用的函数—— merge()
,它是我们处理多个数据表时,连接数据的得力助手。就像拼图一样,merge()
可以把分散的数据碎片拼凑成一个完整的数据集,方便我们进行后续的分析和挖掘。
1. 为什么需要 merge()
?
在实际工作中,我们经常会遇到数据分散在多个表格里的情况。比如,用户数据可能在一个表里,订单数据在另一个表里,商品信息又在单独的一个表里。如果想分析用户购买了哪些商品,或者哪些商品卖得最好,就需要把这些表连接起来。merge()
函数就是用来干这个的,它就像一个智能的连接器,根据你指定的条件,把不同的表格合并成一个。
2. merge()
函数的基本用法
merge()
函数的基本语法是这样的:
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, ...)
别被这一长串参数吓到,其实最常用的就那么几个:
left
: 左侧的 DataFrame,也就是你要合并的第一个表格。right
: 右侧的 DataFrame,也就是你要合并的第二个表格。how
: 连接方式,后面会详细介绍。inner
、left
、right
、outer
四种连接方式,控制着如何合并数据。on
: 用于连接的列名,如果两个表里都有相同的列名,就用它。 相当于告诉merge()
,这两个表要根据哪一列来连接。left_on
: 左侧 DataFrame 中用于连接的列名,当左右两个表用于连接的列名不一样的时候,可以使用它。right_on
: 右侧 DataFrame 中用于连接的列名, 当左右两个表用于连接的列名不一样的时候,可以使用它。
2.1 举个例子: 简单的 merge()
假设我们有两个表:用户表
和订单表
。
用户表 (user_df):
用户ID | 用户名 | 城市 |
---|---|---|
1 | 张三 | 北京 |
2 | 李四 | 上海 |
3 | 王五 | 广州 |
订单表 (order_df):
订单ID | 用户ID | 商品名称 | 金额 |
---|---|---|---|
101 | 1 | 苹果 | 10 |
102 | 2 | 香蕉 | 5 |
103 | 1 | 橘子 | 8 |
现在,我们想把用户表
和订单表
合并起来,以便查看每个用户的订单信息。 这两个表都有一个共同的列 用户ID
,我们可以用它来连接这两个表。
import pandas as pd # 创建用户表 user_data = {'用户ID': [1, 2, 3], '用户名': ['张三', '李四', '王五'], '城市': ['北京', '上海', '广州']} user_df = pd.DataFrame(user_data) # 创建订单表 order_data = {'订单ID': [101, 102, 103], '用户ID': [1, 2, 1], '商品名称': ['苹果', '香蕉', '橘子'], '金额': [10, 5, 8]} order_df = pd.DataFrame(order_data) # 使用 merge() 连接两个表 merged_df = pd.merge(order_df, user_df, on='用户ID', how='inner') print(merged_df)
输出结果:
订单ID 用户ID 商品名称 金额 用户名 城市 0 101 1 苹果 10 张三 北京 1 103 1 橘子 8 张三 北京 2 102 2 香蕉 5 李四 上海
看到了吗?merge()
函数根据 用户ID
把两个表连接起来了,现在我们可以看到每个订单对应的用户信息了。
3. how
参数: 连接方式详解
how
参数是 merge()
函数的核心,它决定了如何连接两个表,主要有四种连接方式:inner
、left
、right
、outer
。
3.1 inner
连接 (内连接)
- 定义:
inner
连接是默认的连接方式。 它只保留两个表中,on
列的值都匹配的行。 - 特点: 最终结果是两个表的交集。 如果某个
用户ID
在订单表
里,但不在用户表
里,那么这个订单的信息就会被丢弃。 - 场景: 适用于需要找到两个表共同信息的情况。
例子 (使用上面的 用户表
和 订单表
):
merged_df_inner = pd.merge(order_df, user_df, on='用户ID', how='inner') print(merged_df_inner)
输出结果和前面的例子一样。
3.2 left
连接 (左连接)
- 定义:
left
连接会保留左侧表(left
参数指定的表)的所有行,以及右侧表中与左侧表on
列匹配的行。 如果右侧表没有匹配的行,那么对应的列会用NaN
(缺失值) 填充。 - 特点: 结果至少包含左侧表的所有行。 相当于以左侧表为主,将右侧表的信息合并过来。
- 场景: 适用于需要保留左侧表的所有信息,并尽可能地获取右侧表相关信息的情况。
例子:
假设我们新增一个用户,但这个用户还没有订单:
用户表 (user_df_new):
用户ID | 用户名 | 城市 |
---|---|---|
1 | 张三 | 北京 |
2 | 李四 | 上海 |
3 | 王五 | 广州 |
4 | 赵六 | 深圳 |
import pandas as pd # 创建用户表 user_data = {'用户ID': [1, 2, 3, 4], '用户名': ['张三', '李四', '王五', '赵六'], '城市': ['北京', '上海', '广州', '深圳']} user_df_new = pd.DataFrame(user_data) # 创建订单表 order_data = {'订单ID': [101, 102, 103], '用户ID': [1, 2, 1], '商品名称': ['苹果', '香蕉', '橘子'], '金额': [10, 5, 8]} order_df = pd.DataFrame(order_data) # 使用 left 连接 merged_df_left = pd.merge(order_df, user_df_new, on='用户ID', how='left') print(merged_df_left)
输出结果:
订单ID 用户ID 商品名称 金额 用户名 城市 0 101 1 苹果 10 张三 北京 1 102 2 香蕉 5 李四 上海 2 103 1 橘子 8 张三 北京
即使用户ID
为 4 的赵六在订单表里没有订单信息,使用left
连接,也会保留他的用户信息,而他的订单信息列将会是 NaN
。
3.3 right
连接 (右连接)
- 定义:
right
连接和left
连接相反,它会保留右侧表(right
参数指定的表)的所有行,以及左侧表中与右侧表on
列匹配的行。 如果左侧表没有匹配的行,对应的列会用NaN
填充。 - 特点: 结果至少包含右侧表的所有行。 相当于以右侧表为主,将左侧表的信息合并过来。
- 场景: 适用于需要保留右侧表的所有信息,并尽可能地获取左侧表相关信息的情况。
例子:
还是用上面的用户表
和订单表
,我们反过来连接:
import pandas as pd # 创建用户表 user_data = {'用户ID': [1, 2, 3], '用户名': ['张三', '李四', '王五'], '城市': ['北京', '上海', '广州']} user_df = pd.DataFrame(user_data) # 创建订单表 order_data = {'订单ID': [101, 102, 103, 104], '用户ID': [1, 2, 1, 5], '商品名称': ['苹果', '香蕉', '橘子', '葡萄'], '金额': [10, 5, 8, 12]} order_df_new = pd.DataFrame(order_data) # 使用 right 连接 merged_df_right = pd.merge(user_df, order_df_new, on='用户ID', how='right') print(merged_df_right)
输出结果:
用户ID 用户名 城市 订单ID 商品名称 金额 0 1 张三 北京 101 苹果 10.0 1 2 李四 上海 102 香蕉 5.0 2 1 张三 北京 103 橘子 8.0 3 5 NaN NaN 104 葡萄 12.0
我们看到,订单表
中 用户ID
为 5 的用户,虽然在用户表
中不存在,但是因为使用了 right
连接,所以该信息被保留了下来,用户名和城市显示为 NaN。 这展示了right
连接的特性。
3.4 outer
连接 (外连接)
- 定义:
outer
连接会保留两个表的所有行。 对于无法匹配的行,用NaN
填充缺失的列。 - 特点: 结果是两个表的并集。 包含了两个表的所有信息,如果某些行在另一个表中不存在,那么对应的列会是
NaN
。 - 场景: 适用于需要获取两个表的所有信息,并希望全面了解数据的情况。
例子:
import pandas as pd # 创建用户表 user_data = {'用户ID': [1, 2, 3, 4], '用户名': ['张三', '李四', '王五', '赵六'], '城市': ['北京', '上海', '广州', '深圳']} user_df_new = pd.DataFrame(user_data) # 创建订单表 order_data = {'订单ID': [101, 102, 103, 104], '用户ID': [1, 2, 1, 5], '商品名称': ['苹果', '香蕉', '橘子', '葡萄'], '金额': [10, 5, 8, 12]} order_df_new = pd.DataFrame(order_data) # 使用 outer 连接 merged_df_outer = pd.merge(order_df_new, user_df_new, on='用户ID', how='outer') print(merged_df_outer)
输出结果:
订单ID 用户ID 商品名称 金额 用户名 城市 0 101 1 苹果 10.0 张三 北京 1 103 1 橘子 8.0 张三 北京 2 102 2 香蕉 5.0 李四 上海 3 104 5 葡萄 12.0 NaN NaN 4 NaN 3 NaN NaN 王五 广州 5 NaN 4 NaN NaN 赵六 深圳
可以看到,outer
连接保留了所有行。 订单表里用户ID为5的记录,以及用户表里用户ID为3和4的记录,都有保留,没有匹配到的信息用NaN填充。
4. on
, left_on
, right_on
参数: 指定连接键
on
: 当两个表用于连接的列名完全相同时,使用on
参数指定连接的列名。 这是最简单的情况。merged_df = pd.merge(df1, df2, on='key') # 假设 df1 和 df2 都有名为 'key' 的列
left_on
和right_on
: 当两个表用于连接的列名不一致时,需要使用left_on
和right_on
参数来分别指定左侧和右侧表用于连接的列名。merged_df = pd.merge(df1, df2, left_on='key1', right_on='key2') # df1 的 'key1' 列 和 df2 的 'key2' 列用于连接
例子: 列名不一致的情况
假设 用户表
的列名是 用户ID
,而 订单表
的列名是 客户ID
(为了说明问题,故意不一样):
用户表 (user_df):
用户ID | 用户名 | 城市 |
---|---|---|
1 | 张三 | 北京 |
2 | 李四 | 上海 |
3 | 王五 | 广州 |
订单表 (order_df):
订单ID | 客户ID | 商品名称 | 金额 |
---|---|---|---|
101 | 1 | 苹果 | 10 |
102 | 2 | 香蕉 | 5 |
103 | 1 | 橘子 | 8 |
import pandas as pd # 创建用户表 user_data = {'用户ID': [1, 2, 3], '用户名': ['张三', '李四', '王五'], '城市': ['北京', '上海', '广州']} user_df = pd.DataFrame(user_data) # 创建订单表 order_data = {'订单ID': [101, 102, 103], '客户ID': [1, 2, 1], '商品名称': ['苹果', '香蕉', '橘子'], '金额': [10, 5, 8]} order_df = pd.DataFrame(order_data) # 使用 left_on 和 right_on 连接 merged_df = pd.merge(order_df, user_df, left_on='客户ID', right_on='用户ID', how='inner') print(merged_df)
输出结果:
订单ID 客户ID 商品名称 金额 用户ID 用户名 城市 0 101 1 苹果 10 1 张三 北京 1 103 1 橘子 8 1 张三 北京 2 102 2 香蕉 5 2 李四 上海
在这个例子中,我们使用 left_on='客户ID'
和 right_on='用户ID'
指定了连接的列名,成功地将两个表连接起来了。
5. 多列连接
merge()
函数不仅可以根据一列连接,还可以根据多列进行连接。 只需要在 on
, left_on
或 right_on
参数中传入一个包含多个列名的列表即可。
例子: 根据多列连接
假设我们有两个表,一个表记录了用户的姓名和城市,另一个表记录了用户的姓名和地址邮编。 现在我们希望通过姓名和城市进行连接。
用户表 (user_df):
用户名 | 城市 | 爱好 |
---|---|---|
张三 | 北京 | 篮球 |
李四 | 上海 | 游泳 |
张三 | 广州 | 足球 |
邮编表 (zip_df):
用户名 | 城市 | 邮编 |
---|---|---|
张三 | 北京 | 100000 |
李四 | 上海 | 200000 |
王五 | 广州 | 510000 |
import pandas as pd # 创建用户表 user_data = {'用户名': ['张三', '李四', '张三'], '城市': ['北京', '上海', '广州'], '爱好': ['篮球', '游泳', '足球']} user_df = pd.DataFrame(user_data) # 创建邮编表 zip_data = {'用户名': ['张三', '李四', '王五'], '城市': ['北京', '上海', '广州'], '邮编': [100000, 200000, 510000]} zip_df = pd.DataFrame(zip_data) # 使用多列连接 merged_df = pd.merge(user_df, zip_df, on=['用户名', '城市'], how='left') print(merged_df)
输出结果:
用户名 城市 爱好 邮编 0 张三 北京 篮球 100000.0 1 李四 上海 游泳 200000.0 2 张三 广州 足球 NaN
在这个例子中,我们通过 on=['用户名', '城市']
指定了根据 用户名
和 城市
两列进行连接。 这样,只有当 用户名
和 城市
都匹配的行才会被连接在一起。
6. 处理重复列名
当两个表连接后,如果存在相同的列名,merge()
函数会自动在列名后面添加后缀 _x
和 _y
来区分它们, _x
表示来自左侧表, _y
表示来自右侧表。
例子: 处理重复列名
假设两个表都有 年龄
列,我们连接它们:
左侧表 (left_df):
用户名 | 年龄 |
---|---|
张三 | 25 |
李四 | 30 |
右侧表 (right_df):
用户名 | 年龄 |
---|---|
张三 | 26 |
王五 | 35 |
import pandas as pd # 创建左侧表 left_data = {'用户名': ['张三', '李四'], '年龄': [25, 30]} left_df = pd.DataFrame(left_data) # 创建右侧表 right_data = {'用户名': ['张三', '王五'], '年龄': [26, 35]} right_df = pd.DataFrame(right_data) # 连接两个表 merged_df = pd.merge(left_df, right_df, on='用户名', how='left') print(merged_df)
输出结果:
用户名 年龄_x 年龄_y 0 张三 25 26 1 李四 30 NaN
可以看到, 年龄
列被分成了 年龄_x
和 年龄_y
,分别代表来自左侧表和右侧表的年龄信息。
如果不想使用默认的后缀,可以使用 suffixes
参数自定义后缀。
merged_df = pd.merge(left_df, right_df, on='用户名', how='left', suffixes=('_left', '_right')) print(merged_df)
输出结果:
用户名 年龄_left 年龄_right 0 张三 25 26 1 李四 30 NaN
7. 在不同场景下如何选择连接方式
选择合适的连接方式非常重要,这取决于你的数据分析需求。 下面是一些常见场景和推荐的连接方式:
场景 1: 找出共同的信息
- 需求: 你希望找到两个表中都存在的数据,例如,找出同时购买了 A 商品和 B 商品的用户。
- 推荐:
inner
连接。
场景 2: 以一个表为主,补充另一个表的信息
- 需求: 你有一个用户列表,想知道每个用户对应的订单信息。 即使某些用户没有订单,你也希望保留他们的信息。
- 推荐:
left
连接。 以用户表为左侧表,订单表为右侧表。
场景 3: 以一个表为主,补充另一个表的信息,但希望看到所有信息
- 需求: 你有订单列表,想知道每个订单对应的用户信息。 即使某些订单没有对应的用户,你仍然希望看到这些订单。
- 推荐:
right
连接。 以用户表为右侧表,订单表为左侧表。
场景 4: 获取所有信息,并处理缺失值
- 需求: 你想获取所有用户和所有订单的信息,并处理那些没有匹配到的数据。
- 推荐:
outer
连接。 如果数据量太大,可以考虑其他处理缺失值的方式。
8. 实战案例: 电商数据分析
假设我们是一家电商公司,我们有三个表格: 用户表
,订单表
和 商品表
。
用户表 (users.csv):
用户ID,用户名,城市 1,张三,北京 2,李四,上海 3,王五,广州 4,赵六,深圳
订单表 (orders.csv):
订单ID,用户ID,商品ID,数量,下单时间 101,1,101,2,2023-01-10 102,2,102,1,2023-01-12 103,1,103,3,2023-01-15 104,3,101,1,2023-01-20
商品表 (products.csv):
商品ID,商品名称,价格 101,苹果,10 102,香蕉,5 103,橘子,8 104,葡萄,12
我们的目标是:
- 计算每个用户的总消费额。
- 统计每个商品的销售数量和总销售额。
- 分析每个城市的用户消费情况。
import pandas as pd # 读取数据 users_df = pd.read_csv('users.csv') orders_df = pd.read_csv('orders.csv') products_df = pd.read_csv('products.csv') # 1. 计算每个用户的总消费额 # 1. 将订单表和商品表连接,获取商品价格 order_product_df = pd.merge(orders_df, products_df, on='商品ID', how='left') # 2. 计算每笔订单的消费额 order_product_df['消费额'] = order_product_df['数量'] * order_product_df['价格'] # 3. 将连接后的表和用户表连接 user_order_df = pd.merge(order_product_df, users_df, on='用户ID', how='left') # 4. 计算每个用户的总消费额 user_total_cost = user_order_df.groupby('用户名')['消费额'].sum().reset_index() print('每个用户的总消费额: ', user_total_cost) # 2. 统计每个商品的销售数量和总销售额 # 1. 将订单表和商品表连接 order_product_df = pd.merge(orders_df, products_df, on='商品ID', how='left') # 2. 计算每笔订单的消费额 order_product_df['消费额'] = order_product_df['数量'] * order_product_df['价格'] # 3. 统计每个商品的销售数量和总销售额 product_sales = order_product_df.groupby('商品名称').agg({'数量': 'sum', '消费额': 'sum'}).reset_index() print('\n每个商品的销售数量和总销售额: ', product_sales) # 3. 分析每个城市的用户消费情况 # 1. 将订单表和商品表连接,获取商品价格 order_product_df = pd.merge(orders_df, products_df, on='商品ID', how='left') # 2. 计算每笔订单的消费额 order_product_df['消费额'] = order_product_df['数量'] * order_product_df['价格'] # 3. 将连接后的表和用户表连接 user_order_df = pd.merge(order_product_df, users_df, on='用户ID', how='left') # 4. 统计每个城市的总消费额 city_total_cost = user_order_df.groupby('城市')['消费额'].sum().reset_index() print('\n每个城市的用户消费情况: ', city_total_cost)
在这个案例中,我们使用了多次 merge()
函数,通过 left
连接和 left
连接,把多个表连接起来,获取了我们需要的分析数据。
9. 总结
merge()
函数是 Pandas 中非常重要的一个工具,它能帮助我们高效地连接和处理多个数据表。 通过掌握 how
参数的不同选项,以及 on
, left_on
, right_on
参数的用法,你就能灵活地应对各种数据连接的需求。 记住,多加练习,才能熟练掌握! 希望这篇文章对你有所帮助! 如果你有任何问题,欢迎随时提问! 祝你数据分析之旅愉快!
10. 进阶技巧: 性能优化
当处理大型数据集时,merge()
函数的性能可能会成为瓶颈。 以下是一些优化技巧:
- 选择合适的连接方式: 尽量选择最合适的连接方式,避免不必要的
outer
连接。 - 索引: 如果连接键是 DataFrame 的索引,
merge()
函数的性能会更好。 可以使用set_index()
方法将列设置为索引。 - 数据类型: 确保连接键的数据类型一致。 如果数据类型不一致,
merge()
函数需要进行类型转换,这会降低性能。 - 使用
pd.concat()
: 如果只是简单地将两个 DataFrame 堆叠在一起(例如,增加行),pd.concat()
通常比merge()
更快。 - 避免不必要的连接: 在进行
merge()
之前,尽量过滤掉不需要的行或列,减少数据量。
11. 常见问题及解答
Q: 为什么我的
merge()
连接结果不对?- A: 检查以下几点:
- 确认连接的列名是否正确。
on
,left_on
,right_on
参数是否正确设置。 - 检查连接的列的数据类型是否一致。
- 确认你选择了正确的连接方式 (
inner
,left
,right
,outer
)。 根据你的需求,选择最合适的连接方式。 - 检查是否有重复的列名。 如果有,使用
suffixes
参数自定义后缀,或者在连接之前重命名列名。
- 确认连接的列名是否正确。
- A: 检查以下几点:
Q: 我的
merge()
运行速度很慢,怎么办?- A: 参考 10. 进阶技巧:性能优化 中的建议。 尝试优化你的代码,例如,使用索引、调整数据类型、选择更合适的连接方式等。
Q: 我可以使用
merge()
连接超过两个表吗?- A: 可以。 你可以使用链式
merge()
, 例如:merged_df = pd.merge(pd.merge(df1, df2, ...), df3, ...)
, 或者使用循环。 但是,当表很多时,代码可能会变得复杂,建议考虑使用其他工具,比如数据库。
- A: 可以。 你可以使用链式
希望这些能帮助你更好地理解和使用 merge()
函数! 祝你编码愉快!