web-dev-qa-db-fra.com

Comment définir un schéma pour un type personnalisé dans Spark SQL?

L'exemple de code suivant essaie de placer certains objets de cas dans une trame de données. Le code inclut la définition d'une hiérarchie d'objets de cas et une classe de cas utilisant ce trait:

import org.Apache.spark.{SparkContext, SparkConf}
import org.Apache.spark.sql.SQLContext

sealed trait Some
case object AType extends Some
case object BType extends Some

case class Data( name : String, t: Some)

object Example {
  def main(args: Array[String]) : Unit = {
    val conf = new SparkConf()
      .setAppName( "Example" )
      .setMaster( "local[*]")

    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)

    import sqlContext.implicits._

    val df = sc.parallelize( Seq( Data( "a", AType), Data( "b", BType) ), 4).toDF()
    df.show()
  }
}    

Lors de l'exécution du code, je rencontre malheureusement l'exception suivante:

Java.lang.UnsupportedOperationException: Schema for type Some is not supported

Des questions

  • Existe-t-il une possibilité d'ajouter ou de définir un schéma pour certains types (ici tapez Some)?
  • Existe-t-il une autre approche pour représenter ce type d'énumérations?
    • J'ai essayé d'utiliser Enumeration directement, mais aussi sans succès. (voir ci-dessous)

Code pour Enumeration:

object Some extends Enumeration {
  type Some = Value
  val AType, BType = Value
}

Merci d'avance. J'espère que la meilleure approche n'est pas d'utiliser des chaînes à la place.

26
Martin Senne

Spark 2.0.0 + :

UserDefinedType a été rendu privé dans Spark 2.0.0 et pour l'instant il n'a pas de remplacement convivial Dataset.

Voir: SPARK-14155 (Masquer UserDefinedType dans Spark 2.0)

La plupart du temps, le typage statique Dataset peut servir de remplacement. Il y a un Jira en attente SPARK-7768 pour rendre à nouveau l'API UDT publique avec la version cible 2.4.

Voir aussi Comment stocker des objets personnalisés dans un ensemble de données?

Spark <2.0.0

Existe-t-il une possibilité d'ajouter ou de définir un schéma pour certains types (ici tapez Some)?

Je suppose que la réponse dépend à quel point vous en avez besoin. Il semble qu'il soit possible de créer un UserDefinedType mais il nécessite un accès à DeveloperApi et n'est pas exactement simple ou bien documenté.

import org.Apache.spark.sql.types._

@SQLUserDefinedType(udt = classOf[SomeUDT])
sealed trait Some
case object AType extends Some
case object BType extends Some

class SomeUDT extends UserDefinedType[Some] {
  override def sqlType: DataType = IntegerType

  override def serialize(obj: Any) = {
    obj match {
      case AType => 0
      case BType => 1
    }
  }

  override def deserialize(datum: Any): Some = {
    datum match {
      case 0 => AType
      case 1 => BType
    }
  }

  override def userClass: Class[Some] = classOf[Some]
}

Vous devriez probablement remplacer également hashCode et equals.

Son homologue PySpark peut ressembler à ceci:

from enum import Enum, unique
from pyspark.sql.types import UserDefinedType, IntegerType

class SomeUDT(UserDefinedType):
    @classmethod
    def sqlType(self):
        return IntegerType()

    @classmethod
    def module(cls):
        return cls.__module__

    @classmethod 
    def scalaUDT(cls): # Required in Spark < 1.5
        return 'net.zero323.enum.SomeUDT'

    def serialize(self, obj):
        return obj.value

    def deserialize(self, datum):
        return {x.value: x for x in Some}[datum]

@unique
class Some(Enum):
    __UDT__ = SomeUDT()
    AType = 0
    BType = 1

Dans Spark <1.5 Python UDT nécessite une paire Scala UDT, mais il semble que ce ne soit plus le cas dans 1.5.

Pour un UDT simple comme vous pouvez utiliser des types simples (par exemple IntegerType au lieu de l'ensemble Struct).

22
zero323