Quand j'essaie de faire la même chose dans mon code comme mentionné ci-dessous
dataframe.map(row => {
val row1 = row.getAs[String](1)
val make = if (row1.toLowerCase == "tesla") "S" else row1
Row(row(0),make,row(2))
})
J'ai pris la référence ci-dessus à partir d'ici: Scala: Comment puis-je remplacer une valeur dans Dataframs à l'aide de scala Mais j'obtiens une erreur de codeur
Impossible de trouver le codeur pour le type stocké dans un jeu de données. Les types primitifs (Int, S tring, etc.) et les types de produit (classes de cas) sont pris en charge lors de l'importation de demandes spark.im._ La prise en charge de la sérialisation d'autres types sera ajoutée dans les prochaines versions.
Note: J'utilise spark 2.0!
Il n'y a rien d'inattendu ici. Vous essayez d'utiliser un code qui a été écrit avec Spark 1.x et n'est plus pris en charge dans Spark 2.0:
DataFrame.map
est ((Row) ⇒ T)(ClassTag[T]) ⇒ RDD[T]
Dataset[Row].map
est ((Row) ⇒ T)(Encoder[T]) ⇒ Dataset[T]
Pour être honnête, cela n'avait pas beaucoup de sens dans 1.x non plus. Indépendamment de la version, vous pouvez simplement utiliser DataFrame
API:
import org.Apache.spark.sql.functions.{when, lower}
val df = Seq(
(2012, "Tesla", "S"), (1997, "Ford", "E350"),
(2015, "Chevy", "Volt")
).toDF("year", "make", "model")
df.withColumn("make", when(lower($"make") === "tesla", "S").otherwise($"make"))
Si vous voulez vraiment utiliser map
, vous devez utiliser Dataset
de manière statique:
import spark.implicits._
case class Record(year: Int, make: String, model: String)
df.as[Record].map {
case tesla if tesla.make.toLowerCase == "tesla" => tesla.copy(make = "S")
case rec => rec
}
ou au moins retourner un objet qui aura un encodeur implicite:
df.map {
case Row(year: Int, make: String, model: String) =>
(year, if(make.toLowerCase == "tesla") "S" else make, model)
}
Enfin si pour certains complètement fo raison que vous voulez vraiment mapper sur Dataset[Row]
vous devez fournir le codeur requis:
import org.Apache.spark.sql.catalyst.encoders.RowEncoder
import org.Apache.spark.sql.types._
import org.Apache.spark.sql.Row
// Yup, it would be possible to reuse df.schema here
val schema = StructType(Seq(
StructField("year", IntegerType),
StructField("make", StringType),
StructField("model", StringType)
))
val encoder = RowEncoder(schema)
df.map {
case Row(year, make: String, model) if make.toLowerCase == "tesla" =>
Row(year, "S", model)
case row => row
} (encoder)
Pour le scénario où le schéma de la structure de données est connu à l'avance, la solution donnée par @ zero323 est la solution
mais pour les scénarios avec schéma dynamique/ou transfert de plusieurs images par données à une fonction générique: Le code suivant a fonctionné pour nous lors de la migration de 1.6.1 de 2.2.0
import org.Apache.spark.sql.Row
val df = Seq(
(2012, "Tesla", "S"), (1997, "Ford", "E350"),
(2015, "Chevy", "Volt")
).toDF("year", "make", "model")
val data = df.rdd.map(row => {
val row1 = row.getAs[String](1)
val make = if (row1.toLowerCase == "tesla") "S" else row1
Row(row(0),make,row(2))
})
ce code s'exécute sur les deux versions de spark.
inconvénient: l'optimisation fournie par spark sur les cadres de données/ensembles de données api ne sera pas appliquée.