获取Spark 2.1.1中窗口的最后一个元素

时间:2017-07-06 13:20:48

标签: scala apache-spark apache-spark-sql window-functions

我有一个数据框,其中我有子类别,并且想要每个子类别的最后一个元素。

val windowSpec = Window.partitionBy("name").orderBy("count")
sqlContext
    .createDataFrame(
      Seq[(String, Int)](
        ("A", 1),
        ("A", 2),
        ("A", 3),
        ("B", 10),
        ("B", 20),
        ("B", 30)
      ))
    .toDF("name", "count")
    .withColumn("firstCountOfName", first("count").over(windowSpec))
    .withColumn("lastCountOfName", last("count").over(windowSpec))
    .show()

给我一​​些奇怪的信息:

+----+-----+----------------+---------------+                                   
|name|count|firstCountOfName|lastCountOfName|
+----+-----+----------------+---------------+
|   B|   10|              10|             10|
|   B|   20|              10|             20|
|   B|   30|              10|             30|
|   A|    1|               1|              1|
|   A|    2|               1|              2|
|   A|    3|               1|              3|
+----+-----+----------------+---------------+

正如我们所看到的,返回的first值已正确计算,但last不是,它始终是列的当前值。

有人有解决方案可以做我想要的吗?

2 个答案:

答案 0 :(得分:5)

根据问题SPARK-20969,您应该能够通过定义窗口的足够边界来获得预期结果,如下所示。

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

val windowSpec = Window
  .partitionBy("name")
  .orderBy("count")
  .rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)

sqlContext
  .createDataFrame(
    Seq[(String, Int)](
      ("A", 1),
      ("A", 2),
      ("A", 3),
      ("B", 10),
      ("B", 20),
      ("B", 30)
    ))
  .toDF("name", "count")
  .withColumn("firstCountOfName", first("count").over(windowSpec))
  .withColumn("lastCountOfName", last("count").over(windowSpec))
  .show()

或者,如果您要在同一列上进行排序,那么您可以使用非有序窗口更改minmax,然后它也可以正常运行。< / p>

答案 1 :(得分:0)

另一种方法是使用groupby ad join计算第一个和最后一个值

val data = spark
  .createDataFrame(
    Seq[(String, Int)](
      ("A", 1),
      ("A", 2),
      ("A", 3),
      ("B", 10),
      ("B", 20),
      ("B", 30)
    ))
  .toDF("name", "count")


val firstLast = data.groupBy("name").agg(first("count").as("firstCountOfName"), last("count").as("lastCountOfName"))

val result = data.join(firstLast, Seq("name"), "left")

result.show()

输出:

+----+-----+----------------+---------------+
|name|count|firstCountOfName|lastCountOfName|
+----+-----+----------------+---------------+
|   A|    1|               1|              3|
|   A|    2|               1|              3|
|   A|    3|               1|              3|
|   B|   10|              10|             30|
|   B|   20|              10|             30|
|   B|   30|              10|             30|
+----+-----+----------------+---------------+

希望这有帮助