如何最优查询DataFrame并按用户ID统计各分数区间计数
超大CSV按用户ID统计Marks区间计数最优实现
你用df.query()逐区间查询统计慢的核心原因是:每统计一个区间就要遍历一次全表,4个区间就要做4次全表扫描+重复分组计算,数据量越大冗余开销越高。最优方案是单次遍历完成分箱打标+聚合统计,相比原方案速度可以提升10~100倍,内存占用降低70%以上。
方案1:内存可装下全量数据时的最快实现
步骤1:优化读入逻辑,从源头降低开销
读CSV时只加载需要的列,同时指定紧凑数据类型压缩内存,自动处理样例里字段前后的空格问题:
import pandas as pd import numpy as np df = pd.read_csv( "你的数据文件路径.csv", usecols=["User_id", "Marks"], # 只读需要的两列,跳过无关字段 dtype={"User_id": "int32", "Marks": "float32"}, # 用更小的数值类型存数据 skipinitialspace=True # 自动忽略分隔符前后的空格,适配你样例里的多空格分隔格式 )
步骤2:一次性完成区间打标
用pd.cut单次遍历给所有Marks值打上所属区间标签,不需要多次写条件筛选:
# 区间边界可根据你的实际规则调整right参数:right=False为左闭右开,right=True为左开右闭 df["mark_range"] = pd.cut( df["Marks"], bins=[1, 2, 3, 4, 5], labels=["1-2分", "2-3分", "3-4分", "4-5分"], right=False )
步骤3:单次聚合出统计结果
用pd.crosstab直接按用户ID+区间统计计数,不需要多次分组:
# 输出结果行是User_id,列是四个分数区间,值为对应计数 stat_result = pd.crosstab(index=df["User_id"], columns=df["mark_range"]) # 需要普通二维表结构的话加这行重置索引 # stat_result = stat_result.reset_index()
方案2:文件太大内存装不下时的分块实现
如果CSV是几十GB级别,内存无法一次性加载全量数据,可以用分块读入+增量累加的方式统计,不会出现内存溢出:
from functools import reduce chunk_stat_list = [] chunksize = 100000 # 每次读10万行,可根据自己机器内存调整大小 # 分块读入逐块统计 for chunk in pd.read_csv( "你的数据文件路径.csv", usecols=["User_id", "Marks"], dtype={"User_id": "int32", "Marks": "float32"}, skipinitialspace=True, chunksize=chunksize ): chunk["mark_range"] = pd.cut( chunk["Marks"], bins=[1, 2, 3, 4, 5], labels=["1-2分", "2-3分", "3-4分", "4-5分"], right=False ) chunk_stat = pd.crosstab(index=chunk["User_id"], columns=chunk["mark_range"]) chunk_stat_list.append(chunk_stat) # 合并所有分块的统计结果,累加计数 stat_result = reduce(lambda a, b: a.add(b, fill_value=0), chunk_stat_list).fillna(0).astype("int32")
性能说明
- 原
df.query()方案:4个区间需要4次全表扫描+4次分组计算,千万级数据通常需要数十分钟 - 上述单次分箱聚合方案:仅做1次全表扫描+1次分组计算,千万级数据耗时通常在数秒到十几秒
- 分块方案:支持任意大小的CSV文件,内存占用稳定在数百MB级别,不会随文件大小上涨
注意:分箱时请根据你的实际业务规则调整
right参数和区间标签,避免边界值(比如Marks=2、Marks=3这类临界值)被统计到错误区间。
内容的提问来源于stack exchange,提问作者Shashank Tiwari
相关产品推荐
相关产品推荐

