如何自动化实现按消费总额均等划分客户Decile(十分位)
问题描述
我有一张交易数据表,包含Customer_ID(客户ID)和order_cost(订单金额)字段,记录了数千条随机排序的交易数据:
| Customer_ID(客户ID) | order_cost(订单金额) |
|---|---|
| 1 | $503 |
| 53 | $7 |
| 4 | $80 |
| 13 | $76 |
| 6 | $270 |
| 78 | $2 |
| 8 | $45 |
| 910 | $89 |
| 10 | $3 |
| 1130 | $43 |
| etc... | etc... |
我需要按Customer_ID分组,聚合所有订单金额得到spending(消费总额)列,然后新增Decile(十分位)字段,为每个客户分配1-10的编号,要求每个Decile内所有客户的spending总和占总消费的10%。预期结果表结构如下:
| Customer_ID(客户ID) | spending(消费总额) | Decile(十分位) |
|---|---|---|
| 45 | $500 | 1 |
| 3 | $700 | 1 |
| 349 | $800 | 1 |
| 23 | $1,000 | 1 |
| 64 | $2,000 | 1 |
| 718 | $2,100 | 1 |
| 3452 | $2,300 | 1 |
| 1276 | $2,600 | 2 |
| 10 | $3,000 | 2 |
| 34 | $4,000 | 2 |
| etc... | etc... | etc... |
目前我已通过PySpark实现分组聚合,用ntile(5000)对客户按spending升序分区,再手动配置when语句划分Decile,但手动试错太耗时,想找自动化实现该均等十分位划分的方法。现有代码如下:
import pyspark.sql.functions as F import pyspark.sql.window as W deciles = (table .groupBy('Customer_ID') .agg(F.sum('order_cost').alias('spending')) .withColumn('rank', F.ntile(5000).over(W.Window.partitionBy().orderBy(F.asc('spending')))) .withColumn('rank', F.when(F.col('rank')<=4628, F.lit(1)) .when(F.col('rank')<=4850, F.lit(2)) .when(F.col('rank')<=4925, F.lit(3)) .when(F.col('rank')<=4965, F.lit(4)) .when(F.col('rank')<=4980, F.lit(5)) .when(F.col('rank')<=4987, F.lit(6)) .when(F.col('rank')<=4993, F.lit(7)) .when(F.col('rank')<=4997, F.lit(8)) .when(F.col('rank')<=4999, F.lit(9)) .when(F.col('rank')<=5000, F.lit(10)) .otherwise(F.lit(0))) ) end_table = (table.alias('a').join(deciles.alias('b'), ['Customer_ID'], 'left') .selectExpr('a.*', 'b.rank') )
自动化实现方案
要实现按消费总额占比划分均等十分位,核心是计算累计消费占比,再根据占比区间自动分配Decile编号,无需手动调整阈值。步骤如下:
- 预处理订单金额:移除
order_cost中的$和逗号,转换为数值类型,避免聚合时出错。 - 计算客户消费总额:按
Customer_ID分组求和得到spending。 - 计算全局总消费与累计占比:按消费降序排序(高消费客户优先),计算累计消费占总消费的比例。
- 自动分配Decile:根据累计占比区间直接映射到1-10的十分位编号。
完整代码如下:
import pyspark.sql.functions as F import pyspark.sql.window as W # 1. 清洗订单金额,转换为数值类型 clean_table = table.withColumn( 'order_cost_num', F.regexp_replace(F.col('order_cost'), '[$,]', '').cast('double') ) # 2. 计算每个客户的消费总额 customer_spending = clean_table.groupBy('Customer_ID').agg( F.sum('order_cost_num').alias('spending') ) # 3. 计算全局总消费,同时按消费降序计算累计消费及占比 window_spec = W.Window.orderBy(F.desc('spending')) total_spending = customer_spending.select(F.sum('spending').alias('total')).collect()[0]['total'] deciles_auto = customer_spending.withColumn( 'cumulative_spending', F.sum('spending').over(window_spec) ).withColumn( 'cumulative_percent', F.col('cumulative_spending') / total_spending ).withColumn( 'Decile', F.floor(F.col('cumulative_percent') * 10) + 1 # 0-10%→1,10-20%→2...90-100%→10 ) # 4. 关联回原表得到最终结果 end_table_auto = table.alias('a').join( deciles_auto.alias('b'), on='Customer_ID', how='left' ).select( 'a.*', 'b.spending', 'b.Decile' )
关键说明
- 预处理步骤确保金额为数值类型,避免字符串求和错误。
- 按消费降序排序是因为高消费客户数量少但贡献占比高,能保证每个Decile的总消费占比更接近10%。
- 累计占比乘以10后取整加1,自动完成区间映射,无需手动调整阈值。
- 若存在多个客户消费总额相同导致累计占比跨区间的情况,PySpark会将这些客户分到同一个Decile,保证分组合理性。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

