【精选优质专栏推荐】


每个专栏均配有案例与图文讲解,循序渐进,适合新手与进阶学习者,欢迎订阅。

在这里插入图片描述

简介

数据合并是将来自不同来源的数据组合为统一数据集的过程。在许多数据科学工作流程中,当相关信息分散在多个表或文件中时——例如,银行客户档案及其交易历史——数据合并成为获取更深层次洞察和推动有影响力分析的必要步骤。然而,高效执行数据合并过程可能非常困难,原因包括数据不一致、数据格式异构,或者仅仅因为数据集规模庞大。

本文将揭示七个实用的 Pandas 技巧,以加快数据合并的过程,使你能够将更多精力集中在数据科学和机器学习工作流程的其他关键阶段。由于 Pandas 库在以下代码示例中扮演重要角色,请务必首先 import pandas as pd。

1. 使用 merge() 进行安全的一对一合并

使用 Pandas 的 merge() 函数合并具有共同关键属性或标识符的两个数据集时,通过设置 validate='one_to_one' 参数可以提高效率和稳健性。它确保合并键在两个数据框中都是唯一的,并捕获可能的重复错误,防止错误传递到后续的数据分析阶段。

import pandas as pd

# 创建左侧数据框
left  = pd.DataFrame({'id':[1,2,3], 'name':['Ana','Bo','Cy']})
# 创建右侧数据框
right = pd.DataFrame({'id':[1,2,3], 'spent':[10,20,30]})

# 使用 merge 进行合并,按 'id' 列对齐,验证是一对一关系
merged = pd.merge(left, right, on='id', how='left', validate='one_to_one')
# merged 结果包含左表所有行,同时添加右表对应 'spent' 列

示例创建了两个小型数据框,你也可以用自己的 “left” 和 “right” 数据框尝试,只要它们有共同的合并键(本例中是 'id' 列)。

尝试不同的 how 连接方式,如 rightouterinner,以及替换其中一个数据框的 id 值,观察合并结果的变化。

2. 使用 DataFrame.join() 的基于索引的合并

将数据框中共享的合并键设置为索引,有助于提高合并速度,尤其是涉及多次合并时。示例如下,将合并键设置为索引后,使用数据框的 join() 方法进行合并。

# 创建用户数据框,并将 'user_id' 设置为索引
users  = pd.DataFrame({'user_id':[101,102,103], 'name':['Ada','Ben','Cal']}).set_index('user_id')
# 创建成绩数据框,并将 'user_id' 设置为索引
scores = pd.DataFrame({'user_id':[101,103], 'score':[88,91]}).set_index('user_id')

# 使用 join 方法按索引进行合并
joined = users.join(scores, how='left')
# joined 结果保留 users 所有行,并添加 scores 中对应 'score' 列

3. 使用 merge_asof() 进行时间感知合并

在高粒度时间序列数据中,如购物订单及其关联票据,精确的时间戳可能并不总是匹配。因此,与其寻找精确匹配的合并键(即时间),不如采用最近键方法。可以使用 merge_asof() 高效实现。

# 创建票据时间和价格数据框
tickets = pd.DataFrame({'t':[1,3,7], 'price':[100,102,101]})
# 创建订单时间和数量数据框
orders = pd.DataFrame({'t':[2,4,6], 'qty':[5,2,8]})

# 按时间进行最近匹配合并(backward 表示使用之前最近的时间点)
asof_merged = pd.merge_asof(orders.sort_values('t'), tickets.sort_values('t'), on='t', direction='backward')

4. 使用 Series.map() 进行快速查找

当需要从查找表中添加单列(如 Pandas Series 映射产品 ID 到名称)时,map() 方法是比完整 join 更快、更简洁的替代方案。

# 创建订单数据框
orders = pd.DataFrame({'product_id':[2001,2002,2001,2003]})
# 创建产品 ID 与名称映射
product_lookup = pd.Series({2001:'Laptop', 2002:'Headphones', 2003:'Monitor'})

# 使用 map 方法快速添加产品名称列
orders['product_name'] = orders['product_id'].map(product_lookup)

5. 使用 drop_duplicates() 防止非预期合并

如果忽略可能的重复键(有时是无意产生的),往往会发生非预期的多对多合并。在合并前仔细分析数据并确保删除可能的重复项,可以防止处理大型数据集时行数爆炸和内存激增。

# 创建订单数据框(可能有重复 id)
orders = pd.DataFrame({'id':[1,1,2], 'item':['apple','banana','cherry']})
# 创建客户数据框(可能有重复 id)
customers = pd.DataFrame({'id':[1,2,2], 'name':['Alice','Bob','Bob-dupli']})

# 删除重复的客户 id
customers = customers.drop_duplicates(subset='id')

# 使用 merge 进行合并,并验证多对一关系
merged = pd.merge(orders, customers, on='id', how='left', validate='many_to_one')

6. 使用 CategoricalDtype 快速匹配键

另一种减少内存峰值并加速合并时比较的方法是,将合并键转换为分类变量(CategoricalDtype)。如果数据集中有大量重复字符串的键(如字母数字客户代码),在合并前应用此技巧能显著提高性能。

# 创建左表和右表数据框
left  = pd.DataFrame({'k':['a','b','c','a']})
right = pd.DataFrame({'k':['a','b'], 'v':[1,2]})

# 将合并键转换为分类类型
cat = pd.api.types.CategoricalDtype(categories=right['k'].unique())
left['k']  = left['k'].astype(cat)
right['k'] = right['k'].astype(cat)

# 使用 merge 进行合并
merged = pd.merge(left, right, on='k', how='left')

7. 使用 loc[] 投影裁剪合并负载

比听起来简单得多。此技巧适用于特征较多的数据集,通过在合并前选择必要列,可减少数据搬运、比较和内存存储,从而提高效率。

# 创建销售数据框
sales = pd.DataFrame({
    'order_id':[101,102,103],
    'customer_id':[1,2,3],
    'amount':[250,120,320],
    'discount_code':['SPRING','NONE','NONE']
})

# 创建客户数据框
customers = pd.DataFrame({
    'customer_id':[1,2,3],
    'region':['EU','US','APAC'],
    'notes':['VIP','Late payer','New customer']
})

# 只选择合并所需的列,减少内存和计算量
customers_selected = customers.loc[:, ['customer_id','region']]
sales_selected = sales.loc[:, ['order_id','customer_id','amount']]

# 按 'customer_id' 列合并
merged = pd.merge(sales_selected, customers_selected, on='customer_id', how='left')

总结

通过将本文介绍的七个 Pandas 技巧应用到大型数据集上可以显著提高数据合并的效率。

以下为文章内容总结:

技巧 作用
pd.merge() 对一对一键进行验证,防止多对多爆炸,节省时间和内存。
DataFrame.join() 基于索引的直接合并减少键对齐开销,简化多次合并链。
pd.merge_asof() 对时间序列数据进行排序的最近键合并,无需繁琐的重采样。
Series.map() 基于查找的键值增强比完整 DataFrame 合并更快。
DataFrame.drop_duplicates() 删除重复键,防止多对多爆炸和不必要的处理。
CategoricalDtype 将复杂字符串键转换为分类类型,节省内存并加快相等比较。
DataFrame.loc[] 在合并前只选择所需列。

更多推荐