You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala DataFrame中按条件计算滚动求和

计算Scala DataFrame的条件滚动求和

我有如下Scala DataFrame:

+---+------+----------+-------+-----------+
|id |amount|date      |ndxdays|ndxdaysDate|
+---+------+----------+-------+-----------+
|1  |77    |2023-10-01|2      |2023-09-29 |
|1  |14    |2023-10-02|2      |2023-09-30 |
|1  |70    |2023-10-04|2      |2023-10-02 |
|1  |70    |2023-10-05|2      |2023-10-03 |
|1  |14    |2023-10-07|2      |2023-10-05 |
|2  |65    |2023-10-01|3      |2023-09-28 |
|2  |32    |2023-10-02|3      |2023-09-29 |
|2  |21    |2023-10-03|3      |2023-09-30 |
|2  |78    |2023-10-07|3      |2023-10-04 |
|3  |80    |2023-10-01|2      |2023-09-29 |
|3  |98    |2023-10-03|2      |2023-10-01 |
|3  |36    |2023-10-06|2      |2023-10-04 |
+---+------+----------+-------+-----------+

需求

为每一行计算滚动求和,仅纳入当前行ndxDaysDate小于该行date的amount值,即在遵守ndxdaysDate < date条件的前提下,计算同id下符合条件的amount累计和。

预期输出

+---+------+----------+-------+-----------+-----------+
|id |amount|date      |ndxdays|ndxdaysDate|rollingsum |
+---+------+----------+-------+-----------+-----------+
|1  |77    |2023-10-01|2      |2023-09-29 |       77  |<- 仅77符合:2023-10-01(date) > 2023-09-29(ndxdaysDate)(id=1)
|1  |14    |2023-10-02|2      |2023-09-30 |       91  |<- 14+77:2023-10-02和2023-10-01的date均大于2023-09-30(id=1)
|1  |70    |2023-10-04|2      |2023-10-02 |       70  |<- 仅70符合:2023-10-04(date) > 2023-10-02(ndxdaysDate)(id=1)
|1  |70    |2023-10-05|2      |2023-10-03 |       140 |<- 70+70:2023-10-05和2023-10-04的date均大于2023-10-03(id=1)
|1  |14    |2023-10-07|2      |2023-10-05 |       14  |<- 仅14符合:2023-10-07(date) > 2023-10-05(ndxdaysDate)(id=1)
|2  |65    |2023-10-01|3      |2023-09-28 |       65  |<- 仅65符合:2023-10-01(date) > 2023-09-28(ndxdaysDate)(id=2)
|2  |32    |2023-10-02|3      |2023-09-29 |       97  |<- 65+32:2023-10-02和2023-10-01的date均大于2023-09-29(id=2)
|2  |21    |2023-10-03|3      |2023-09-30 |       118 |<- 65+32+21:2023-10-03、2023-10-02和2023-10-01的date均大于2023-09-30(id=2)
|2  |78    |2023-10-07|3      |2023-10-04 |       78  |<- 仅78符合:2023-10-07(date) > 2023-10-04(ndxdaysDate)(id=2)
|3  |80    |2023-10-01|2      |2023-09-29 |       80  |<- 仅80符合:2023-10-01(date) > 2023-09-29(ndxdaysDate)(id=3)
|3  |98    |2023-10-03|2      |2023-10-01 |       98  |<- 仅98符合:2023-10-03(date) > 2023-10-01(ndxdaysDate)(id=3)
|3  |36    |2023-10-06|2      |2023-10-04 |       36  |<- 仅36符合:2023-10-06(date) > 2023-10-04(ndxdaysDate)(id=3)
+---+------+----------+-------+-----------+-----------+

解决方案

方法1:使用内置聚合函数(高效推荐)

先确保日期列是Date类型,再通过collect_list收集同id的所有日期与金额,过滤符合条件的记录后求和:

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.DateType

// 转换日期列(若原始为字符串类型)
val formattedDF = originalDF
  .withColumn("date", to_date(col("date"), "yyyy-MM-dd"))
  .withColumn("ndxdaysDate", to_date(col("ndxdaysDate"), "yyyy-MM-dd"))

// 计算条件滚动求和
val resultDF = formattedDF.withColumn(
  "rollingsum",
  expr("""
    aggregate(
      filter(
        collect_list(struct(date, amount)), 
        x -> x.date > ndxdaysDate
      ), 
      0, 
      (acc, x) -> acc + x.amount
    )
  """)
)

resultDF.show()

方法2:自连接分组求和(逻辑直观)

通过自连接关联同id的记录,过滤符合条件的行后分组求和:

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.DateType

// 转换日期列(若原始为字符串类型)
val formattedDF = originalDF
  .withColumn("date", to_date(col("date"), "yyyy-MM-dd"))
  .withColumn("ndxdaysDate", to_date(col("ndxdaysDate"), "yyyy-MM-dd"))

// 自连接+过滤+分组求和
val resultDF = formattedDF.alias("a")
  .join(formattedDF.alias("b"), col("a.id") === col("b.id"), "left")
  .filter(col("b.date") > col("a.ndxdaysDate"))
  .groupBy(
    col("a.id"), col("a.amount"), col("a.date"), 
    col("a.ndxdays"), col("a.ndxdaysDate")
  )
  .agg(sum("b.amount").alias("rollingsum"))
  .orderBy(col("a.id"), col("a.date"))

resultDF.show()

内容的提问来源于stack exchange,提问作者Dil1y_reddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:35:54