Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala Spark-递归函数,因为udf生成异常_Scala_Apache Spark_Recursion_Apache Spark Sql - Fatal编程技术网

Scala Spark-递归函数,因为udf生成异常

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

我正在使用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)
[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(...).groupBy(....).agg(collect_list(...))
    
    可以具有非确定性输出。如果您真的决定走这条路线,您应该跳过orderBy,显式地对收集的数组进行排序

  • 不能将当前函数传递给
    udf
    。您必须首先取消对它的修剪,但它需要不同的参数顺序(请参见下面的示例)

  • 如果可以,这可能是调用它的正确方法(请注意,您省略了第二个参数):

    更糟糕的是,您在
    map
    中调用它,其中
    udf
    根本不适用

  • udf
    无法返回
    。它必须返回

  • 数组
    的外部表示形式是
    Seq[Row]
    。不能仅用
    替换它
  • 可以使用
    apply
    通过索引访问SQL数组:

    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


我尝试按照您的指示尽可能多地更改代码,但我被迫采用一些原始方法,因为我需要运行此程序的服务器有Spark 1.6,据我所知,groupByKey、mapGroups和reduceGroups将大大简化我的生活,无法在该版本中使用。这是一个新版本,以防您需要检查。我做了几次更新,这就是为什么我喜欢问一个不同的问题,而不是一次又一次地更新这里。我在Window()中添加了partitionBy和orderBy,以避免您指出的问题。我不使用udf,而是使用一个“normal”函数,希望我能够从映射中调用它,这样我就不会在返回类型中受到限制。我知道RDD可以做到这一点,但我对DF并不乐观。