🏠 首页 攻略 Python 数据处理 8 个被低估的技巧,90%的人不知道

Python 数据处理 8 个被低估的技巧,90%的人不知道

从内存优化到批量处理,这 8 个 Python 数据处理技巧帮你把代码速度提升数倍。每个技巧都有实际代码示例,看完就能用。

你的 Python 代码可能慢得离谱

上周帮一个朋友看代码。他处理一个 2GB 的 CSV 文件,跑了 40 分钟还没出结果。

我扫了一眼,三个问题:循环读取、重复创建 DataFrame、没利用向量化。

改完以后,3 分钟搞定。

不是他的逻辑有问题,是方法不对。下面这些技巧,能帮你避开大部分常见坑。

技巧 1:别用 read_csv 读大文件

很多人写数据分析代码,第一行就是:

df = pd.read_csv('huge_file.csv')

文件超过 1GB 的时候,这行代码会吃掉你大量内存。

换成分块读取:

chunks = []
for chunk in pd.read_csv('huge_file.csv', chunksize=100000):
    filtered = chunk[chunk['status'] == 'active']
    chunks.append(filtered)

df = pd.concat(chunks, ignore_index=True)

这样每次只加载 10 万行,内存压力小很多。

关键点: 在分块阶段就做好过滤和聚合,别把所有数据都攒到最后。

技巧 2:用 .loc 替代逐行循环

这是最常见的性能杀手。

# 错误示范
for index, row in df.iterrows():
    if row['age'] > 30:
        row['category'] = 'senior'

这种写法在处理 10 万行数据时,可能要跑好几分钟。

换成向量化操作:

df.loc[df['age'] > 30, 'category'] = 'senior'

一行搞定,速度快几十倍。

如果逻辑太复杂,向量化写不出来,至少用 apply():

df['category'] = df.apply(lambda x: 'senior' if x['age'] > 30 else 'junior', axis=1)

虽然不如纯向量化快,但比 iterrows 好得多。

技巧 3:数据类型选对,内存省一半

Pandas 默认会把数字列读成 float64,字符串列读成 object。很多时候没必要。

# 默认读取
df = pd.read_csv('data.csv')
print(df.dtypes)
# age: float64
# price: float64
# category: object

手动指定类型:

df = pd.read_csv('data.csv', dtype={
    'age': 'int8',
    'price': 'float32',
    'category': 'category'
})

category 类型特别适合身份证号、地区编码这类重复值多的字段。内存占用能降 80% 以上。

技巧 4:用 DuckDB 替代 Pandas 做查询

如果你的数据量已经大到 Pandas 处理吃力,试试 DuckDB。

它长得像 SQL,跑起来像数据库,还能直接读 CSV/Parquet。

import duckdb

result = duckdb.query("""
    SELECT city, AVG(price) as avg_price
    FROM 'sales.csv'
    WHERE date >= '2026-01-01'
    GROUP BY city
    ORDER BY avg_price DESC
""").df()

DuckDB 的优势是:

  • 支持 SQL 语法,不用学新 API
  • 自动并行处理
  • 能处理比内存更大的数据集
  • 和 Pandas 无缝切换

对于数据清洗和聚合场景,DuckDB 经常比 Pandas 快 5-10 倍。

技巧 5:保存为 Parquet,别再用 CSV

CSV 是文本格式,读取时要解析每一行。Parquet 是列式存储,体积小、速度快。

# 保存
df.to_parquet('data.parquet', engine='pyarrow')

# 读取
df = pd.read_parquet('data.parquet')

实测对比(同一个 500MB 文件):

  • CSV 写入:约 12 秒
  • Parquet 写入:约 4 秒
  • CSV 读取:约 8 秒
  • Parquet 读取:约 2 秒

而且 Parquet 会保留数据类型,不会出现 CSV 常见的数字变字符串的问题。

建议: 中间结果一律存 Parquet,只有最终导出才转 CSV。

技巧 6:用 merge 替代多次 join

很多人习惯链式拼接:

df1.merge(df2).merge(df3).merge(df4)

数据量大时,每 merge 一次都要重新构建索引。

更好的做法是一次性合并:

tables = {'df2': df2, 'df3': df3, 'df4': df4}

result = df1
for key, table in tables.items():
    result = result.merge(table, on='id', how='left')

虽然还是循环,但至少避免了不必要的中间对象创建。

如果表结构相似,也可以用 pd.concat 做纵向合并,速度更快。

技巧 7:处理缺失值的正确姿势

缺失值处理是数据清洗的重灾区。

常见错误:

# 直接删掉所有含缺失值的行
df.dropna()

这会导致大量有效数据丢失。

正确做法是先分析缺失模式:

missing_ratio = df.isnull().sum() / len(df)
print(missing_ratio[missing_ratio > 0].sort_values(ascending=False))

df['age'].fillna(df['age'].median(), inplace=True)
df['city'].fillna('未知', inplace=True)

原则: 数值型用中位数填充,分类用众数填充,时间序列用前向填充。别一股脑全删。

技巧 8:善用 profiling 找瓶颈

最后这个技巧最重要——别猜哪里慢,测一下。

import cProfile

cProfile.run('your_function()', sort='cumulative')

或者用更直观的 line profiler:

%load_ext line_profiler
%lprun -f your_function your_function()

这样你能看到每一行代码的实际耗时。

大部分时候你会发现:真正拖慢速度的是你以为"应该很快"的那几行。

总结一张表

技巧适用场景预期提升
分块读取大文件处理内存降低 70%+
向量化操作条件判断、列计算速度提升 10-50 倍
优化数据类型内存敏感场景内存降低 30-80%
DuckDB 查询复杂聚合分析速度提升 5-10 倍
Parquet 存储频繁读写中间数据I/O 降低 60%+
批量 merge多表关联减少中间对象
智能填补缺失值数据清洗保留更多有效数据
Profiling 定位瓶颈性能优化精准找到慢点

最后说一句

数据处理这事儿,80% 的性能问题都来自同样的几个坏习惯。

别循环,别全量加载,别乱用默认类型。

先把这三点做到,你的代码就能快一大截。剩下的 20%,靠 profiling 慢慢调。

有具体场景拿不准怎么优化的?把数据量和需求发出来,帮你看看。


本文基于实际测试经验整理。代码示例基于 Python 3.11 + Pandas 2.x + DuckDB 1.x。