Spark 2.0 Dataset vs DataFrame

à partir de spark 2.0.1 j'ai quelques questions. J'ai lu beaucoup de documentation mais jusqu'à présent je n'ai pas pu trouver de réponses suffisantes:

  • Quelle est la différence entre
    • df.select("foo")
    • df.select($"foo")
  • est-ce que je comprends bien que
    • myDataSet.map(foo.someVal) est typesafe et ne se convertira pas en RDD mais rester dans L'ensemble de données représentation / pas de charge supplémentaire (performance sage pour 2.0.0)
  • toutes les autres commandes, p.ex. select,.. sont tout sucre syntaxique. Ils ne sont pas typesafe et une carte pourrait être utilisée à la place. Comment pourrais-je taper df.select("foo") sans un énoncé de carte?
    • Pourquoi devrais-je utiliser un UDF / UADF au lieu d'une carte (en supposant que la carte reste dans la représentation de l'ensemble de données)?
18
demandé sur Community 2016-11-14 22:44:36

2 réponses

  1. la différence entre df.select("foo") et df.select($"foo") est une signature. Le premier prend au moins un String , le second zéro ou plus Columns . Il n'y a pas de différence pratique au-delà.
  2. myDataSet.map(foo.someVal) contrôles de type, mais comme toute Dataset opération utilise RDD d'objets, et par rapport à DataFrame opérations, Il ya un important au-dessus. Regardons un exemple simple:

    case class FooBar(foo: Int, bar: String)
    val ds = Seq(FooBar(1, "x")).toDS
    ds.map(_.foo).explain
    
    == Physical Plan ==
    *SerializeFromObject [input[0, int, true] AS value#123]
    +- *MapElements <function1>, obj#122: int
       +- *DeserializeToObject newInstance(class $line67.$read$$iw$$iw$FooBar), obj#121: $line67.$read$$iw$$iw$FooBar
          +- LocalTableScan [foo#117, bar#118]
    

    comme vous pouvez le voir ce plan d'exécution nécessite l'accès à tous les champs et doit DeserializeToObject .

  3. Pas de. En général, d'autres méthodes ne sont pas sucre syntaxique et générer un plan d'exécution sensiblement différent. Par exemple:

    ds.select($"foo").explain
    
    == Physical Plan ==
    LocalTableScan [foo#117]
    

    par rapport au plan affiché avant qu'elle puisse accéder directement à la colonne. Il n'est pas si une limitation de L'API mais le résultat d'une différence dans la sémantique opérationnelle.

  4. Comment ai-je pu df.sélectionnez ("foo") type-safe sans l'énoncé d'une carte?

    il n'y a pas une telle option. Tandis que les colonnes dactylographiées vous permettent de transformer statiquement Dataset en un autre statiquement typé Dataset :

    ds.select($"bar".as[Int])
    

    il n'y a pas de type sûr. Il y a quelques autres tente d'inclure des opérations optimisées de type sûr, comme agrégations typées , mais cette API expérimentale.

  5. pourquoi utiliser UDF / UADF au lieu d'une carte

    c'est entièrement à vous de décider. Chaque structure de données distribuées dans Spark fournit ses propres avantages et désavantages (voir par exemple UDAF Spark avec ArrayType comme bufferSchema performance les questions de ).

personnellement, je trouve statiquement dactylographié Dataset pour être le moins utile:

  • "ne fournissent pas la même gamme d'optimisations que Dataset[Row] (bien qu'ils partagent le format de stockage et certaines optimisations de plan d'exécution, il ne bénéficie pas pleinement de la génération de code ou de stockage hors tas) ni l'accès à toutes les capacités analytiques du DataFrame .

  • les transformations dactylographiées sont des boîtes noires, et créent efficacement une barrière d'analyse pour l'optimiseur. Par exemple, les sélections (filtres) ne peuvent pas être poussées au-dessus de la transformation dactylographiée:

    ds.groupBy("foo").agg(sum($"bar") as "bar").as[FooBar].filter(x => true).where($"foo" === 1).explain
    
    == Physical Plan ==
    *Filter (foo#133 = 1)
    +- *Filter <function1>.apply
       +- *HashAggregate(keys=[foo#133], functions=[sum(cast(bar#134 as double))])
          +- Exchange hashpartitioning(foo#133, 200)
             +- *HashAggregate(keys=[foo#133], functions=[partial_sum(cast(bar#134 as double))])
                +- LocalTableScan [foo#133, bar#134]
    

    par rapport à:

    ds.groupBy("foo").agg(sum($"bar") as "bar").as[FooBar].where($"foo" === 1).explain
    
    == Physical Plan ==
    *HashAggregate(keys=[foo#133], functions=[sum(cast(bar#134 as double))])
    +- Exchange hashpartitioning(foo#133, 200)
       +- *HashAggregate(keys=[foo#133], functions=[partial_sum(cast(bar#134 as double))])
          +- *Filter (foo#133 = 1)
             +- LocalTableScan [foo#133, bar#134] 
    

    Ceci a des répercussions sur des caractéristiques comme prédicat de refoulement ou de projection déroulant.

  • il n'y a pas aussi flexible comme RDDs avec seulement un petit sous-ensemble de types supportés nativement.

  • "Type de sécurité" avec Encoders est discutable lorsque Dataset est converti à l'aide de as la méthode. Comme la forme des données n'est pas encodée à l'aide d'une signature, un compilateur ne peut que vérifier l'existence d'un Encoder .

questions connexes:

21
répondu user6910411 2018-01-04 16:35:44

l'Étincelle Dataset est beaucoup plus puissant que l'Étincelle Dataframe . Petit exemple - vous ne pouvez créer Dataframe de Row , Tuple ou n'importe quel type de données primitif mais Dataset vous donne le pouvoir de créer Dataset de n'importe quel type non primitif aussi. i.e. vous pouvez littéralement créer Dataset de type d'objet.

Ex:

case class Employee(id:Int,name:String)

Dataset[Employee]   // is valid
Dataframe[Employee] // is invalid
2
répondu Kapil 2018-05-23 01:09:45