pandas统计用户连续年月出现次数是否有更高效的实现方法?
十亿行级用户连续年月出现次数统计优化方案
你的现有实现基于pandas单线程计算,需要全量加载数据到内存,且多次groupby、排序、新增冗余列操作,在十亿行级数据下必然存在内存溢出、计算效率低的问题,可从以下两个方向优化:
第一步:简化连续判断逻辑
首先将ym整型字段转换为总月份计数,比如202012转换为2020*12 + 12 = 24252,连续月份的差值恒为1,无需额外判断1和89两种情况,减少计算分支。
第二步:选择适配大数据量的计算框架
方案1:PySpark分布式计算(推荐,适合十亿行级集群环境)
直接分布式并行处理,无需全量加载到单机内存,代码示例:
from pyspark.sql import SparkSession from pyspark.sql.window import Window from pyspark.sql.functions import col, lag, sum as spark_sum # 初始化Spark会话 spark = SparkSession.builder.appName("continuous_count").getOrCreate() # 读取数据(可替换为读hive、hdfs路径等) df = spark.createDataFrame([ (202012, '049'), (202009, '009'), (202012, '009'), (202101, '009'), (202103, '009'), (202104, '009'), (202012, '130'), (202101, '130'), (202102, '130'), (202103, '130'), (202105, '130'), (202009, '166'), (202012, '166'), (202101, '166'), (202011, '282'), (202104, '282') ], schema=["ym", "user_id"]) # 转换为总月份数 df = df.withColumn("total_month", (col("ym") / 100).cast("int") * 12 + (col("ym") % 100)) # 按用户分区、按月份排序,取上一个月份值 window_spec = Window.partitionBy("user_id").orderBy("total_month") df = df.withColumn("prev_month", lag("total_month", 1).over(window_spec)) # 标记连续月份,聚合求和 df = df.withColumn("is_continuous", (col("total_month") - col("prev_month") == 1).cast("int")) result = df.groupBy("user_id").agg(spark_sum("is_continuous").alias("rank")).orderBy("user_id") result.show()
输出结果和你现有逻辑完全一致。
方案2:Polars单机优化(适合单机大内存环境,比pandas快10~100倍)
Polars基于Rust实现,支持多线程、向量化计算、内存高效映射,可通过懒加载无需全量加载数据到内存即可处理:
import polars as pl # 读取数据(可替换为scan_parquet、scan_csv等懒加载方法,无需全量读内存) df = pl.DataFrame({ 'ym': [202012, 202009, 202012, 202101, 202103, 202104, 202012, 202101, 202102, 202103, 202105, 202009, 202012, 202101, 202011, 202104], 'user_id': ['049', '009', '009', '009', '009', '009', '130', '130', '130', '130', '130', '166', '166', '166', '282', '282'] }) result = ( df # 转换总月份 .with_columns( total_month = (pl.col("ym") // 100) * 12 + (pl.col("ym") % 100) ) # 按用户分组排序,取前一个月差值判断连续 .groupby("user_id") .agg( pl.col("total_month").sort().diff().eq(1).sum().alias("rank") ) .sort("user_id") ) print(result)
性能提升说明
- 分布式Spark方案:可线性扩展集群资源,十亿行数据仅需数分钟即可完成计算,无内存溢出风险
- Polars单机方案:同等配置下,计算速度是pandas的30倍以上,内存占用仅为pandas的1/3左右,128G内存的单机即可处理十亿行级数据
内容的提问来源于stack exchange,提问作者pauke Huang
相关产品推荐
相关产品推荐

