web-dev-qa-db-ja.com

Spark SQLでカスタムタイプのスキーマを定義するには?

次のサンプルコードは、いくつかのケースオブジェクトをデータフレームに配置しようとします。コードには、この特性を使用したケースオブジェクト階層とケースクラスの定義が含まれています。

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

コードを実行すると、残念ながら次の例外が発生します。

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

ご質問

  • 特定の型(ここではSome型)のスキーマを追加または定義する可能性はありますか?
  • この種の列挙を表す別のアプローチはありますか?
    • Enumerationを直接使用しようとしましたが、成功しませんでした。 (下記参照)

Enumerationのコード:

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

前もって感謝します。最善のアプローチは、代わりに文字列を使用しないことです。

26
Martin Senne

Spark 2.0.0 +

UserDefinedTypeはSpark 2.0.0で非公開になり、今のところDatasetに優しい代替物はありません。

参照: SPARK-14155(Spark 2.0)のUserDefinedTypeを非表示)

ほとんどの場合、静的に型指定されたDatasetは置換として機能します。保留中のJira SPARK-7768 があり、ターゲットバージョン2.4でUDT APIを再び公開します。

データセットにカスタムオブジェクトを保存する方法? も参照してください

スパーク<2.0.0

特定のタイプ(ここではタイプSome)のスキーマを追加または定義する可能性はありますか?

答えは、どれほどひどくこれを必要とするかによると思います。 UserDefinedTypeを作成することは可能に見えますが、DeveloperApiへのアクセスが必要であり、正確であるか、十分に文書化されていません。

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

おそらくhashCodeequalsもオーバーライドする必要があります。

PySparkのカウンターパートは次のようになります。

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

In Spark <1.5 Python UDTにはペアのScala UDTが必要ですが、それはもはやそうではないようです1.5。

単純なUDTの場合、単純な型を使用できます(たとえば、IntegerType全体ではなくStruct)。

22
zero323