【问题标题】:SparkSQL: conditional sum using two columnsSparkSQL:使用两列的条件求和
【发布时间】:2016-02-24 20:00:33
【问题描述】:

我希望你能帮助我解决这个问题。 我有一个DF如下:

val df = sc.parallelize(Seq(
  (1, "a", "2014-12-01", "2015-01-01", 100), 
  (2, "a", "2014-12-01", "2015-01-02", 150),
  (3, "a", "2014-12-01", "2015-01-03", 120), 
  (4, "b", "2015-12-15", "2015-01-01", 100)
)).toDF("id", "prodId", "dateIns", "dateTrans", "value")
.withColumn("dateIns", to_date($"dateIns")
.withColumn("dateTrans", to_date($"dateTrans"))

我很乐意做一个 groupBy prodId 并聚合 'value' 对由列 'dateIns' 和 'dateTrans' 之间的差异定义的日期范围求和。特别是,我想有一种方法来定义一个条件总和,它将上述列之间预定义的最大差异内的所有值相加。 IE。从 dateIns ('dateTrans' - 'dateIns'

spark 中是否有任何预定义的聚合函数允许进行条件求和?您是否建议开发一个 aggr。 UDF(如果是,有什么建议)? 我正在使用 pySpqrk,但也很高兴获得 Scala 解决方案。非常感谢!

【问题讨论】:

    标签: sql apache-spark aggregate-functions apache-spark-sql pyspark


    【解决方案1】:

    让你更有趣一点,所以窗口中有一些事件:

    val df = sc.parallelize(Seq(
      (1, "a", "2014-12-30", "2015-01-01", 100), 
      (2, "a", "2014-12-21", "2015-01-02", 150),
      (3, "a", "2014-12-10", "2015-01-03", 120), 
      (4, "b", "2014-12-05", "2015-01-01", 100)
    )).toDF("id", "prodId", "dateIns", "dateTrans", "value")
    .withColumn("dateIns", to_date($"dateIns"))
    .withColumn("dateTrans", to_date($"dateTrans"))
    

    你需要的或多或少是这样的:

    import org.apache.spark.sql.functions.{col, datediff, lit, sum}
    
    // Find difference in tens of days 
    val diff = (datediff(col("dateTrans"), col("dateIns")) / 10)
      .cast("integer") * 10
    
    val dfWithDiff = df.withColumn("diff", diff)
    
    val aggregated = dfWithDiff 
      .where((col("diff") < 30) && (col("diff") >= 0))
      .groupBy(col("prodId"), col("diff"))
      .agg(sum(col("value")))
    

    结果

    aggregated.show
    // +------+----+----------+
    // |prodId|diff|sum(value)|
    // +------+----+----------+
    // |     a|  20|       120|
    // |     b|  20|       100|
    // |     a|   0|       100|
    // |     a|  10|       150|
    // +------+----+----------+
    

    其中diff 是范围 (0 -> [0, 10), 10 -> [10, 20), ...) 的下限。如果您删除 val 并调整导入,这也适用于 PySpark。

    编辑(每列汇总):

    val exprs = Seq(0, 10,  20).map(x => sum(
      when(col("diff") === lit(x), col("value"))
        .otherwise(lit(0)))
        .alias(x.toString))
    
    dfWithDiff.groupBy(col("prodId")).agg(exprs.head, exprs.tail: _*).show
    
    // +------+---+---+---+
    // |prodId|  0| 10| 20|
    // +------+---+---+---+
    // |     a|100|150|120|
    // |     b|  0|  0|100|
    // +------+---+---+---+
    

    与 Python 等效:

    from pyspark.sql.functions import *
    
    def make_col(x):
       cnd = when(col("diff") == lit(x), col("value")).otherwise(lit(0))
       return sum(cnd).alias(str(x))
    
    exprs = [make_col(x) for x in range(0, 30, 10)]
    dfWithDiff.groupBy(col("prodId")).agg(*exprs).show()   
    
    ## +------+---+---+---+
    ## |prodId|  0| 10| 20|
    ## +------+---+---+---+
    ## |     a|100|150|120|
    ## |     b|  0|  0|100|
    ## +------+---+---+---+
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-11
      • 1970-01-01
      • 2018-04-13
      • 1970-01-01
      • 2021-12-29
      • 2022-01-07
      • 2018-04-14
      • 1970-01-01
      相关资源
      最近更新 更多