web-dev-qa-db-fra.com

Pourquoi "Impossible de trouver le codeur pour le type stocké dans un jeu de données" lors de la création d'un jeu de données de classe de cas personnalisé?

Spark 2.0 (final) avec Scala 2.11.8. Le code super simple suivant génère l'erreur de compilation Error:(17, 45) Unable to find encoder for type stored in a Dataset. Primitive types (Int, String, etc) and Product types (case classes) are supported by importing spark.implicits._ Support for serializing other types will be added in future releases.

import org.Apache.spark.sql.SparkSession

case class SimpleTuple(id: Int, desc: String)

object DatasetTest {
  val dataList = List(
    SimpleTuple(5, "abc"),
    SimpleTuple(6, "bcd")
  )

  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder.
      master("local")
      .appName("example")
      .getOrCreate()

    val dataset = sparkSession.createDataset(dataList)
  }
}
52
clay

Spark Datasets nécessite Encoders pour le type de données sur le point d'être stocké. Pour les types courants (atomes, types de produits), il existe un certain nombre de codeurs prédéfinis mais vous devez d'abord les importer de SparkSession.implicits pour le faire fonctionner:

val sparkSession: SparkSession = ???
import sparkSession.implicits._
val dataset = sparkSession.createDataset(dataList)

Sinon, vous pouvez fournir directement un formulaire explicite

import org.Apache.spark.sql.{Encoder, Encoders}

val dataset = sparkSession.createDataset(dataList)(Encoders.product[SimpleTuple])

ou implicite

implicit val enc: Encoder[SimpleTuple] = Encoders.product[SimpleTuple]
val dataset = sparkSession.createDataset(dataList)

Encoder pour le type stocké.

Notez que Enocders fournit également un certain nombre de Encoders prédéfinis pour les types atomiques et que Encoders pour les complexes, peuvent être dérivés avec ExpressionEncoder =.

Lectures complémentaires:

75
zero323

Pour les autres utilisateurs (le vôtre est correct), notez qu'il est également important que le case class est défini en dehors de la portée de object. Alors:

Échoue:

object DatasetTest {
  case class SimpleTuple(id: Int, desc: String)

  val dataList = List(
    SimpleTuple(5, "abc"),
    SimpleTuple(6, "bcd")
  )

  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder
      .master("local")
      .appName("example")
      .getOrCreate()
    val dataset = sparkSession.createDataset(dataList)
  }
}

Ajouter les implicites, échoue toujours avec la même erreur:

object DatasetTest {
  case class SimpleTuple(id: Int, desc: String)

  val dataList = List(
    SimpleTuple(5, "abc"),
    SimpleTuple(6, "bcd")
  )

  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder
      .master("local")
      .appName("example")
      .getOrCreate()

    import sparkSession.implicits._
    val dataset = sparkSession.createDataset(dataList)
  }
}

Travaux:

case class SimpleTuple(id: Int, desc: String)

object DatasetTest {   
  val dataList = List(
    SimpleTuple(5, "abc"),
    SimpleTuple(6, "bcd")
  )

  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder
      .master("local")
      .appName("example")
      .getOrCreate()

    import sparkSession.implicits._
    val dataset = sparkSession.createDataset(dataList)
  }
}

Voici le bogue pertinent: https://issues.Apache.org/jira/browse/SPARK-1354 , donc, espérons-le, il sera corrigé dans la prochaine version de Spark = 2.

(Edit: on dirait que ce correctif est en fait dans Spark 2.0.0 ... Donc je ne suis pas sûr de savoir pourquoi cela échoue encore)).

44
MrProper