如何从两个数据帧中对列进行分组,然后在行之间应用聚合差异函数?

时间:2018-04-17 11:52:21

标签: scala apache-spark apache-spark-sql spark-dataframe

我有两个数据帧如下:

+--------+----------+------+-------------------+
|readerId|locationId|userId|          timestamp|
+--------+----------+------+-------------------+
|      R2|        l1|    u2|2018-04-12 05:00:00|
|      R1|        l1|    u1|2018-04-12 05:00:00|
|      R3|        l3|    u3|2018-04-12 05:00:00|
+--------+----------+------+-------------------+

+--------+----------+------+-------------------+
|readerId|locationId|userId|          timestamp|
+--------+----------+------+-------------------+
|      R1|        l1|    u1|2018-04-12 07:00:00|
|      R2|        l1|    u2|2018-04-12 10:00:00|
|      R3|        l3|    u3|2018-04-12 07:00:00|
+--------+----------+------+-------------------+

我想对readerIdlocationId进行分组,然后找到分组值的时间戳差异。例如:对于readerID R1,locationID l1,时间戳差异为2小时。

我通过加入两个数据框并使用withColumn来实现它。

val joinedDf = asKuduDf.join(
        asOutToInDf,
        col("kdf.locationId") <=> col("outInDf.locationId") &&
          (col("kdf.readerId") <=> col("outInDf.readerId")),
        "inner")
      //Time loged in calculation
      val timestampDf = joinedDf.withColumn(
        "totalTime",
        ((unix_timestamp($"outInDf.timestamp") -
          unix_timestamp($"kdf.timestamp"))/60).cast("long")
      ).toDF()

有更好的方法吗?我也尝试过以下方式。

val unionDf = outToInDf.union(kuduDf)
val timeDiffDf = unionDf.groupBy($"readerId", $"locationId").agg(diff($"timestamp"))

但上述方法的问题在于没有'diff'功能。

2 个答案:

答案 0 :(得分:2)

join是正确的解决方案。一般而言,GROUP BY聚合不是一种选择,特别是如果(readerIdlocationId)不是唯一标识符。

你可以

unionDf
  .groupBy($"readerId", $"locationId")
  .agg((max($"timestamp").cast("long") - min($"timestamp").cast(long) / 60).alias("diff"))

但这是一种高度人为的解决方案,与join相比没有任何优势。它对一些微妙的数据问题也很敏感。

答案 1 :(得分:1)

您可以将两个数据框与union合并,在聚合中,您可以将差异计算为

val mergedDF = asKuduDf.union(asOutToInDf)
  .groupBy($"readerId", $"locationId")
  .agg(collect_list($"timestamp").as("time"))

mergedDF.withColumn("dif",
  abs(unix_timestamp($"time" (0)) - unix_timestamp($"time" (1))) / 60
)

输出:

+--------+----------+------------------------------------------+-----+
|readerId|locationId|time                                      |dif  |
+--------+----------+------------------------------------------+-----+
|R3      |l3        |[2018-04-12 05:00:00, 2018-04-12 07:00:00]|120.0|
|R2      |l1        |[2018-04-12 05:00:00, 2018-04-12 10:00:00]|300.0|
|R1      |l1        |[2018-04-12 05:00:00, 2018-04-12 07:00:00]|120.0|
+--------+----------+------------------------------------------+-----+

希望这有帮助!