资讯动态

使用Python进行大规模数据处理和分析

发布时间:2026/10/17 12:56:16 来源:尧图企业网站定制
前言先纠正一个常见误解大规模没有绝对标准它是相对于你手上这台机器的内存而言的。对一台 8 GB 内存的笔记本2 GB 的 CSV 就已经是大规模——因为你不能把它一次性读成 DataFrame而对一个有几十个节点的集群同样 2 GB 的数据连热身都算不上。所以本文不吹用 Python 处理大数据而是给出一个诚实的判断框架当数据放不进内存时有哪几条路可选每条路的代价是什么以及什么时候根本不需要上分布式框架。一个必须点破的事实分布式框架不是免费午餐。引入 Dask 或 Spark 意味着额外的调度开销、序列化开销、调试难度和部署成本。如果数据只有几百 MB用它们往往比直接用标准库慢。先量数据规模再选工具。本机没有 Python 解释器和任何第三方库代码只能逐行人工推演不声称运行过。一、先量清楚你到底有多大动手之前先回答三个问题数据总量是多少压缩前 / 解压后单机可用内存是多少要留出至少一半余量给中间结果是必须全量加载还是可以逐批处理大多数分析任务属于第三类的后一种求计数、求和、均值、去重、过滤、分组聚合——这些都能流式streaming完成不需要把全部数据同时放在内存里。真正必须全量加载的场景是需要对整个数据集排序、需要随机访问、需要多轮迭代的算法。判断标准很简单能不能只遍历一遍数据就得到答案能就走流式不能再考虑加内存或换引擎。数据规模推荐路径代价远小于内存直接读进内存最省事无接近或略超内存分块处理、生成器流水线需要改写成流式写法显著超过内存换引擎Dask / Spark 等或上更多内存部署与调试成本明显上升二、第一选择分块与生成器分块chunking的思路是不一次读完而是每次读一部分处理完就释放。pandas 的read_csv有chunksize参数返回一个可迭代对象每次产出一个较小的 DataFrame。用法思路是对每个块做聚合把结果累积到一个小的中间结构里最后合并。具体参数名与返回类型以 pandas 官方文档为准。标准库这边的 CSV 读取天然就是流式的csv.reader是一个迭代器逐行产出列表不需要把整个文件读进内存。# 适用于 Python 3.8import csvfrom collections import defaultdictdef sum_by_category(path, key_col, value_col):流式分组求和只保留聚合结果不保留原始行。totals defaultdict(float)counts defaultdict(int)# newline 是 csv 模块的要求否则换行符处理会出问题with open(path, r, encodingutf-8, newline) as f:reader csv.reader(f)header next(reader) # 跳过表头key_idx header.index(key_col)val_idx header.index(value_col)for row in reader:if len(row) max(key_idx, val_idx):continue # 跳过残缺行key row[key_idx]try:value float(row[val_idx])except ValueError:continue # 跳过无法解析的单元格totals[key] valuecounts[key] 1return {k: totals[k] / counts[k] for k in totals}if __name__ __main__:# 演示调用实际运行需存在该 CSV 文件result sum_by_category(data.csv, category, amount)for k in sorted(result):print(k, round(result[k], 2))这段代码的内存占用与文件大小无关无论文件是 1 MB 还是 10 GB驻留内存只取决于分组的类别数。这就是流式的价值。生成器generator是构造流式管道的工具。用yield逐条产出记录把读取 → 清洗 → 过滤 → 聚合拆成若干个小函数每个都是惰性的。好处是管道里没有任何一个环节持有全部数据而且环节之间可以独立调试。# 适用于 Python 3.8import csvdef read_rows(path, encodingutf-8):逐行产出字典惰性求值。with open(path, r, encodingencoding, newline) as f:reader csv.DictReader(f)for row in reader:yield rowdef keep_valid(rows, required):过滤掉关键字段为空的行。for row in rows:if all(row.get(col) for col in required):yield rowdef to_number(rows, col):把指定列转成浮点数转换失败的行被丢弃。for row in rows:try:yield float(row[col])except (TypeError, ValueError):continue# 管道每一步都是生成器只有最后一步才真正驱动计算pipeline to_number(keep_valid(read_rows(data.csv), [amount]), amount)total 0.0count 0for value in pipeline:total valuecount 1print(均值:, total / count if count else None)注意生成器只能遍历一次。上面pipeline一旦被for消费就空了想再算一遍必须重新构造管道。这是最常见的误用。三、第二选择换引擎当分块也解决不了比如需要全局排序、需要多轮迭代的机器学习训练才考虑专门的大数据引擎。Dask的思路是并行化的 pandas / numpy它把大数据切成很多小块用任务图调度接口尽量贴近 pandas学习成本相对低。适合本来用 pandas 写但数据放不下的场景。Spark是更重的一套独立集群、独立的执行模型、需要序列化与调度。它适合数据量确实很大、且已经有集群资源的场景。选型时要记住几条现实约束小数据上用分布式框架几乎总是更慢因为调度和序列化的固定开销摆在那里分布式框架的报错信息通常比单机难读调试成本要高一个量级换引擎意味着重写代码不是换个 import 就行先试着加内存、改数据类型比如用更省的整数类型、只加载需要的列这些土办法经常能解决问题而且改动最小。具体 API、参数与部署方式以各框架官方文档为准。四、GIL 与并发的边界谈大规模绕不开并行。这里必须讲准因为错误结论流传很广。CPython 有全局解释器锁GIL, Global Interpreter Lock它保证同一进程内同一时刻只有一个线程执行 Python 字节码。由此推出两个结论CPU 密集型任务用多线程没有加速效果——所有线程抢同一把锁本质还是轮流跑。I/O 密集型任务用多线程有效——线程在等待网络或磁盘时会释放 GIL别的线程可以继续跑。CPU 密集要真正并行得用多进程multiprocessing每个进程有独立的解释器与独立的 GIL或者调用会释放 GIL 的 C 扩展或者换语言实现。还有一个必须澄清的说法Python 已经没有 GIL 了是错的。CPython 从 3.13 起提供实验性的自由线程no-GIL构建但它不是默认构建需要单独安装生态兼容性也还在推进中。默认的 CPython 依然有 GIL。因此对一个数据管道并行的正确姿势通常是多进程读不同的分片文件每个进程内部仍是流式处理而不是开一堆线程去读同一个文件。五、把原则落到清单上处理超内存数据时的检查顺序先量总量与内存确认是不是真的超了能不能改成单遍流式能就别折腾必须全量时能不能只加载需要的列、只保留必要的类型还不行才考虑分块 外部排序 / 中间结果落盘最后才考虑引第三方引擎并评估它带来的复杂度。常见坑点不量数据规模就上分布式框架。❌ 几百 MB 的数据直接上 Spark 集群还觉得这样才专业。 ✅ 先量总量能用标准库或单机 pandas 解决就别引入调度开销。把大规模当成绝对概念。❌ 认为没上分布式就等于没做大数据。 ✅ 认清大规模是相对于可用内存而言的同一个文件在不同机器上是不同量级。生成器被消费后重复遍历。❌ 用一个生成器表达式算完总和后又拿它去算平均值得到空结果。 ✅ 生成器只能遍历一次需要多次统计时重新构造管道或显式落成列表但要评估内存。一次性把大文件读进列表。❌rows list(csv.reader(f))把整个文件驻留内存。 ✅ 直接迭代 reader 本身或写成生成器管道只保留聚合结果。认为多线程能加速 CPU 密集任务。❌ 用ThreadPoolExecutor去做大量纯计算期待线性提速。 ✅ 受 GIL 限制CPU 密集要用多进程或释放 GIL 的原生扩展多线程的收益在 I/O 等待上。断言Python 已经没有 GIL。❌ 因为听说过 no-GIL 就说 GIL 已经消失。 ✅ CPython 3.13 起只有实验性自由线程构建默认构建仍然有 GIL。open不写encoding。❌ 在跨平台场景下用默认编码读数据文件得到乱码或解析失败。 ✅ 显式指定encodingutf-8或数据真实的编码读 CSV 还要加newline。忽视中间结果的落盘策略。❌ 每一步都把所有中间结果留在内存里管道走到一半内存爆掉。 ✅ 只在必要时保留聚合结果需要多轮使用时把中间结果写到磁盘用文件换内存。总结场景推荐做法关键提醒数据远小于内存直接加载别为了专业引入框架可单遍流式完成生成器管道 /csv.reader迭代生成器只能遍历一次略超内存分块处理只保留聚合值内存占用与文件大小解耦远超内存换引擎Dask / Spark或加内存有部署与调试成本需权衡需要并行CPU 密集用多进程I/O 密集用多线程默认 CPython 仍有 GIL这条路上最实用的态度是克制先确认规模再尝试把写法改成流式最后才谈换引擎。绝大多数所谓Python 处理不了的大数据其实是被一次性加载的写法卡住的而不是 Python 本身不行。所有第三方库的具体参数与行为请以各自官方文档的当前版本为准。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑