web-dev-qa-db-ja.com

ScalaおよびSpark UDF関数

単純なUDFを作成して、sparkのtemptablの時間フィールドからいくつかの値を変換または抽出しました。関数を登録しますが、sqlを使用して関数を呼び出すと、NullPointerExceptionがスローされます。以下は私の機能とそれを実行するプロセスです。ツェッペリンを使用しています。これは昨日うまくいっていたが、今朝は動かなくなった。

関数

def convert( time:String ) : String = {
  val sdf = new Java.text.SimpleDateFormat("HH:mm")
  val time1 = sdf.parse(time)
  return sdf.format(time1)
}

関数を登録する

sqlContext.udf.register("convert",convert _)

SQLなしで関数をテストする-これは機能します

convert(12:12:12) -> returns 12:12

ZeppelinでSQLを使用して関数をテストすると、これは失敗します。

%sql
select convert(time) from temptable limit 10

誘惑の構造

root
 |-- date: string (nullable = true)
 |-- time: string (nullable = true)
 |-- serverip: string (nullable = true)
 |-- request: string (nullable = true)
 |-- resource: string (nullable = true)
 |-- protocol: integer (nullable = true)
 |-- sourceip: string (nullable = true)

私が取得しているスタックトレースの一部。

Java.lang.NullPointerException
    at org.Apache.hadoop.Hive.ql.exec.FunctionRegistry.getFunctionInfo(FunctionRegistry.Java:643)
    at org.Apache.hadoop.Hive.ql.exec.FunctionRegistry.getFunctionInfo(FunctionRegistry.Java:652)
    at org.Apache.spark.sql.Hive.HiveFunctionRegistry.lookupFunction(hiveUdfs.scala:54)
    at org.Apache.spark.sql.Hive.HiveContext$$anon$3.org$Apache$spark$sql$catalyst$analysis$OverrideFunctionRegistry$$super$lookupFunction(HiveContext.scala:376)
    at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$$anonfun$lookupFunction$2.apply(FunctionRegistry.scala:44)
    at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$$anonfun$lookupFunction$2.apply(FunctionRegistry.scala:44)
    at scala.Option.getOrElse(Option.scala:120)
    at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$class.lookupFunction(FunctionRegistry.scala:44)
10
fanbondi

関数を直接定義する代わりにudfを使用する

import org.Apache.spark.sql.functions._

val convert = udf[String, String](time => {
        val sdf = new Java.text.SimpleDateFormat("HH:mm")
        val time1 = sdf.parse(time)
        sdf.format(time1)
    }
)

Udfの入力パラメーターは、列(または列)です。そして、戻り値の型は列です。

case class UserDefinedFunction protected[sql] (
    f: AnyRef,
    dataType: DataType,
    inputTypes: Option[Seq[DataType]]) {

  def apply(exprs: Column*): Column = {
    Column(ScalaUDF(f, dataType, exprs.map(_.expr), inputTypes.getOrElse(Nil)))
  }
}
13
Rockie Yang

関数をUDFとして定義する必要があります。

import org.Apache.spark.sql.expressions.UserDefinedFunction
import org.Apache.spark.sql.functions.udf

val convertUDF: UserDefinedFunction = udf((time:String) => {
  val sdf = new Java.text.SimpleDateFormat("HH:mm")
  val time1 = sdf.parse(time)
  sdf.format(time1)
})

次に、UDFをDataFrameに適用します。

// assuming your DataFrame is already defined
dataFrame.withColumn("time", convertUDF(col("time"))) // using the same name replaces existing

さて、実際の問題に関しては、このエラーを受け取る1つの理由は、DataFrameにnullである行が含まれていることが原因である可能性があります。 UDFを適用する前にそれらを除外すると、問題なく続行できるはずです。

dataFrame.filter(col("time").isNotNull)

Nullに遭遇する以外にUDFを実行するときにNullPointerExceptionが発生する原因は何でしょうか。私の提案とは異なる理由が見つかった場合は、喜んでお知らせします。

2
kfkhalili