Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 带当前行条件的火花窗功能_Scala_Apache Spark_Apache Spark Sql - Fatal编程技术网

Scala 带当前行条件的火花窗功能

Scala 带当前行条件的火花窗功能,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我正在尝试计算一个给定的订单id在过去365天内有多少订单付款。这不是问题所在:我使用的是 让我感到棘手的是:我不想在当前订单id的订单日期之后的payment\u date时间窗口中计算订单 目前,我有这样的想法: val window: WindowSpec = Window .partitionBy("customer_id") .orderBy("order_date") .rangeBetween(-365*days, -1) +---------+-----------

我正在尝试计算一个给定的
订单id
在过去365天内有多少订单付款。这不是问题所在:我使用的是

让我感到棘手的是:我不想在当前订单id的
订单日期之后的
payment\u date
时间窗口中计算订单

目前,我有这样的想法:

val window: WindowSpec = Window
  .partitionBy("customer_id")
  .orderBy("order_date")
  .rangeBetween(-365*days, -1)
+---------+-----------+-------------+------------+-----------------+
|order_id |order_date |payment_date |customer_id |paid_order_count |
+---------+-----------+-------------+------------+-----------------+
|1        |2017-01-01 |2017-01-10   |A           |0                |
|2        |2017-02-01 |2017-02-10   |A           |1                |
|3        |2017-02-02 |2017-02-20   |A           |1                |

这将统计客户当前订单前365天内的所有订单

现在,我如何将当前订单的
订单\日期
纳入计数条件

例如:

+---------+-----------+-------------+------------+
|order_id |order_date |payment_date |customer_id |
+---------+-----------+-------------+------------+
|1        |2017-01-01 |2017-01-10   |A           |
|2        |2017-02-01 |2017-02-10   |A           |
|3        |2017-02-02 |2017-02-20   |A           |
生成的表应如下所示:

val window: WindowSpec = Window
  .partitionBy("customer_id")
  .orderBy("order_date")
  .rangeBetween(-365*days, -1)
+---------+-----------+-------------+------------+-----------------+
|order_id |order_date |payment_date |customer_id |paid_order_count |
+---------+-----------+-------------+------------+-----------------+
|1        |2017-01-01 |2017-01-10   |A           |0                |
|2        |2017-02-01 |2017-02-10   |A           |1                |
|3        |2017-02-02 |2017-02-20   |A           |1                |
对于
order\u id=3
而言,支付的
订单数量不应为
2
,而应为
1
,因为
order\u id=2
是在下单
order\u id=3
后支付的

我希望我能很好地解释我的问题,并期待你的想法

问题很好!!! 使用rangeBetween的两个注释创建了一个基于行数而非值的固定框架,因此在两种情况下会出现问题:

  • 客户并不是每天都有订单,所以365行窗口可能包含订单日期早于一年前的行
  • 若客户每天有一个以上的订单,这将影响一年的保险范围
  • 1和2的组合
  • 另外,rangeBetween不适用于日期和时间戳数据类型

    要解决此问题,可以使用带有列表和自定义项的窗口函数:

    import org.apache.spark.sql.functions._
    import org.apache.spark.sql.expressions.Window
    
      val df = spark.sparkContext.parallelize(Seq(
        (1, "2017-01-01", "2017-01-10", "A")
        , (2, "2017-02-01", "2017-02-10", "A")
        , (3, "2017-02-02", "2017-02-20", "A")
      )
      ).toDF("order_id", "order_date", "payment_date", "customer_id")
        .withColumn("order_date_ts", to_timestamp($"order_date", "yyyy-MM-dd").cast("long"))
        .withColumn("payment_date_ts", to_timestamp($"payment_date", "yyyy-MM-dd").cast("long"))
    
    //      df.printSchema()
    //      df.show(false)
    
      val window = Window.partitionBy("customer_id").orderBy("order_date_ts").rangeBetween(Window.unboundedPreceding, -1)
    
      val count_filtered_dates = udf( (days: Int, top: Long, array: Seq[Long]) => {
          val bottom = top - (days * 60 * 60 * 24).toLong // in spark timestamps are in secconds, calculating the date days ago
          array.count(v => v >= bottom && v < top)
        }
      )
    
      val res = df.withColumn("paid_orders", collect_list("payment_date_ts") over window)
          .withColumn("paid_order_count", count_filtered_dates(lit(365), $"order_date_ts", $"paid_orders"))
    
      res.show(false)
    
    将日期转换为以秒为单位的火花时间戳可以提高列表的内存效率


    这是最容易实现的代码,但不是最理想的,因为列表将占用一些内存,自定义UDAF将是最好的,但需要更多的编码,稍后可能会执行。如果每个客户有数千个订单,这仍然有效。

    那么您只需要根据付款日期计算每个订单日期的累积计数?