web-dev-qa-db-ja.com

Spark SQLでUDFに追加のパラメータを渡すにはどうすればよいですか?

DataFrameの日付列を解析します。日付列ごとに、日付の解像度が変更される場合があります(つまり、解像度が「月」に設定されている場合、2011/01/10 => 2011/01 )。

次のコードを書きました。

def convertDataFrame(dataframe: DataFrame, schema : Array[FieldDataType], resolution: Array[DateResolutionType]) : DataFrame =
{
  import org.Apache.spark.sql.functions._
  val convertDateFunc = udf{(x:String, resolution: DateResolutionType) => SparkDateTimeConverter.convertDate(x, resolution)}
  val convertDateTimeFunc = udf{(x:String, resolution: DateResolutionType) => SparkDateTimeConverter.convertDateTime(x, resolution)}

  val allColNames = dataframe.columns
  val allCols = allColNames.map(name => dataframe.col(name))

  val mappedCols =
  {
    for(i <- allCols.indices) yield
    {
      schema(i) match
      {
        case FieldDataType.Date => convertDateFunc(allCols(i), resolution(i)))
        case FieldDataType.DateTime => convertDateTimeFunc(allCols(i), resolution(i))
        case _ => allCols(i)
      }
    }
  }

  dataframe.select(mappedCols:_*)

}}

ただし、機能しません。 ColumnsのみをUDFに渡すことができるようです。 DataFrameRDDに変換して各行に関数を適用すると、非常に遅くなるのではないかと思います。

誰もが正しい解決策を知っていますか?ありがとうございました!

16
DarkZero

カレーを少しだけ使ってください:

def convertDateFunc(resolution: DateResolutionType) = udf((x:String) => 
  SparkDateTimeConverter.convertDate(x, resolution))

次のように使用します。

case FieldDataType.Date => convertDateFunc(resolution(i))(allCols(i))

補足説明として、sql.functions.truncおよびsql.functions.date_format。これらは、UDFをまったく使用せずに、少なくともジョブの一部である必要があります。

Spark 2.2以降では、typedLit関数を使用できます。

import org.Apache.spark.sql.functions.typedLit

SeqMapなどのより広い範囲のリテラルをサポートします。

38
zero323

_org.Apache.spark.sql.functions_で定義されているlit(...)関数を使用して、リテラルColumnを作成してudfに渡すことができます。

例えば:

_val takeRight = udf((s: String, i: Int) => s.takeRight(i))
df.select(takeRight($"stringCol", lit(1)))
_
15