获取Spark Dataframe列中列表的最后一个元素

时间:2018-08-10 18:42:10

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

我有一个具有以下架构的DataFrame

root
 |-- memberId: long (nullable = true)
 |-- items: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- timestamp: long (nullable = true)
 |    |    |-- itemId: integer (nullable = true)
 |    |    |-- weight: double (nullable = true)

说,DataFrame(称为df)看起来像这样。

+-----------+------------------------------------------------------------------------+
|memberId   |items                                                                   |
+-----------+------------------------------------------------------------------------+
|10000000001|[[1234567891, 104, 1.0], [1234567892, 103, 3.0]]                        |
|10000000002|[[1234567891, 103, 1.0], [1234567893, 102, 1.0], [1234567894, 101, 2.0]]|
+-----------+------------------------------------------------------------------------+

可以看出,dfmemberIdliststruct的映射。我想对其进行转换,以便检索与每个成员相对应的struct列表中的最后一个元素。因此,生成的DataFrame应该看起来像

+-----------+----------------------+
|memberId   |lastItem              |
+-----------+----------------------+
|10000000001|[1234567892, 103, 3.0]|
|10000000002|[1234567894, 101, 2.0]|
+-----------+----------------------+

我尝试过

val newDf = df
  .withColumn("lastItem", last($"items"))
  .drop("items")

但这会引发以下形式的异常:

grouping expressions sequence is empty, 
and '`memberId`' is not an aggregate function. 
Wrap '(last(`items`, false) AS `item`)' in 
windowing function(s) or wrap '`memberId`' in 
first() (or first_value) if you don't care which value you get

我之所以会这样,是因为lastaggregation的一个功能,要求我在调用.groupBy("memberId")之前先last

我该怎么做?在使用UDF时,不鼓励使用DataFrame,但是我找不到能够完成我打算做的工作的本地函数。

1 个答案:

答案 0 :(得分:2)

您可以在数组类型为apply的{​​{1}}方法上执行此操作,通过该方法可以访问数组元素:

Column

编辑:

要获得前n-1个项目,我将使用UDF:

val newDf = df
.withColumn("lastItem", $"items"(size($"items")-1))
.drop("items")

也许也可以使用纯DataFrame API来完成,但我认为这会相当复杂(例如,结合使用val sliceUDF = udf((arr:Seq[Row],from:Int,to:Int) => arr.slice(from,to).map{case Row(ts:Long,Id:Int,w:Double) => (ts,Id,w)}) val newDf = df .withColumn("subItems", sliceUDF($"items",lit(0),size($"items")-1)) .drop("items") ,window-function和posexplode