6.3 抽样与批流处理:处理量级的选择


6.3 抽样与批流处理:处理量级的选择

本节摘要:数据量大到全量跑不动时,有两手准备——抽样(sample 取可复现的代表性子集)与分批(chunksize 分块读取、批处理与流处理的量级观)。本节讲 sample 的参数纪律、分层抽样的思路,以及"一次做多少菜"的量级判断。承接 6.2 的正则,为调味架收官。

一家电商公司的取数群聊

一家电商公司的取数群里,每周都有同一段对话:有人喊"两亿行的表,脚本跑了一下午还没出",有人回"你先抽十万行试试逻辑"。抽样的价值就在这一问一答里——调试阶段,逻辑正确性与数据规模无关;先在小样本上把手法练对,再决定全量怎么跑。本节讲"量"的两手:一手是抽样,用 sample 取出可复现的子集;一手是分批,chunksize 分块读、批处理与流处理各守各的量级。往前接调味架的工具视角,往后直接衔接第 7 章中央厨房的扩容决策。

参数拆解:sample 的旋钮

**df.sample(n=None, frac=None, replace=False, weights=None, random_state=None)**:n 给条数,frac 给比例(两者二选一);replace=True 允许重复抽取(放回抽样,frac 大于等于时必开);weights 按列加权抽样,权重大的行更容易被抽中;random_state 固定随机种子——同样种子同样结果,是"可复现"三个字的全部实现。它还有个兄弟 df.nlargest(3.2 见过),区别在于 sample 随机、nlargest 按值——"看看数据长什么样"用 sample,"找出最大的几条"用 nlargest。

import pandas as pd df = pd.DataFrame({ "用户": [f"u{i}" for i in range(100)], "地区": ["南方"] * 60 + ["北方"] * 40, "消费": [abs(hash(u)) % 500 for u in range(100)], }) # 抽百条里的十条,种子固定,谁跑都一样 s1 = df.sample(n=10, random_state=42) s2 = df.sample(n=10, random_state=42) print(s1.equals(s2)) # True <- 可复现 # 按比例抽一成 print(len(df.sample(frac=0.1, random_state=1))) # 10

实操示例:分层抽样与分块读取

场景一:南北用户数量悬殊,随机抽样会把北方抽得太少——按地区分层,每组各抽固定比例,样本结构才贴近总体。

# 分层抽样:按地区分组,各抽两成,组合回表 strata = df.groupby("地区", group_keys=False).apply( lambda g: g.sample(frac=0.2, random_state=7)) print(strata["地区"].value_counts()) # 南方 12 # 北方 8 # 比例仍约六比四,代表性保住了

场景二:文件大到内存装不下,用 chunksize 分块读、逐块清洗、最后拼接结果——批处理的入门形态。

chunks = pd.read_csv("orders.csv", chunksize=100000) # 每次读十万行 results = [] for chunk in chunks: # 迭代器:一块一块给 cleaned = chunk.dropna(subset=["金额"]) results.append(cleaned.groupby("渠道")["金额"].sum()) total = pd.concat(results).groupby(level=0).sum() # 分块聚合后二次合并

抽样的两种失真

随机种子能保复现,保不了代表性——抽样失真主要有两型。覆盖失真:稀有类别在随机样本里可能一条都抽不到,调试时以为逻辑通了,全量一跑新类别报错——分层抽样正是为此准备的兜底。时段失真:数据若按时间落盘,短窗口抽样看到的模式可能只在特定时段成立——调试期多换几个 random_state 各看一遍,比死磕单一样本更接近真相。判断样本够不够用的土办法:抽出的样本跑一遍 1.1 的验收单,唯一值数与全量差得太远,说明代表性不够,加量或分层。

什么时候不必抽样

调味也要看菜:有些场合抽样反而画蛇添足。数据只有几万行、全量跑不过几秒,直接全量,省去样本代表性的论证;验收类检查(1.1 的类型体检)本就该全量做,抽样会漏掉只在某段出现的脏数据;指标要对外交付时,口径里若没有"抽样"二字,就必须全量。抽样的正当场合始终是"探索与调试"——逻辑验证、参数试跑、可视化预览。把这条边界记牢,sample 才是省时工具而不是偷工减料的遮羞布。

坑点与翻车

**翻车一:random_state 想起就给。**调试时抽 A 样本,回归测试时抽 B 样本,"同一份代码结果不同"的灵异事件多半源于此——种子的位置与数值写进配置或注释,全组共用。**翻车二:抽样前先排序。**sample 对排序后的表抽样,看似随机、实则受上游排序影响;如果上游刚好按时间排过序,短窗口试跑看到的数据就带时段偏差——抽样本身就是打乱,一般无需担心,但配合 nlargest 或 head 使用时要警觉。**翻车三:分块循环里忘聚合策略。**逐块算出十个"局部均值"再平均,与全量均值不是一回事——分块聚合的合并口径要预先设计(求和、计数可先攒后除;均值、去重必须攒中间量),场景二里的二次 groupby 正是为此。**翻车四:流处理当批处理写。**流式数据边到边算,没有"读完了再聚合"这回事,窗口(滚动、会话)是它的时间刻度——把批处理的习惯直接搬进流处理,出的数永远慢半拍或对不上。

替代方案

只想"瞄一眼结构",head 与 tail 比抽样更省事,但看分布仍要 sample;探查列的类型与缺失,1.1 的验收单比任何抽样都直接;数据超出单机内存的量级,抽样调试之后正式跑就该换 7.4 的 Dask 或 PySpark;数据持续不断产生,批处理升级成流处理,窗口函数(4.3 的思想)在流式世界是核心词汇。量级阶梯记一句话:单表内存装得下用 pandas,装不下用分块或 Dask,跨机器用 Spark,永不停止用流。

本节要点回顾

  • sample 四旋钮:n 与 frac 定量,replace 允许放回,random_state 锁复现;
  • 分层抽样:groupby 配 sample,各层按比例取,代表性不塌方;
  • chunksize 分块:read_csv 返回迭代器,逐块清洗、设计好合并口径;
  • 批与流:批处理攒够再算,流处理边到边算,窗口是流的时间刻度;
  • 量级阶梯:pandas、分块、Dask 与 Spark、流式,逐级升灶。

调味架三味到此齐备。下一章进中央厨房:pandas 的函数地图、NumPy 的向量化、SciPy 与 scikit-learn 的调料柜、Dask 与 PySpark 的大灶台——见 7.1。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U