Scala Spark-递归函数,因为udf生成异常
我正在使用DataFrames,这些元素的模式类似于:Scala Spark-递归函数,因为udf生成异常,scala,apache-spark,recursion,apache-spark-sql,Scala,Apache Spark,Recursion,Apache Spark Sql,我正在使用DataFrames,这些元素的模式类似于: root |-- NPAData: struct (nullable = true) | |-- NPADetails: struct (nullable = true) | | |-- location: string (nullable = true) | | |-- manager: string (nullable = true) | |-- service: array (nullab
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)
[1234,WrappedArray([npaNew,npaOlder,...npaOldest])]
[1234,npaNew]
在我的数据框中,我想对具有相同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...)))))
每行的示例类似于:
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)
[1234,WrappedArray([npaNew,npaOlder,...npaOldest])]
[1234,npaNew]
现在我想要的是生成另一个数据帧,只拾取WrappedArray中的一个元素,因此我想要一个类似以下的输出:
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)
[1234,WrappedArray([npaNew,npaOlder,...npaOldest])]
[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=>Unit类型的架构
我做错了什么
另一方面,我不确定我是否正确传递了
groupedNpa(“npa”)
中的npa
列。我将WrappedArray作为一行,因为我不知道如何迭代Array[Row]
(Array[Row]中不存在get(index)
方法)TL;DR只需使用中描述的方法之一
如果要使用复杂逻辑并返回行
,可以跳过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)
其中编码器
是有效的行编码器
()
您的代码有多种错误:
不保证值的顺序。因此:groupBy
可以具有非确定性输出。如果您真的决定走这条路线,您应该跳过orderBy,显式地对收集的数组进行排序orderBy(...).groupBy(....).agg(collect_list(...))
- 不能将当前函数传递给
。您必须首先取消对它的修剪,但它需要不同的参数顺序(请参见下面的示例)udf
- 如果可以,这可能是调用它的正确方法(请注意,您省略了第二个参数):
更糟糕的是,您在
中调用它,其中map
根本不适用udf
无法返回udf
。它必须返回行
的外部表示形式是数组
。不能仅用Seq[Row]
替换它行
- 可以使用
通过索引访问SQL数组:apply
但由于非决定论的原因,它不是一种正确的方法。您可以应用df.select($"array"(size($"array") - 1))
,但正如一开始所指出的,还有更有效的解决方案sort\u 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