如何将80GB+公共数据库数据导入Python开展EDA及可视化处理
80GB级CSV文件出租车行程数据EDA落地方案
零额外依赖方案:pandas分块增量统计
- 放弃直接调用
pd.read_csv()加载全量文件的思路,给read_csv传入chunksize参数,将文件拆分为若干个可完全加载进内存的小块逐块读取,单块大小建议设为100万-500万行,可根据自身机器可用内存调整,保证单块加载后内存占用不超过可用内存的30%即可,不会触发内核崩溃。 - 提前初始化EDA需要的统计结果容器,包括数值列的计数、总和、平方和、最值、空值数,分类列的取值频次,按维度聚合的中间结果。每读取一个数据块,就更新对应容器的统计值,全量文件读取完成后,直接基于累计的统计值计算均值、标准差、占比、分组聚合结果即可,全程不需要在内存中留存全量明细数据。
- 核心参考代码:
import pandas as pd import numpy as np # 提前指定需要统计的字段,避免读取无关列浪费内存 numeric_cols = ["trip_seconds", "trip_miles", "fare", "tips", "tolls", "trip_total"] cat_cols = ["payment_type", "company"] date_cols = ["trip_start_timestamp", "trip_end_timestamp"] # 初始化统计容器 numeric_stats = { col: {"valid_count":0, "val_sum":0, "val_sq_sum":0, "min_val":np.inf, "max_val":-np.inf, "null_cnt":0} for col in numeric_cols } cat_counts = {col:{} for col in cat_cols} daily_trip_cnt = {} # 逐块读取 chunk_iter = pd.read_csv( "taxi_trips.csv", chunksize=2_000_000, usecols=numeric_cols+cat_cols+date_cols+["trip_id"], # 只读需要的列,进一步降低内存占用 parse_dates=date_cols ) for chunk in chunk_iter: # 更新数值列统计值 for col in numeric_cols: col_valid = chunk[col].dropna() numeric_stats[col]["valid_count"] += len(col_valid) numeric_stats[col]["val_sum"] += col_valid.sum() numeric_stats[col]["val_sq_sum"] += (col_valid ** 2).sum() numeric_stats[col]["min_val"] = min(numeric_stats[col]["min_val"], col_valid.min()) numeric_stats[col]["max_val"] = max(numeric_stats[col]["max_val"], col_valid.max()) numeric_stats[col]["null_cnt"] += chunk[col].isna().sum() # 更新分类列频次 for col in cat_cols: vcount_dict = chunk[col].value_counts().to_dict() for k, v in vcount_dict.items(): cat_counts[col][k] = cat_counts[col].get(k, 0) + v # 更新按天聚合的单量 daily_cnt = chunk.groupby(chunk["trip_start_timestamp"].dt.date)["trip_id"].count().to_dict() for d, cnt in daily_cnt.items(): daily_trip_cnt[d] = daily_trip_cnt.get(d, 0) + cnt # 所有块读完后,直接基于三个统计容器计算均值、标准差、占比等EDA指标即可
- 可视化环节直接使用聚合完成的统计结果绘图即可,不需要调用全量明细数据。
低代码量方案:使用外存计算框架替代原生pandas
- 如果不想手动写分块统计逻辑,直接用Dask DataFrame即可,它的API和pandas保持90%以上的一致性,会自动调度分块计算、利用磁盘做外存,不需要手动管理分块逻辑,代码改动量极小。本地单机器即可运行,不需要搭建分布式集群,安装直接执行
pip install dask[dataframe]。 - 核心参考代码:
import dask.dataframe as dd # 读取文件,不会立即加载全量数据进内存 ddf = dd.read_csv( "taxi_trips.csv", usecols=numeric_cols+cat_cols+date_cols+["trip_id"], parse_dates=date_cols ) # 编写和pandas语法一致的统计逻辑,调用compute()才会实际执行计算 fare_mean = ddf["fare"].mean().compute() pay_type_share = ddf["payment_type"].value_counts(normalize=True).compute() daily_trips = ddf.groupby(ddf["trip_start_timestamp"].dt.date)["trip_id"].count().compute()
- 首次读取完成后建议将文件转存为Parquet列式存储格式,80GB的CSV转成Parquet后体积约10GB左右,后续读取、过滤、聚合的速度会比CSV快5-10倍,一劳永逸,转存代码:
ddf.to_parquet("taxi_trips_store/"),后续直接读取该路径即可。
细粒度探索方案:分层抽样加载
- 对于需要观察明细分布、排查异常值、做特征关联分析的场景,不需要使用全量数据,在分块读取阶段按1%-5%的比例分层抽样即可,抽取出的样本量约800MB-4GB,完全可以用pandas直接加载完成常规EDA流程,抽样结果的统计偏差在EDA可接受范围内。
- 抽样参考代码:
sample_ratio = 0.02 sample_chunks = [] for chunk in pd.read_csv("taxi_trips.csv", chunksize=2_000_000, usecols=numeric_cols+cat_cols+date_cols+["trip_id"], parse_dates=date_cols): sample_chunks.append(chunk.sample(frac=sample_ratio)) sample_df = pd.concat(sample_chunks, ignore_index=True) # 后续可直接对sample_df使用常规pandas逻辑做探索、绘图
不要尝试强行加载全量80GB数据进内存,常规EDA99%的需求都可以通过增量统计、外存计算、抽样三种方式组合覆盖,不会出现内存不足、内核崩溃的问题。
内容的提问来源于stack exchange,提问作者Ashish Padhi
相关产品推荐
相关产品推荐

