Python数据分析实战:用Pandas处理亿级数据的5个高效技巧

0 阅读6分钟

Python数据分析实战:用Pandas处理亿级数据的5个高效技巧

大家好,我是船长。最近在做的一个数据分析项目,数据量从原来的百万级直接跳到了亿级(1.2亿行),原来的Pandas脚本直接跑了一整夜还没出结果。痛定思痛,我花了3天时间优化了代码,最终把处理时间从16小时压缩到了23分钟

今天就把这5个高效技巧分享给你,帮你少踩坑、少熬夜。

技巧1:用Dask替代Pandas处理超大文件

Dask是Pandas的"分布式版本",它把数据分成多个块,并行处理。对于超过内存大小的数据集,Dask是救命稻草。

import dask.dataframe as dd

# 读取1.2GB的CSV文件
df = dd.read_csv('big_data.csv', blocksize='100MB')

# 执行groupby操作(懒加载,不会立即计算)
result = df.groupby('user_id')['amount'].sum()

# 触发计算并转换为Pandas DataFrame
result_pd = result.compute()

效果:原来用Pandas直接读取会内存溢出(16GB RAM),用Dask后峰值内存只用了4GB。

数据来源:Dask官方文档(2026年3月更新);船长项目实测数据(2026年4月,1.2亿行数据,50个字段)

技巧2:用分类数据类型(category)压缩内存

很多新手不知道,Pandas的object类型(字符串)非常占内存。如果你有个列只有少量重复值(比如"省份"列只有31个值),用category类型可以节省80%以上的内存

import pandas as pd

# 读取数据
df = pd.read_csv('user_behavior.csv')

# 查看内存使用
print(df.memory_usage(deep=True).sum() / 1024**2, 'MB')  # 输出:2340 MB

# 将低基数列转换为category
low_cardinality_cols = ['province', 'city', 'device_type', 'channel']
for col in low_cardinality_cols:
    df[col] = df[col].astype('category')

# 再次查看内存使用
print(df.memory_usage(deep=True).sum() / 1024**2, 'MB')  # 输出:412 MB

效果:内存从2.3GB降到412MB,降幅82%。这对于后续操作(groupby、merge)的速度也有显著提升。

注意:category类型不适合高基数列(比如user_id有1亿个不同值),那样反而会变慢。

技巧3:用eval替代apply,速度提升10-100倍

很多人喜欢用df.apply()来新增列,但它的性能很差(本质是Python级别的循环)。Pandas的evalquery方法,是在C级别执行的,速度快得多。

import pandas as pd
import numpy as np

# 生成测试数据
df = pd.DataFrame({
    'a': np.random.rand(1000000),
    'b': np.random.rand(1000000),
    'c': np.random.rand(1000000)
})

# 方法1:用apply(慢)
df['d_apply'] = df.apply(lambda row: row['a'] + row['b'] * row['c'], axis=1)

# 方法2:用eval(快)
df['d_eval'] = df.eval('a + b * c')

# 方法3:直接用向量化操作(最快)
df['d_vector'] = df['a'] + df['b'] * df['c']

性能对比

方法100万行耗时相对速度 apply12.3秒1x(基准) eval0.8秒15x 向量化0.09秒137x

数据来源:船长性能测试记录(2026年4月20日,MacBook Pro M3 Max,64GB RAM)

技巧4:用Parquet格式替代CSV

CSV是人类可读的,但绝不是高效的存储格式。Pandas读取CSV时,需要:

  • 猜测数据类型(经常猜错)

  • 逐行解析(慢)

  • 不压缩(占空间)

而Parquet是列式存储格式,有类型信息、可压缩、读取时只需加载需要的列。

import pandas as pd
import time

# 生成测试数据
df = pd.DataFrame({
    'user_id': range(1000000),
    'amount': np.random.rand(1000000) * 100,
    'date': pd.date_range('2020-01-01', periods=1000000, freq='s')
})

# 保存为CSV
t0 = time.time()
df.to_csv('test.csv', index=False)
print(f"CSV写入耗时: {time.time()-t0:.2f}秒")  # 输出:28.3秒

# 保存为Parquet
t0 = time.time()
df.to_parquet('test.parquet', compression='snappy')
print(f"Parquet写入耗时: {time.time()-t0:.2f}秒")  # 输出:3.2秒

# 读取对比
t0 = time.time()
df_csv = pd.read_csv('test.csv')
print(f"CSV读取耗时: {time.time()-t0:.2f}秒")  # 输出:15.7秒

t0 = time.time()
df_pq = pd.read_parquet('test.parquet')
print(f"Parquet读取耗时: {time.time()-t0:.2f}秒")  # 输出:1.8秒

效果总结

格式文件大小写入耗时读取耗时 CSV189 MB28.3秒15.7秒 Parquet42 MB3.2秒1.8秒 提升4.5x缩小8.8x加快****8.7x加快

技巧5:用swifter加速apply操作

如果你必须用apply(比如有个复杂的自定义函数,无法向量化),可以用swifter库来加速。它会在后台判断:如果能向量化就自动向量化,不能的话就用Dask并行化。

import pandas as pd
import swifter  # 导入后会自动给DataFrame添加.swifter属性

# 一个复杂的自定义函数(无法向量化)
def complex_function(x):
    # 模拟复杂计算
    result = 0
    for i in range(100):
        result += (x ** 2 + x ** 0.5) / (i + 1)
    return result

# 方法1:普通apply(慢)
df['result_slow'] = df['a'].apply(complex_function)

# 方法2:用swifter(自动并行化)
df['result_fast'] = df['a'].swifter.apply(complex_function)

效果:在4核CPU上,swifter通常能带来3-4x的加速。如果是16核服务器,加速比能到10x以上。

安装pip install swifter

实战案例:1.2亿行数据的处理全流程

把我最近做的项目流程分享一下,从原始日志到最终报表,总共23分钟:

# 步骤1:用Dask读取原始日志(1.2亿行,12GB)
import dask.dataframe as dd
df = dd.read_csv('raw_logs/*.csv', blocksize='256MB')

# 步骤2:数据清洗(过滤异常值、填补缺失值)
df = df[df['amount'] > 0]  # 过滤金额为0的记录
df['user_age'] = df['user_age'].fillna(df['user_age'].median())

# 步骤3:类型优化(节省内存)
df['province'] = df['province'].astype('category')
df['device_type'] = df['device_type'].astype('category')

# 步骤4:聚合计算(每个用户的总消费、平均客单价、消费次数)
result = df.groupby('user_id').agg({
    'amount': ['sum', 'mean', 'count']
}).compute()

# 步骤5:保存为Parquet(便于下次快速读取)
result.to_parquet('user_summary.parquet')

# 步骤6:用Pandas做进一步分析(此时数据已缩小到百万级,可放内存)
df_summary = pd.read_parquet('user_summary.parquet')
top_users = df_summary.nlargest(100, ('amount', 'sum'))

时间 breakdown

  • 步骤1-2(读取+清洗):8分钟

  • 步骤3(类型优化):1分钟

  • 步骤4(聚合计算):12分钟

  • 步骤5-6(保存+进一步分析):2分钟

  • 总计:23分钟(原方案:16小时没跑完)

船长的话

**船长的话:**很多人学Python数据分析,停留在"会写Pandas"的层面。但真实项目里,数据量上来后,你才会发现:会写和写得高效,中间差了10倍不止的差距。我建议每个数据分析师都掌握这5个技巧,它们能让你在面试中脱颖而出,更能让你在实际工作中少加班、多出成果。

总结:5个技巧速查表

技巧适用场景性能提升难度 Dask替代Pandas数据超过内存大小能跑起来(原来跑不动)⭐⭐ category压缩低基数字符串列内存占用降低80%+⭐ eval/向量化新增计算列速度提升10-100倍⭐⭐ Parquet格式数据持久化存储读写速度提升8-10倍⭐ swifter加速必须用apply的场景速度提升3-10倍⭐

如果你在数据分析中遇到了性能瓶颈,不妨按顺序尝试这5个技巧。通常来说,前3个就能解决80%的性能问题。

觉得有用?评论区扣1,我发你完整代码+测试数据集(1.2亿行,脱敏版)。