spark DataSetのselect()
関数を見ると、さまざまな生成された関数シグネチャがあります。
_(c1: TypedColumn[MyClass, U1],c2: TypedColumn[MyClass, U2] ....)
_
これは、MyClassのメンバーを直接参照し、タイプセーフである必要があることを示唆しているようですが、方法がわかりません...
ds.select("member")
はもちろん機能します..ds.select(_.member)
も何らかの形で機能する可能性がありますか?
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])
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関数。したがって、実際には、各アプローチの利点を実際に評価するには、パフォーマンスを経験的にテストする必要があります。