如何基于分块读取的大型CSV文件计算中位数/百分位数?
这个问题问到点子上了!分块算求和这种能直接累加的统计量确实轻松,但中位数、百分位数这类依赖数据排序分布的指标,处理起来就得换思路了。我给你两种方案,分别对应精确计算和近似计算,你可以根据自己的内存情况来挑:
精确计算中位数/百分位数(内存允许时)
如果你的机器内存能装下所有bytes_alloc列的数据(数百万行的话,假设每个值是8字节的浮点数,总大小也就几十到几百MB,大部分机器都能hold住),那直接收集所有分块的目标列再合并计算是最稳妥的,结果完全精确。
先帮你修正下现有代码里的小bug:你循环里写的for tf in df:是错误的,应该遍历read_csv返回的分块对象,变量名最好区分开,避免混淆:
import pandas as pd # 初始化列表存储每个分块的bytes_alloc列 data_chunks = [] # 分块读取CSV chunk_reader = pd.read_csv("../Samples/analytics/trace.csv", chunksize=1024*25) for chunk in chunk_reader: # 只保留需要的列,进一步节省内存 data_chunks.append(chunk["bytes_alloc"]) # 合并所有分块的数据 all_bytes = pd.concat(data_chunks, ignore_index=True) # 计算中位数 median_value = all_bytes.median() # 计算90%百分位数 p90_value = all_bytes.quantile(0.9) # 计算95%百分位数 p95_value = all_bytes.quantile(0.95) print(f"中位数: {median_value}") print(f"90%百分位数: {p90_value}") print(f"95%百分位数: {p95_value}")
近似计算(内存极度紧张时)
如果数据量实在大到连单列都存不下,那只能用近似方法。这里给你两种实用的思路:
方法1:基于分块分位数的近似估算
先统计每个分块的关键分位数(比如0%、25%、50%、75%、100%),再把这些分位数收集起来,按分块行数加权计算整体近似分位数:
import pandas as pd import numpy as np chunk_reader = pd.read_csv("../Samples/analytics/trace.csv", chunksize=1024*25) # 存储每个分块的分位数和行数 quantile_list = [] row_counts = [] for chunk in chunk_reader: col = chunk["bytes_alloc"] # 计算当前分块的核心分位点,可根据需求增加更多分位点 quantiles = col.quantile([0, 0.25, 0.5, 0.75, 1.0]).values quantile_list.append(quantiles) row_counts.append(len(col)) # 按分块行数加权计算近似中位数 approx_median = np.average([q[2] for q in quantile_list], weights=row_counts) # 合并所有分块分位数,计算近似95%百分位数 all_quantiles = np.concatenate(quantile_list) approx_p95 = np.percentile(all_quantiles, 95) print(f"近似中位数: {approx_median}") print(f"近似95%百分位数: {approx_p95}")
方法2:使用t-digest算法(更精准的近似)
t-digest是专门为超大规模数据设计的近似分位数算法,误差极小且内存占用极低。你需要先安装tdigest库(pip install tdigest),然后分块更新t-digest再计算结果:
import pandas as pd from tdigest import TDigest chunk_reader = pd.read_csv("../Samples/analytics/trace.csv", chunksize=1024*25) digest = TDigest() for chunk in chunk_reader: # 将当前分块的bytes_alloc数据加入t-digest digest.update(chunk["bytes_alloc"].values) # 计算近似中位数和百分位数 approx_median = digest.percentile(50) approx_p95 = digest.percentile(95) print(f"t-digest近似中位数: {approx_median}") print(f"t-digest近似95%百分位数: {approx_p95}")
如果你的数据分布比较均匀,近似方法的结果会非常接近精确值;如果数据极端倾斜,优先考虑精确方法(只要内存够)。
内容的提问来源于stack exchange,提问作者user3206440
相关产品推荐
相关产品推荐

