如何在Scala中的列中迭代对

时间:2018-01-11 03:35:22

标签: scala apache-spark dictionary apache-spark-sql reduce

我有一个这样的数据框,从镶木地板文件导入:

| Store_id | Date_d_id  | 
|    0     | 23-07-2017 |  
|    0     | 26-07-2017 |   
|    0     | 01-08-2017 |   
|    0     | 25-08-2017 |  
|    1     | 01-01-2016 |  
|    1     | 04-01-2016 |   
|    1     | 10-01-2016 |   

接下来我想要实现的目标是在每个客户的日期中成对并获得日差。这是它应该是什么样子:

| Store_id | Date_d_id  | Day_diff |
|    0     | 23-07-2017 |   null   |
|    0     | 26-07-2017 |    3     |
|    0     | 01-08-2017 |    6     |   
|    0     | 25-08-2017 |    24    |  
|    1     | 01-01-2016 |    null  |  
|    1     | 04-01-2016 |    3     |   
|    1     | 10-01-2016 |    6     | 

最后,我想将数据框架减少到客户的平均日差:

| Store_id | avg_diff |
|    0     | 7.75     |  
|    1     |  3       |

我是Scala的新手,我甚至不知道从哪里开始。任何帮助都非常感谢!提前谢谢。

另外,我正在使用Zeppelin笔记本

2 个答案:

答案 0 :(得分:0)

一种方法是使用lag(Date) over Window partition和UDF来计算连续行之间的天数差异,然后将DataFrame分组以获得平均天数差异。请注意,Date_d_id转换为yyyy-mm-dd格式,以便在Window分区中正确排序字符串:

import org.apache.spark.sql.functions._
import org.apache.spark.sql.expressions.Window

val df = Seq(
  (0, "23-07-2017"),
  (0, "26-07-2017"),
  (0, "01-08-2017"),
  (0, "25-08-2017"),
  (1, "01-01-2016"),
  (1, "04-01-2016"),
  (1, "10-01-2016")
).toDF("Store_id", "Date_d_id")

def daysDiff = udf(
  (d1: String, d2: String) => {
    import java.time.LocalDate 
    import java.time.temporal.ChronoUnit.DAYS

    DAYS.between(LocalDate.parse(d1), LocalDate.parse(d2))
  }
)

val df2 = df.
  withColumn( "Date_ymd",
    regexp_replace($"Date_d_id", """(\d+)-(\d+)-(\d+)""", "$3-$2-$1")).
  withColumn( "Prior_date_ymd",
    lag("Date_ymd", 1).over(Window.partitionBy("Store_id").orderBy("Date_ymd"))).
  withColumn( "Days_diff",
    when($"Prior_date_ymd".isNotNull, daysDiff($"Prior_date_ymd", $"Date_ymd")).
    otherwise(0L))

df2.show
// +--------+----------+----------+--------------+---------+
// |Store_id| Date_d_id|  Date_ymd|Prior_date_ymd|Days_diff|
// +--------+----------+----------+--------------+---------+
// |       1|01-01-2016|2016-01-01|          null|        0|
// |       1|04-01-2016|2016-01-04|    2016-01-01|        3|
// |       1|10-01-2016|2016-01-10|    2016-01-04|        6|
// |       0|23-07-2017|2017-07-23|          null|        0|
// |       0|26-07-2017|2017-07-26|    2017-07-23|        3|
// |       0|01-08-2017|2017-08-01|    2017-07-26|        6|
// |       0|25-08-2017|2017-08-25|    2017-08-01|       24|
// +--------+----------+----------+--------------+---------+

val resultDF = df2.groupBy("Store_id").agg(avg("Days_diff").as("Avg_diff"))

resultDF.show
// +--------+--------+
// |Store_id|Avg_diff|
// +--------+--------+
// |       1|     3.0|
// |       0|    8.25|
// +--------+--------+

答案 1 :(得分:0)

您可以使用lag函数获取Window函数之前的日期,然后进行一些操作以获得您需要的最终dataframe

首先需要将Date_d_id列转换为包含时间戳,以便排序正常工作

import org.apache.spark.sql.functions._
val timestapeddf = df.withColumn("Date_d_id", from_unixtime(unix_timestamp($"Date_d_id", "dd-MM-yyyy")))

应该将dataframe作为

+--------+-------------------+
|Store_id|          Date_d_id|
+--------+-------------------+
|       0|2017-07-23 00:00:00|
|       0|2017-07-26 00:00:00|
|       0|2017-08-01 00:00:00|
|       0|2017-08-25 00:00:00|
|       1|2016-01-01 00:00:00|
|       1|2016-01-04 00:00:00|
|       1|2016-01-10 00:00:00|
+--------+-------------------+

然后您可以在lag函数上应用window函数,最后得到日期差异为

import org.apache.spark.sql.expressions._
val windowSpec = Window.partitionBy("Store_id").orderBy("Date_d_id")
val laggeddf = timestapeddf.withColumn("Day_diff", when(lag("Date_d_id", 1).over(windowSpec).isNull, null).otherwise(datediff($"Date_d_id", lag("Date_d_id", 1).over(windowSpec))))

laggeddf应该是

+--------+-------------------+--------+
|Store_id|Date_d_id          |Day_diff|
+--------+-------------------+--------+
|0       |2017-07-23 00:00:00|null    |
|0       |2017-07-26 00:00:00|3       |
|0       |2017-08-01 00:00:00|6       |
|0       |2017-08-25 00:00:00|24      |
|1       |2016-01-01 00:00:00|null    |
|1       |2016-01-04 00:00:00|3       |
|1       |2016-01-10 00:00:00|6       |
+--------+-------------------+--------+

现在最后一步是使用groupByaggregation来查找平均值

laggeddf.groupBy("Store_id")
  .agg(avg("Day_diff").as("avg_diff"))

应该给你

+--------+--------+
|Store_id|avg_diff|
+--------+--------+
|       0|    11.0|
|       1|     4.5|
+--------+--------+

现在,如果您想忽略空Day_diff,那么您可以

laggeddf.groupBy("Store_id")
          .agg((sum("Day_diff")/count($"Day_diff".isNotNull)).as("avg_diff"))

应该给你

+--------+--------+
|Store_id|avg_diff|
+--------+--------+
|       0|    8.25|
|       1|     3.0|
+--------+--------+

我希望答案很有帮助