web-dev-qa-db-ja.com

Spark typedcolumnを使用したデータセット選択

spark DataSetのselect()関数を見ると、さまざまな生成された関数シグネチャがあります。

_(c1: TypedColumn[MyClass, U1],c2: TypedColumn[MyClass, U2] ....)
_

これは、MyClassのメンバーを直接参照し、タイプセーフである必要があることを示唆しているようですが、方法がわかりません...

ds.select("member")はもちろん機能します..ds.select(_.member)も何らかの形で機能する可能性がありますか?

10
Jeremy

Scala DSL for selectでは、Columnを識別する方法はたくさんあります。

  • シンボルから:_'name_
  • 文字列から:_$"name"_またはcol(name)
  • 式から:expr("nvl(name, 'unknown') as renamed")

TypedColumnからColumnを取得するには、単に_myCol.as[T]_を使用します。

例:ds.select(col("name").as[String])

14
Sim

ds.select(_.member)と同等のものが必要な場合は、mapを使用してください。

_case class MyClass(member: MyMember, foo: A, bar: B)
val ds: DataSet[MyClass] = ???
val members: DataSet[MyMember] = ds.map(_.member)
_

編集mapを使用しないための引数。

同じことを行うためのよりパフォーマンスの高い方法は、プロジェクションを使用することであり、mapをまったく使用しません。コンパイル時の型チェックは失われますが、その代わりに、Catalystクエリエンジンにさらに最適化された処理を実行する機会が与えられます。 @Simが以下のコメントでほのめかしているように、主要な最適化は、MyClassのコンテンツ全体をタングステンメモリスペースからJVMヒープメモリにデシリアライズする必要がなく、アクセサを呼び出すだけで、 __.member_の結果をタングステンに戻します。

より具体的な例を示すために、次のようにデータモデルを再定義しましょう。

_  // Make sure these are not nested classes 
  // (i.e. in a top level compilation units).
  case class MyMember(something: Double)
  case class MyClass(member: MyMember, foo: Int, bar: String)
_

_SQLImplicits.newProductEncoder[T <: Product]_が_Encoder[MyClass]_ APIに必要な暗黙の_Dataset[T]_を提供できるように、これらはcaseクラスである必要があります。

これで、上記の例をより具体的にすることができます。

_  val ds: Dataset[MyClass] = Seq(MyClass(MyMember(1.0), 2, "three")).toDS()
  val membersMapped: Dataset[Double] = ds.map(_.member.something)
_

舞台裏で何が起こっているかを確認するには、explain()メソッドを使用します。

_membersMapped.explain()

== Physical Plan ==
*(1) SerializeFromObject [input[0, double, false] AS value#19]
+- *(1) MapElements <function1>, obj#18: double
   +- *(1) DeserializeToObject newInstance(class MyClass), obj#17: MyClass
      +- LocalTableScan [member#12, foo#13, bar#14]
_

これにより、タングステンとの間のシリアル化が明確になります。

射影を使用して同じ値を取得しましょう[^ 1]:

_val ds2: Dataset[Double] = ds.select($"member.something".as[Double])
ds2.explain()

== Physical Plan ==
LocalTableScan [something#25]
_

それでおしまい!シングルステップ[^ 2]。 MyClassを元のデータセットにエンコードする以外のシリアル化はありません。

[^ 1]:射影が_$"member.something"_ではなく_$"value.member.something"_として定義される理由は、Catalystが単一列のDataFrameのメンバーを自動的に射影することに関係しています。

[^ 2]:公平を期すために、最初の物理計画のステップの横にある_*_は、それらがWholeStageCodegenExecによって実装されることを示しています。これにより、これらのステップは単一のオンザフライコンパイルになります。独自のランタイム最適化のセットが適用されているJVM関数。したがって、実際には、各アプローチの利点を実際に評価するには、パフォーマンスを経験的にテストする必要があります。

13
metasim