web-dev-qa-db-ja.com

Spark DataFrame?でオブジェクトの配列を受け入れるUDFを定義する

SparkのDataFrameを使用する場合、列のデータをマッピングするにはユーザー定義関数(UDF)が必要です。 UDFでは、引数の型を明示的に指定する必要があります。私の場合、オブジェクトの配列で構成される列を操作する必要がありますが、使用する型がわかりません。以下に例を示します。

import sqlContext.implicits._

// Start with some data. Each row (here, there's only one row) 
// is a topic and a bunch of subjects
val data = sqlContext.read.json(sc.parallelize(Seq(
  """
  |{
  |  "topic" : "pets",
  |  "subjects" : [
  |    {"type" : "cat", "score" : 10},
  |    {"type" : "dog", "score" : 1}
  |  ]
  |}
  """)))

組み込みのorg.Apache.spark.sql.functionsを使用して、列のデータに対して基本的な操作を実行するのは比較的簡単です

import org.Apache.spark.sql.functions.size
data.select($"topic", size($"subjects")).show

+-----+--------------+
|topic|size(subjects)|
+-----+--------------+
| pets|             2|
+-----+--------------+

通常、任意の操作を実行するカスタムUDFを書くのは簡単です

import org.Apache.spark.sql.functions.udf
val enhance = udf { topic : String => topic.toUpperCase() }
data.select(enhance($"topic"), size($"subjects")).show 

+----------+--------------+
|UDF(topic)|size(subjects)|
+----------+--------------+
|      PETS|             2|
+----------+--------------+

しかし、UDFを使用して「件名」列のオブジェクトの配列を操作する場合はどうなりますか? UDFの引数にはどのタイプを使用しますか?たとえば、sparkが提供する関数を使用する代わりに、サイズ関数を再実装する場合:

val my_size = udf { subjects: Array[Something] => subjects.size }
data.select($"topic", my_size($"subjects")).show

明らかにArray[Something]は機能しません...どのタイプを使用すればよいですか!? Array[]を完全に捨てるべきですか?ぶらぶらと回るとscala.collection.mutable.WrappedArrayと関係があるかもしれませんが、まだ別のタイプが必要です。

25
ohruunuruus

探しているのは_Seq[o.a.s.sql.Row]_です:

_import org.Apache.spark.sql.Row

val my_size = udf { subjects: Seq[Row] => subjects.size }
_

説明

  • ArrayTypeの現在の表現は、ご存じのとおり、WrappedArrayなので、Arrayは機能しません。安全な側に留まる方が良いでしょう。
  • 公式仕様によればStructTypeのローカル(外部)タイプはRowです。残念ながら、個々のフィールドへのアクセスはタイプセーフではありません。

  • Spark <2.3でstructを作成するには、udfに渡される関数はProduct type(_Tuple*_または_case class_を返す必要があります)、Rowではありません。これは、対応するudfバリアント Scala反射に依存

    n引数のScalaクロージャーをユーザー定義関数(UDF)として定義します。データ型は、Scalaクロージャの署名に基づいて自動的に推測されます。

  • Spark> = 2.3では、Rowを直接返すことができます スキーマが提供されている限り

    def udf(f: AnyRef, dataType: DataType): UserDefinedFunction Scalaクロージャーを使用して決定論的なユーザー定義関数(UDF)を定義します。このバリアントでは、呼び出し元は出力データ型を指定する必要があり、自動入力型強制はありません。

    たとえば、 Spark UDFをJavaに作成する方法/複合型を返すKotlinを参照してください を参照してください。

23
zero323