現在、.withColumnの複数のチェーンを介して複数のDataFrame列に同じプロシージャを繰り返し適用するコードがあり、プロシージャを合理化する関数を作成したいと考えています。私の場合、キーで集計された列の累積合計を見つけています:
val newDF = oldDF
.withColumn("cumA", sum("A").over(Window.partitionBy("ID").orderBy("time")))
.withColumn("cumB", sum("B").over(Window.partitionBy("ID").orderBy("time")))
.withColumn("cumC", sum("C").over(Window.partitionBy("ID").orderBy("time")))
//.withColumn(...)
私が望むのは次のようなものです:
def createCumulativeColums(cols: Array[String], df: DataFrame): DataFrame = {
// Implement the above cumulative sums, partitioning, and ordering
}
またはそれ以上:
def withColumns(cols: Array[String], df: DataFrame, f: function): DataFrame = {
// Implement a udf/arbitrary function on all the specified columns
}
_*
_を含む可変引数でselect
を使用できます。
_import spark.implicits._
df.select($"*" +: Seq("A", "B", "C").map(c =>
sum(c).over(Window.partitionBy("ID").orderBy("time")).alias(s"cum$c")
): _*)
_
この:
Seq("A", ...).map(...)
を使用して列名をウィンドウ式にマップします$"*" +: ...
_を付加します。... : _*
_で結合シーケンスをアンパックします。また、次のように一般化できます。
_import org.Apache.spark.sql.{Column, DataFrame}
/**
* @param cols a sequence of columns to transform
* @param df an input DataFrame
* @param f a function to be applied on each col in cols
*/
def withColumns(cols: Seq[String], df: DataFrame, f: String => Column) =
df.select($"*" +: cols.map(c => f(c)): _*)
_
withColumn
構文が読みやすい場合は、foldLeft
を使用できます。
_Seq("A", "B", "C").foldLeft(df)((df, c) =>
df.withColumn(s"cum$c", sum(c).over(Window.partitionBy("ID").orderBy("time")))
)
_
たとえば、次のように一般化できます。
_/**
* @param cols a sequence of columns to transform
* @param df an input DataFrame
* @param f a function to be applied on each col in cols
* @param name a function mapping from input to output name.
*/
def withColumns(cols: Seq[String], df: DataFrame,
f: String => Column, name: String => String = identity) =
cols.foldLeft(df)((df, c) => df.withColumn(name(c), f(c)))
_
質問は少し古いですが、アキュムレータとしてDataFrame
を使用して列のリストを折り畳み、DataFrame
をマッピングすると、列数が自明でない場合の異なるパフォーマンス結果(詳細な説明については here を参照)。短い話...いくつかの列ではfoldLeft
で問題ありませんが、そうでない場合はmap
の方が優れています。