J'essaie de gérer les exceptions courantes dans Spark, comme une opération .map ne fonctionnant pas correctement sur tous les éléments des données ou une exception FileNotFound. J'ai lu toutes les questions existantes et les deux posts suivants:
https://www.nicolaferraro.me/2016/02/18/exception-handling-in-Apache-spark
J'ai essayé une instruction Try dans la ligne attributes => mHealthUser(attributes(0).toDouble, attributes(1).toDouble, attributes(2).toDouble
donc il se lit attributes => Try(mHealthUser(attributes(0).toDouble, attributes(1).toDouble, attributes(2).toDouble)
Mais ça ne compilera pas; le compilateur ne reconnaîtra pas l'instruction .toDF()
ultérieurement. J'ai également essayé un bloc Try {Catch {}} semblable à Java, mais je ne parviens pas à définir correctement la portée. df
n'est alors pas renvoyé. Est-ce que quelqu'un sait comment faire cela correctement? Dois-je même gérer ces exceptions, car la structure Spark semble traiter déjà une exception FileNotFound sans que j'en ajoute une. Mais je voudrais générer une erreur avec le nombre de champs du schéma si le fichier d'entrée a un nombre de colonnes incorrect, par exemple.
Voici le code:
object DataLoadTest extends SparkSessionWrapper {
/** Helper function to create a DataFrame from a textfile, re-used in subsequent tests */
def createDataFrame(fileName: String): DataFrame = {
import spark.implicits._
//try {
val df = spark.sparkContext
.textFile("/path/to/file" + fileName)
.map(_.split("\\t"))
//mHealth user is the case class which defines the data schema
.map(attributes => mHealthUser(attributes(0).toDouble, attributes(1).toDouble, attributes(2).toDouble,
attributes(3).toDouble, attributes(4).toDouble,
attributes(5).toDouble, attributes(6).toDouble, attributes(7).toDouble,
attributes(8).toDouble, attributes(9).toDouble, attributes(10).toDouble,
attributes(11).toDouble, attributes(12).toDouble, attributes(13).toDouble,
attributes(14).toDouble, attributes(15).toDouble, attributes(16).toDouble,
attributes(17).toDouble, attributes(18).toDouble, attributes(19).toDouble,
attributes(20).toDouble, attributes(21).toDouble, attributes(22).toDouble,
attributes(23).toInt))
.toDF()
.cache()
df
} catch {
case ex: FileNotFoundException => println(s"File $fileName not found")
case unknown: Exception => println(s"Unknown exception: $unknown")
}
}
Toutes les suggestions ont apprécié. Merci!
Une autre option serait d’utiliser Try en scala.
Par exemple:
def createDataFrame(fileName: String): Try[DataFrame] = {
try {
//create dataframe df
Success(df)
} catch {
case ex: FileNotFoundException => {
println(s"File $fileName not found")
Failure(ex)
}
case unknown: Exception => {
println(s"Unknown exception: $unknown")
Failure(unknown)
}
}
}
Maintenant, du côté de l'appelant, gérez cela comme suit:
createDataFrame("file1.csv") match {
case Success(df) => {
// proceed with your pipeline
}
case Failure(ex) => //handle exception
}
Ceci est légèrement mieux que d’utiliser Option car l’appelant connaitrait la raison de l’échec et pourrait mieux gérer.
Soit vous laissez l'exception rejetée par la méthode createDataFrame
(et la manipulez à l'extérieur), soit vous modifiez la signature pour renvoyer Option[DataFrame]
:
def createDataFrame(fileName: String): Option[DataFrame] = {
import spark.implicits._
try {
val df = spark.sparkContext
.textFile("/path/to/file" + fileName)
.map(_.split("\\t"))
//mHealth user is the case class which defines the data schema
.map(attributes => mHealthUser(attributes(0).toDouble, attributes(1).toDouble, attributes(2).toDouble,
attributes(3).toDouble, attributes(4).toDouble,
attributes(5).toDouble, attributes(6).toDouble, attributes(7).toDouble,
attributes(8).toDouble, attributes(9).toDouble, attributes(10).toDouble,
attributes(11).toDouble, attributes(12).toDouble, attributes(13).toDouble,
attributes(14).toDouble, attributes(15).toDouble, attributes(16).toDouble,
attributes(17).toDouble, attributes(18).toDouble, attributes(19).toDouble,
attributes(20).toDouble, attributes(21).toDouble, attributes(22).toDouble,
attributes(23).toInt))
.toDF()
.cache()
Some(df)
} catch {
case ex: FileNotFoundException => {
println(s"File $fileName not found")
None
}
case unknown: Exception => {
println(s"Unknown exception: $unknown")
None
}
}
}
EDIT: sur le côté appelant de createDataFrame, il existe plusieurs modèles. Si vous traitez plusieurs noms de fichiers, vous pouvez par exemple faire:
val dfs : Seq[DataFrame] = Seq("file1","file2","file3").map(createDataFrame).flatten
Si vous travaillez sur un seul nom de fichier, vous pouvez faire:
createDataFrame("file1.csv") match {
case Some(df) => {
// proceed with your pipeline
val df2 = df.filter($"activityLabel" > 0).withColumn("binaryLabel", when($"activityLabel".between(1, 3), 0).otherwise(1))
}
case None => println("could not create dataframe")
}
applique try et intercepte le bloc sur les colonnes de la structure de données:
(try{$"credit.amount"} catch{case e:Exception=> lit(0)}).as("credit_amount")