web-dev-qa-db-fra.com

Scala et Spark fonction UDF

J'ai fait un UDF simple pour convertir ou extraire certaines valeurs d'un champ temporel dans un tentablabl dans spark. J'enregistre la fonction, mais lorsque j'appelle la fonction à l'aide de sql, elle déclenche une exception NullPointerException. Voici ma fonction et le processus de son exécution. J'utilise Zeppelin. Étrangement, cela fonctionnait hier, mais il a cessé de fonctionner ce matin.

Fonction

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

Enregistrer la fonction

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

Testez la fonction sans SQL - Cela fonctionne

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

Testez la fonction avec SQL dans Zeppelin cette ÉCHEC.

%sql
select convert(time) from temptable limit 10

Structure de tentable

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)

Une partie du stacktrace que je reçois.

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

Utilisez udf au lieu de définir directement une fonction

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)
    }
)

Le paramètre d'entrée d'un udf est Colonne (ou Colonnes). Et le type de retour est Colonne.

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

Vous devez définir votre fonction comme 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)
})

Ensuite, vous appliqueriez votre UDF sur votre DataFrame.

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

Maintenant, en ce qui concerne votre problème réel, l'une des raisons pour lesquelles vous recevez cette erreur pourrait être parce que votre DataFrame contient des lignes qui sont nulles. Si vous les filtrez avant d'appliquer l'UDF, vous devriez pouvoir continuer sans problème.

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

Je suis curieux de savoir ce qui cause une exception NullPointerException lors de l'exécution d'un UDF autre que celui rencontrant un null, si vous avez trouvé une raison différente de ma suggestion, je serais heureux de le savoir.

2
kfkhalili