PandasDataFrame键值匹配与拆分教程
时间:2025-11-04 19:06:36 278浏览 收藏
本文详细介绍了如何利用Pandas DataFrame进行键匹配与数值拆分,旨在解决数据处理中常见的数值分配问题。通过`value_counts()`函数计算键的重复频率,再使用`div()`函数标准化数值,最后通过`merge()`操作将拆分后的数值精确地分配到另一个DataFrame的对应行中。本教程提供示例代码和详细解析,展示了如何根据一个DataFrame中键的频率,将另一个DataFrame中的数值进行拆分,并确保最终结果的索引与原始DataFrame一致。掌握此方法,能有效提升数据处理效率,适用于需要数据聚合、拆分和重构的场景。

1. 问题背景与目标
在数据处理中,我们经常会遇到需要将汇总数据(例如总销售额、总库存量)按某种比例或规则分配到其组成部分(例如单个销售记录、单个库存单位)的场景。本教程聚焦于一个具体问题:给定两个Pandas DataFrame,df1包含重复的键(例如产品ID),df2包含每个唯一键对应的总数值。我们的目标是创建一个新的DataFrame,其结构与df1相似,但df2中的数值已经被“拆分”并按键的出现频率分配到df1的对应行中。
例如,假设我们有以下两个DataFrame:
DataFrame 1 (df1): 包含重复的 id
| id |
|---|
| A |
| B |
| A |
| C |
| A |
| A |
| C |
DataFrame 2 (df2): 包含每个唯一 id 对应的总数值
| id | Col1 | Col2 | Col3 |
|---|---|---|---|
| A | 400 | 100 | 20 |
| B | 200 | 800 | |
| C | 600 | 800 |
期望的输出结果:
| id | Col1 | Col2 | Col3 |
|---|---|---|---|
| A | 100 | 25 | 5 |
| B | 200 | 800 | |
| A | 100 | 25 | 5 |
| C | 300 | 400 | |
| A | 100 | 25 | 5 |
| A | 100 | 25 | 5 |
| C | 300 | 400 |
从期望结果可以看出,id为'A'的记录在df1中出现了4次,因此df2中'A'对应的Col1 (400) 被拆分为 400/4 = 100,Col2 (100) 被拆分为 100/4 = 25,Col3 (20) 被拆分为 20/4 = 5。同样,'C'出现了2次,其数值被拆分为一半。而'B'只出现1次,其数值保持不变。
2. 核心思路与实现步骤
要实现上述目标,我们需要执行以下几个关键步骤:
- 计算键频率: 统计df1中每个id出现的次数。
- 标准化 df2: 将df2中每个id对应的数值除以其在df1中的出现频率。
- 合并数据: 将标准化后的df2与原始df1进行合并。
- 恢复索引: 确保最终输出的DataFrame具有与原始df1相同的索引结构。
我们将使用Pandas库中的value_counts()、div()和merge()函数来高效完成这些操作。
3. 示例代码与详细解析
首先,我们创建示例数据:
import pandas as pd
import numpy as np
# 创建 DataFrame 1
data1 = {'id': ['A', 'B', 'A', 'C', 'A', 'A', 'C']}
df1 = pd.DataFrame(data1)
# 创建 DataFrame 2
data2 = {'id': ['A', 'B', 'C'],
'Col1': [400, 200, 600],
'Col2': [100, np.nan, 800],
'Col3': [20, 800, np.nan]}
df2 = pd.DataFrame(data2)
print("原始 df1:")
print(df1)
print("\n原始 df2:")
print(df2)原始 df1:
id 0 A 1 B 2 A 3 C 4 A 5 A 6 C
原始 df2:
id Col1 Col2 Col3 0 A 400 100.0 20.0 1 B 200 NaN 800.0 2 C 600 800.0 NaN
现在,执行核心逻辑:
# 1. 计算 df1 中 'id' 列的频率
id_counts = df1['id'].value_counts()
print("\nid 频率:")
print(id_counts)
# 2. 标准化 df2: 将 df2 中的数值除以对应的 id 频率
# - set_index('id') 将 'id' 设置为索引,以便与 id_counts 对齐
# - div(id_counts, axis=0) 对齐索引并执行逐行除法
df2_standardized = df2.set_index('id').div(id_counts, axis=0)
print("\n标准化后的 df2:")
print(df2_standardized)
# 3. 合并数据
# - df1.reset_index() 暂时将 df1 的原始索引保存为一列,以便后续恢复
# - merge() 根据 'id' 列进行左连接 (how='left')
# - set_index('index').reindex(df1.index) 恢复原始索引和行顺序
out = (df1.reset_index()
.merge(df2_standardized, on='id', how='left')
.set_index('index').reindex(df1.index)
)
print("\n最终输出:")
print(out)id 频率:
A 4 C 2 B 1 Name: id, dtype: int64
标准化后的 df2:
Col1 Col2 Col3 id A 100.0 25.0 5.0 B 200.0 NaN 800.0 C 300.0 400.0 NaN
最终输出:
id Col1 Col2 Col3 0 A 100.0 25.0 5.0 1 B 200.0 NaN 800.0 2 A 100.0 25.0 5.0 3 C 300.0 400.0 NaN 4 A 100.0 25.0 5.0 5 A 100.0 25.0 5.0 6 C 300.0 400.0 NaN
代码解析:
id_counts = df1['id'].value_counts():
- 这一步计算了df1中'id'列每个唯一值的出现频率。例如,'A'出现4次,'B'出现1次,'C'出现2次。结果是一个Pandas Series,索引是id值,值是频率。
df2_standardized = df2.set_index('id').div(id_counts, axis=0):
- df2.set_index('id'): 将df2的'id'列设置为其索引。这是为了让df2的行索引与id_counts的索引(即id值)对齐,以便进行正确的逐行除法。
- .div(id_counts, axis=0): 对df2中除id列以外的所有数值列执行除法操作。axis=0表示按行进行操作,Pandas会自动根据索引(即id值)将df2的每一行与id_counts中对应的频率值进行匹配并相除。如果df2中的某个单元格为NaN,除法操作会保留NaN。
out = (df1.reset_index().merge(df2_standardized, on='id', how='left').set_index('index').reindex(df1.index)):
- df1.reset_index(): 在合并之前,df1的原始整数索引(0, 1, 2...)很重要,因为我们希望最终输出的DataFrame具有与df1相同的行顺序和索引。reset_index()将当前索引转换为一个名为'index'的普通列,并生成一个新的默认整数索引。
- .merge(df2_standardized, on='id', how='left'):
- 将df1(现在包含原始索引作为'index'列)与df2_standardized进行合并。
- on='id' 指定了合并的键是'id'列。
- how='left' 执行左连接。这意味着df1中的所有行都会被保留,如果df1中的某个id在df2_standardized中没有匹配项(虽然在这个特定问题中不太可能),则对应的Col1, Col2, Col3会填充NaN。
- .set_index('index'): 合并完成后,我们将之前保存的'index'列重新设置回DataFrame的索引。
- .reindex(df1.index): 这一步是可选但推荐的,它确保最终DataFrame的行顺序和索引类型与原始df1完全一致。reindex会根据df1.index的顺序重新排列行,如果原始索引中有重复值,也会正确处理。
4. 注意事项与最佳实践
- 数据类型: 除法操作可能会导致数值列的数据类型从整数变为浮点数(例如int变为float),这是正常的。如果需要,可以使用astype()进行类型转换,但要注意NaN值可能导致无法转换为纯整数。
- NaN 值处理: df2中的NaN值在除法和合并过程中会保持为NaN。如果需要,可以在合并前后使用fillna()进行填充。
- 性能: 对于非常大的DataFrame,merge操作可能会比较耗时。然而,Pandas的底层实现通常是高度优化的。此方法在大多数情况下都是高效且简洁的。
- 键的唯一性: 确保df2中的'id'列是唯一的,否则set_index('id')可能会引发错误或产生非预期的行为。如果df2中id不唯一,需要先对其进行聚合处理。
- 索引管理: reset_index()和set_index().reindex()的组合是确保最终输出的索引和行顺序与原始df1保持一致的常用且稳健的方法。
5. 总结
本教程提供了一种高效且易于理解的Pandas解决方案,用于根据键的出现频率将一个DataFrame的数值拆分并分配到另一个DataFrame的对应行中。通过结合value_counts()计算频率、div()进行标准化以及merge()进行数据整合,我们能够精确地实现复杂的数值分配逻辑。掌握这种模式对于处理涉及数据聚合、拆分和重构的场景非常有用。
终于介绍完啦!小伙伴们,这篇关于《PandasDataFrame键值匹配与拆分教程》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
296 收藏
-
351 收藏
-
157 收藏
-
485 收藏
-
283 收藏
-
349 收藏
-
291 收藏
-
204 收藏
-
401 收藏
-
227 收藏
-
400 收藏
-
327 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习