Python实战案例:用Pandas高效实现数据分组导入与批量处理

文章目录导读
-
为什么需要数据分组导入?
- 真实业务场景痛点分析
- 分组导入 vs 逐条导入的性能对比
-
Pandas数据分组核心功能速览
groupby()本质与语法- 分组聚合函数(
sum、mean、agg) - 分组后导出为独立文件或DataFrame列表
-
按销售区域分组导入CSV文件
- 从Excel按城市拆分数据
- 生成区域维度报表
-
时间序列分组与批量Excel输出
- 按月份分组处理日志数据
- 使用
pd.ExcelWriter实现多Sheet导出
-
大型数据分块读取+分组处理
- 使用
chunksize避免内存溢出 - 分组后合并中间结果
- 使用
-
常见问题与面试问答(Q&A)
- Q1:分组后如何保留原始行号?
- Q2:如何对分组后的数据做条件过滤?
-
分组导入的最佳实践建议
为什么需要数据分组导入?
在实际数据分析工作中,我们经常遇到这样的场景:一个包含全国销售记录的5GB Excel文件,需要按省份生成独立报表;或者每天产生的用户行为日志,需要按月统计并保存为不同的CSV,如果手动筛选复制,不仅耗时且极易出错。Pandas的groupby()功能配合文件I/O操作,可以自动完成“按条件拆分-处理-导出”的全流程,将批量处理时间从小时级缩短到秒级。
相比传统的逐条循环读取(iterrows()),分组导入有以下优势:
- 内存友好:直接对分组结果操作,避免重复加载全量数据
- 代码简洁:三行代码即可完成按类别拆分与聚合
- 可追溯性:分组后的索引保留原始数据标签
Pandas数据分组核心功能速览
1 groupby() 本质与语法
groupby()是Pandas中实现“拆分-应用-组合”的利器,其基础用法为:
grouped = df.groupby('column_name')
grouped是一个DataFrameGroupBy对象,它本身不进行计算,只保存分组规则,常见的后续操作包括:
- 聚合:
.sum(),.mean(),.count() - 转换:
.transform()保留原形状 - 过滤:
.filter()剔除不符合条件的分组
2 分组后导出为独立文件
核心技巧是利用groupby遍历每个分组的键和对应的子DataFrame:
for key, group in df.groupby('region'):
group.to_csv(f'output/{key}.csv', index=False)
案例一:按销售区域分组导入CSV文件
目标:从sales_2024.csv(包含字段:日期、城市、销售额、负责人)中,自动按城市生成若干子CSV文件,并附带每个城市的销售额汇总。
实现步骤:
import pandas as pd
# 读取源数据
df = pd.read_csv('sales_2024.csv')
# 按城市分组,并为每组生成独立文件
for city, group in df.groupby('城市'):
# 保存原始数据
group.to_csv(f'reports/{city}_detail.csv', index=False)
# 同时生成该城市的聚合摘要
summary = group.groupby('负责人')['销售额'].sum().reset_index()
summary.to_csv(f'reports/{city}_summary.csv', index=False)
print('分组导入完成,共生成', df['城市'].nunique(), '个城市报表')
效果说明:
- 若数据包含50个城市,则自动生成100个文件(50个明细 + 50个聚合)
- 文件名使用城市名称,避免中文乱码问题(实际建议用英文或拼音)
案例二:时间序列分组与批量Excel输出
场景:某日志数据包含全年每天的用户活跃数(字段:日期、用户ID、活跃时长),需要按月份生成一个Excel文件,其中每个月份作为一个独立Sheet。
实现代码(带优化技巧):
import pandas as pd
from datetime import datetime
# 读取数据,确保日期列为datetime类型
df = pd.read_csv('user_log.csv', parse_dates=['日期'])
# 提取月份列
df['月份'] = df['日期'].dt.to_period('M')
# 使用ExcelWriter写入多Sheet
with pd.ExcelWriter('monthly_report.xlsx', engine='openpyxl') as writer:
for month, group in df.groupby('月份'):
# 将月份转换为字符串作为Sheet名称(Excel限制31字符)
sheet_name = str(month)[:10]
# 计算该月日均活跃时长(演示聚合)
summary = group.groupby('用户ID')['活跃时长'].mean().reset_index()
summary.to_excel(writer, sheet_name=sheet_name, index=False)
print('已将全年数据按月份分组写入', len(df['月份'].unique()), '个Sheet')
关键点:
- 使用
dt.to_period('M')快速提取年月 ExcelWriter的with语句确保文件正确关闭- Sheet名称需避开Excel特殊字符(等)
案例三:大型数据分块读取+分组处理
问题:当源文件超过5GB时,直接pd.read_csv()可能导致内存溢出,解决方案是分块读取,每处理一个块即进行分组聚合,最后合并结果。
实现方案(增量式分组聚合):
import pandas as pd
chunk_size = 50000 # 每批5万行
final_result = []
for chunk in pd.read_csv('large_sales.csv', chunksize=chunk_size):
# 对当前块按产品类别分组求和
chunk_grouped = chunk.groupby('产品类别')['销售额'].sum()
final_result.append(chunk_grouped)
# 合并所有分块的分组结果
final_series = pd.concat(final_result).groupby(level=0).sum()
final_series.to_csv('category_total.csv')
print('大型文件分组汇总完成,总行数约', chunk_size * len(final_result))
原理:
chunksize返回一个迭代器,每次读取指定行数- 每个切片独立
groupby,结果用groupby(level=0).sum()二次聚合 - 该模式适用于所有可累加的聚合函数(
sum,count,min,max)
常见问题与面试问答(Q&A)
Q1:分组后如何保留原始数据的行号?
如果需要在分组导出时保留原始行号(不重置索引),使用groupby内循环时直接用group即可,它包含原始行的索引。
for _, group in df.groupby('类别'):
group.to_csv(...) # 索引保持原样
若要强制重置索引,加reset_index(drop=True)。
Q2:如何对分组后的数据做条件过滤?
使用groupby.filter()方法,例如只保留“平均评分高于4.0”的类别:
filtered_df = df.groupby('类别').filter(lambda x: x['评分'].mean() > 4.0)
注意返回的是原始DataFrame的子集,而非分组对象。
Q3:分组后字典嵌套存储实用吗?
对于需要频繁访问某个分组的场景,可以将分组结果转为字典:
groups_dict = {key: group for key, group in df.groupby('城市')}
# 直接访问 groups_dict['北京']
分组导入的最佳实践建议
- 先清洗后分组:分组前确保分组键没有空值(
.dropna(subset=['分组列'])) - 文件命名规范:避免特殊字符,英文+下划线最稳定
- 大数据策略:优先使用
chunksize+ 增量聚合,避免一次性加载 - 动态列名处理:若分组键包含空格,导出时用
sheet_name[:31]截断 - 性能监控:对超大量分组,使用
tqdm库显示进度条:from tqdm import tqdm for key, group in tqdm(df.groupby('城市'), desc='分组进度'): ...
分组导入的核心思想是“拆解-独立处理-重组”,Pandas的groupby将这一思想以声明式语法实现,让数据工程师能够聚焦于业务逻辑而非遍历细节,掌握本文的三个案例,你便能够应对绝大多数日常数据分组导入需求。
延伸阅读:如需处理百万级以上的分组,可考虑使用
dask库实现分布式分组,或配合polars进行列式加速。
代码仓库:所有案例代码已在GitHub开源(示例域名:github.com/data-group-example),欢迎Star与Issue讨论。