我正在使用DataFrames,哪些元素的架构类似于:
root
|-- NPAData: struct (nullable = true)
| |-- NPADetails: struct (nullable = true)
| | |-- location: string (nullable = true)
| | |-- manager: string (nullable = true)
| |-- service: array (nullable = true)
| | |-- element: struct (containsNull = true)
| | | |-- serviceName: string (nullable = true)
| | | |-- serviceCode: string (nullable = true)
|-- NPAHeader: struct (nullable = true)
| | |-- npaNumber: string (nullable = true)
| | |-- date: string (nullable = true)
在我的DataFrame中,我想对具有相同NPAHeader.code
的所有元素进行分组,所以为此我使用以下行:
val groupedNpa = orderedNpa.groupBy($"NPAHeader.code" ).agg(collect_list(struct($"NPAData",$"NPAHeader")).as("npa"))
在此之后,我有一个包含以下架构的数据框:
StructType(StructField(npaNumber,StringType,true), StructField(npa,ArrayType(StructType(StructField(NPAData...)))))
每一行的一个例子类似于:
[1234,WrappedArray([npaNew,npaOlder,...npaOldest])]
现在我想要的是生成另一个DataFrame,只拾取WrappedArray中的一个元素,所以我想要一个类似于的输出:
[1234,npaNew]
注意:WrappedArray中选择的元素是在迭代整个WrappedArray之后匹配Complext逻辑的元素。但是为了简化这个问题,我将总是拿起WrappedArray的最后一个元素(迭代后全部)。
为此,我想定义一个递归udf
import org.apache.spark.sql.functions.udf
def returnRow(elementList : Row)(index:Int): Row = {
val dif = elementList.size - index
val row :Row = dif match{
case 0 => elementList.getAs[Row](index)
case _ => returnRow(elementList)(index + 1)
}
row
}
val returnRow_udf = udf(returnRow _)
groupedNpa.map{row => (row.getAs[String]("npaNumber"),returnRow_udf(groupedNpa("npa")(0)))}
但我在地图中收到以下错误:
线程中的异常" main" java.lang.UnsupportedOperationException: 类型Int =>的模式单位不受支持
我做错了什么?
顺便说一句,我不确定我是否正确传递npa
列groupedNpa("npa")
。我正在访问WrappedArray作为一行,因为我不知道如何迭代Array[Row]
(数组[行]中不存在get(index)
方法)
答案 0 :(得分:1)
TL; DR 只需使用How to select the first row of each group?
中描述的方法之一如果要使用复杂逻辑,并返回Row
,可以跳过SQL API并使用groupByKey
:
val f: (String, Iterator[org.apache.spark.sql.Row]) => Row
val encoder: Encoder
df.groupByKey(_.getAs[String]("NPAHeader.code")).mapGroups(f)(encoder)
或更好:
val g: (Row, Row) => Row
df.groupByKey(_.getAs[String]("NPAHeader.code")).reduceGroups(g)
其中encoder
是有效的RowEncoder
(Encoder error while trying to map dataframe row to updated row)。
您的代码有多种错误:
groupBy
不保证值的顺序。所以:
orderBy(...).groupBy(....).agg(collect_list(...))
可以具有非确定性输出。如果你真的决定走这条路,你应该跳过orderBy
并明确地对收集的数组进行排序。
您无法将curried函数传递给udf
。你必须首先取消它,但它需要不同的参数顺序(见下面的例子)。
如果可以的话,这可能是调用它的正确方法(注意你省略了第二个参数):
returnRow_udf(groupedNpa("npa")(0))
更糟糕的是,您可以在map
内拨打电话,udfs
根本不适用。
udf
无法返回Row
。它必须返回external Scala type。
array<struct>
的外部代表是Seq[Row]
。您不能只用Row
替换它。可以使用apply
索引访问SQL数组:
df.select($"array"(size($"array") - 1))
但由于非确定性,这不是一种正确的方法。您可以申请sort_array
,但正如开头所指出的那样,有更有效的解决方案。
令人惊讶的递归并不那么重要。你可以设计这样的功能:
def size(i: Int=0)(xs: Seq[Any]): Int = xs match {
case Seq() => i
case null => i
case Seq(h, t @ _*) => size(i + 1)(t)
}
val size_ = udf(size() _)
它可以正常工作:
Seq((1, Seq("a", "b", "c"))).toDF("id", "array")
.select(size_($"array"))
虽然递归是一种矫枉过正,如果你可以迭代Seq
。