使用窗口对scala 2中具有if条件的行进行计数

使用窗口对scala 2中具有if条件的行进行计数,scala,apache-spark,Scala,Apache Spark,我已经发布了一个类似的问题,但是有人给了我一个技巧来避免使用“if条件” 在这里,我处于一个类似的位置,我没有找到任何技巧来避免它 我有一个数据帧 var df = sc.parallelize(Array( (1, "2017-06-29 10:53:53.0","2017-06-25 14:60:53.0","boulanger.fr"), (2, "2017-07-05 10:48:57.0","2017-09-05 08:60:53.0","patissier.fr"), (3,

我已经发布了一个类似的问题,但是有人给了我一个技巧来避免使用“if条件”

在这里,我处于一个类似的位置,我没有找到任何技巧来避免它

我有一个数据帧

var df = sc.parallelize(Array(
(1,  "2017-06-29 10:53:53.0","2017-06-25 14:60:53.0","boulanger.fr"), 
(2,  "2017-07-05 10:48:57.0","2017-09-05 08:60:53.0","patissier.fr"), 
(3,  "2017-06-28 10:31:42.0","2017-02-28 20:31:42.0","boulanger.fr"), 
(4,  "2017-08-21 17:31:12.0","2017-10-21 10:29:12.0","patissier.fr"), 
(5,  "2017-07-28 11:22:42.0","2017-05-28 11:22:42.0","boulanger.fr"), 
(6,  "2017-08-23 17:03:43.0","2017-07-23 09:03:43.0","patissier.fr"), 
(7,  "2017-08-24 16:08:07.0","2017-08-22 16:08:07.0","boulanger.fr"), 
(8,  "2017-08-31 17:20:43.0","2017-05-22 17:05:43.0","patissier.fr"), 
(9,  "2017-09-04 14:35:38.0","2017-07-04 07:30:25.0","boulanger.fr"), 
(10, "2017-09-07 15:10:34.0","2017-07-29 12:10:34.0","patissier.fr"))).toDF("id", "date1","date2", "mail")

df = df.withColumn("date1", (unix_timestamp($"date1", "yyyy-MM-dd HH:mm:ss").cast("timestamp")))
df = df.withColumn("date2", (unix_timestamp($"date2", "yyyy-MM-dd HH:mm:ss").cast("timestamp")))

df = df.orderBy("date1", "date2")
它看起来像:

+---+---------------------+---------------------+------------+
|id |date1                |date2                |mail        |
+---+---------------------+---------------------+------------+
|3  |2017-06-28 10:31:42.0|2017-02-28 20:31:42.0|boulanger.fr|
|1  |2017-06-29 10:53:53.0|2017-06-25 15:00:53.0|boulanger.fr|
|2  |2017-07-05 10:48:57.0|2017-09-05 09:00:53.0|patissier.fr|
|5  |2017-07-28 11:22:42.0|2017-05-28 11:22:42.0|boulanger.fr|
|4  |2017-08-21 17:31:12.0|2017-10-21 10:29:12.0|patissier.fr|
|6  |2017-08-23 17:03:43.0|2017-07-23 09:03:43.0|patissier.fr|
|7  |2017-08-24 16:08:07.0|2017-08-22 16:08:07.0|boulanger.fr|
|8  |2017-08-31 17:20:43.0|2017-05-22 17:05:43.0|patissier.fr|
|9  |2017-09-04 14:35:38.0|2017-07-04 07:30:25.0|boulanger.fr|
|10 |2017-09-07 15:10:34.0|2017-07-29 12:10:34.0|patissier.fr|
+---+---------------------+---------------------+------------+
对于每个id,我想在所有其他行中计算具有以下内容的行数:

  • [我的当前日期1-60天,我的当前日期1-1天]中的日期1
  • 日期2<我的当前日期1
  • 与我当前的\u邮件相同的邮件
  • 如果我看第5行,我想返回带有以下内容的行数:

  • 日期1于[2017-05-29 11:22:42.012017-07-27 11:22:42.0]
  • 日期2<2017-07-28 11:22:42.0
  • mail=boulanger.fr
  • -->结果将是2(对应于id 1和id 3)

    所以我想做一些类似的事情:

    val w = Window.partitionBy("mail").orderBy(col("date1").cast("long")).rangeBetween(-60*24*60*60,-1*24*60*60)
    var df= df.withColumn("all_previous", count("mail") over w)
    

    但这将对条件1和条件3作出反应,但不会对第二个条件作出反应。。。我必须添加一些东西来包含第二个条件,将date2与我的_date1进行比较…

    使用一个通用的窗口规范,
    last(date1)
    是每个窗口分区的当前
    date1
    ,以及一个超过0和1的
    和作为条件计数,以下是我如何将您的条件#2纳入计数标准:

    import org.apache.spark.sql.functions._
    import org.apache.spark.sql.expressions.Window
    
    def days(n: Long): Long = n * 24 * 60 * 60
    
    val w = Window.partitionBy("mail").orderBy($"date1".cast("long"))
    val w1 = w.rangeBetween(days(-60), days(0))
    val w2 = w.rangeBetween(days(-60), days(-1))
    
    df.withColumn("all_previous", sum(
          when($"date2".cast("long") < last($"date1").over(w1).cast("long"), 1).
            otherwise(0)
        ).over(w2)
      ).na.fill(0).
      show
    // +---+-------------------+-------------------+------------+------------+
    // | id|              date1|              date2|        mail|all_previous|
    // +---+-------------------+-------------------+------------+------------+
    // |  3|2017-06-28 10:31:42|2017-02-28 20:31:42|boulanger.fr|           0|
    // |  1|2017-06-29 10:53:53|2017-06-25 15:00:53|boulanger.fr|           1|
    // |  5|2017-07-28 11:22:42|2017-05-28 11:22:42|boulanger.fr|           2|
    // |  7|2017-08-24 16:08:07|2017-08-22 16:08:07|boulanger.fr|           3|
    // |  9|2017-09-04 14:35:38|2017-07-04 07:30:25|boulanger.fr|           2|
    // |  2|2017-07-05 10:48:57|2017-09-05 09:00:53|patissier.fr|           0|
    // |  4|2017-08-21 17:31:12|2017-10-21 10:29:12|patissier.fr|           0|
    // |  6|2017-08-23 17:03:43|2017-07-23 09:03:43|patissier.fr|           0|
    // |  8|2017-08-31 17:20:43|2017-05-22 17:05:43|patissier.fr|           1|
    // | 10|2017-09-07 15:10:34|2017-07-29 12:10:34|patissier.fr|           2|
    // +---+-------------------+-------------------+------------+------------+
    
    import org.apache.spark.sql.functions_
    导入org.apache.spark.sql.expressions.Window
    定义天数(n:长):长=n*24*60*60
    val w=Window.partitionBy(“邮件”).orderBy($“date1.cast(“long”))
    val w1=w.范围介于(天(-60),天(0))
    val w2=w.范围介于(天(-60),天(-1))
    df.带列(“所有之前”,总和(
    当($“date2.cast”(“long”)
    [更新]


    此解决方案不正确,即使示例数据集的结果似乎正确。特别是,
    last($“date1”)。over(w1)
    没有按预期的方式工作。答案有望成为有效解决方案的线索。

    @Biswanath如果你有这个窍门的话…:-)我想也许自定义聚合函数可以解决您的问题。但是自定义聚合是无状态的。它们没有保持在第二种情况下计算所需的状态。我不理解2窗口的用法。我想做的是:
    val w2=Window.partitionBy(“mail”).orderBy($“date1.cast”(“long”).rangeBetween(days(-60),days(-1))var df2=df.withColumn(“all_previous”),sum(when($“date2.cast”(“long”)
    有人理解它为什么不起作用吗?这里是我通过这样做得到的错误信息:
    org.apache.spark.sql.catalyst.trees.TreeNode.foreach(TreeNode.scala:118)org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$foreach$1.apply(TreeNode.scala:119)在org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$foreach$1.apply(TreeNode.scala:119)的scala.collection.immutable.List.foreach(List.scala:381)
    @Anneso,我看到两个问题:1。您的
    sum()
    覆盖了整个数据帧,2
    last()
    范围内
    -受限窗口将不包括“当前行”。谢谢,这对我来说更清楚了。然而,我得到了一个似乎不起作用的例子。。。我把它作为一个新的答案,以便有机会以一种可读的方式来做它(评论并不容易:)@anne所以,我的解决方案是不正确的。我在这一点上没有有效的解决方案,因此您可能想要取消接受答案。