page contents

Python处理千万级数据慢成狗,换这3种方法速度提升100倍

机器内存已经吃到 90%,风扇转得像要起飞。代码看着也没什么复杂的,无非是读文件、清洗字段、算个金额,再把结果导出去。

attachments-2026-08-louAzHRZ6a94ef201c8c2.png

一个 8GB 的 CSV,Python 跑了四十多分钟,进度条还卡在 23%。

机器内存已经吃到 90%,风扇转得像要起飞。代码看着也没什么复杂的,无非是读文件、清洗字段、算个金额,再把结果导出去。

问题就出在这个“无非”。

import pandas as pd

data = pd.read_csv("order_detail.csv")

data["phone"] = data["phone"].apply(
    lambda value: str(value).strip().replace("-", "")
)

data["pay_amount"] = data.apply(
    lambda row: row["unit_price"] * row["buy_count"]
    if row["status"] == "paid" else 0,
    axis=1
)

data.to_csv("order_result.csv", index=False)

这种代码处理十万行没什么感觉,数据到了千万级,基本每一行都在给 Python 解释器交税。

我碰到这种任务,一般不急着加机器。先把 apply(axis=1) 删掉,再看文件是不是一次性读进了内存。多数情况下,光这两步就能把速度拉回来。

第一种:别一口吞,分块处理

千万级数据最忌讳一次性 read_csv。

Pandas 不只要存原始数据,还要维护索引、对象类型和中间计算结果。一个几 GB 的文件,进内存后翻两三倍很正常。

我更习惯按块读取,处理完一块立刻写出,不留恋。

from pathlib import Path

import pandas as pd

source_file = Path("order_detail.csv")
target_file = Path("order_result.csv")

if target_file.exists():
    target_file.unlink()

for batch_no, frame in enumerate(
    pd.read_csv(
        source_file,
        chunksize=300_000,
        usecols=["order_id", "phone", "unit_price", "buy_count", "status"]
    )
):
    frame["phone"] = (
        frame["phone"]
        .astype("string")
        .str.strip()
        .str.replace("-", "", regex=False)
    )

    frame["pay_amount"] = (
        frame["unit_price"]
        .mul(frame["buy_count"])
        .where(frame["status"].eq("paid"), 0)
    )

    frame.to_csv(
        target_file,
        mode="a",
        index=False,
        header=batch_no == 0
    )

    print(f"已处理 {batch_no + 1} 批")

这里有个容易漏掉的点:usecols。

文件里有 40 个字段,实际只用 5 个,就别把剩下 35 个也读进来。数据量大以后,少读一个字符串字段,省下来的内存都可能相当可观。

chunksize 也不是越大越好。我一般从 20 万到 50 万开始试,观察内存和磁盘吞吐,再往上调。

第二种:把逐行循环换成向量计算

下面这种写法,我第一眼就不太信。

frame.apply(build_amount, axis=1)

axis=1 意味着 Pandas 要把每一行包装成一个 Series,再调用一次 Python 函数。1000 万行,就是接近 1000 万次函数调用。

业务逻辑能拆成布尔条件和列运算,就别碰逐行 apply。

原来的笨写法:

def build_amount(row):
    if row["status"] == "paid" and row["buy_count"] > 0:
        return row["unit_price"] * row["buy_count"]
    return 0

frame["pay_amount"] = frame.apply(build_amount, axis=1)

换成向量计算:

valid_order = frame["status"].eq("paid") & frame["buy_count"].gt(0)

frame["pay_amount"] = 0.0
frame.loc[valid_order, "pay_amount"] = (
    frame.loc[valid_order, "unit_price"]
    * frame.loc[valid_order, "buy_count"]
)

这不是代码少几行的问题。

向量操作底层主要由 C 和 NumPy 批量执行,Python 不再一行一行地解释。数据越大,差距越明显。很多所谓的“Pandas性能差”,最后查下来,都是把 Pandas 当普通 for 循环用了。

字符串清洗也一样,优先使用 .str:

frame["city_code"] = (
    frame["city_code"]
    .astype("string")
    .str.upper()
    .str.strip()
    .str.slice(0, 6)
)

不要为了看起来灵活,给每一行塞一个 lambda。千万级数据不吃这一套。

第三种:别硬扛,直接换 Polars 的惰性计算

如果数据清洗已经比较固定,而且主要是筛选、转换、聚合,我会直接试 Polars。

特别是 CSV 很大时,scan_csv 不会马上把整个文件读进内存。它会先建立执行计划,只读取真正需要的列,并把过滤尽量提前。

import polars as pl

task = (
    pl.scan_csv("order_detail.csv")
    .select(
        "order_id",
        "phone",
        "unit_price",
        "buy_count",
        "status"
    )
    .with_columns(
        pl.col("phone")
        .cast(pl.String)
        .str.strip_chars()
        .str.replace_all("-", "")
        .alias("phone"),

        pl.when(
            (pl.col("status") == "paid")
            & (pl.col("buy_count") > 0)
        )
        .then(pl.col("unit_price") * pl.col("buy_count"))
        .otherwise(0)
        .alias("pay_amount")
    )
    .filter(pl.col("pay_amount") > 0)
)

task.sink_csv("paid_order_result.csv")

这段代码没有手动循环,也没有先把全部数据塞进 DataFrame。

Polars 会做列裁剪、条件下推和并行执行。实际能快多少,取决于数据类型、磁盘速度和计算逻辑。简单过滤聚合提升几倍很常见;原代码如果大量使用 apply 和 Python 循环,拉开几十倍甚至接近百倍,也不奇怪。

不过别看到“更快”就全项目迁移。

只是偶尔处理一个大文件,Pandas 分块加向量化已经够用。任务每天跑、数据持续增长、计算链路又比较固定,再考虑 Polars。工具换得太早,维护成本最后还是自己扛。

处理千万级数据,顺序别搞反了。

先删逐行 apply,再限制读取列,然后分块。做完这些还是慢,再换执行引擎。上来就开多进程,往往只是让几个进程一起抢内存、抢磁盘,日志看着挺热闹,文件还是没处理完。

Python不一定慢。

慢的通常是那段让 Python 重复执行了一千万次的代码。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
王昭君
王昭君

216 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. Pack 2372 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章