web-dev-qa-db-ja.com

Spark SQLテーブルの複数の列を展開(転置?)

私はSpark SQLを使用しています(SQL構文に影響する場合はSparkであることに言及します-まだ十分な知識がありません)再構築しようとしているテーブルがありますが、複数の列を同時に転置しようとして動けなくなります。

基本的に、次のようなデータがあります。

userId    someString      varA     varB
   1      "example1"    [0,2,5]   [1,2,9]
   2      "example2"    [1,20,5]  [9,null,6]

そして、varAとvarBの両方を同時に爆発させたい(長さは常に一定です)-最終的な出力は次のようになります。

userId    someString      varA     varB
   1      "example1"       0         1
   1      "example1"       2         2
   1      "example1"       5         9
   2      "example2"       1         9
   2      "example2"       20       null
   2      "example2"       5         6

しかし、私は1つのコマンドで1つのexplode(var)ステートメントを動作させるようにしか見えず、それらを連鎖しようとすると(つまり最初のexplodeコマンドの後に一時テーブルを作成する)、明らかに膨大な数の重複、不要な行。

どうもありがとう!

20
anthr

スパーク> = 2.4

Zipudfをスキップして、arrays_Zip 関数:

df.withColumn("vars", explode(arrays_Zip($"varA", $"varB"))).select(
  $"userId", $"someString",
  $"vars.varA", $"vars.varB").show

スパーク<2.4

あなたが望むものは、カスタムUDFなしでは不可能です。 Scalaでは、次のようなことができます。

val data = sc.parallelize(Seq(
    """{"userId": 1, "someString": "example1",
        "varA": [0, 2, 5], "varB": [1, 2, 9]}""",
    """{"userId": 2, "someString": "example2",
        "varA": [1, 20, 5], "varB": [9, null, 6]}"""
))

val df = spark.read.json(data)

df.printSchema
// root
//  |-- someString: string (nullable = true)
//  |-- userId: long (nullable = true)
//  |-- varA: array (nullable = true)
//  |    |-- element: long (containsNull = true)
//  |-- varB: array (nullable = true)
//  |    |-- element: long (containsNull = true)

これで、Zip udfを定義できます。

import org.Apache.spark.sql.functions.{udf, explode}

val Zip = udf((xs: Seq[Long], ys: Seq[Long]) => xs.Zip(ys))

df.withColumn("vars", explode(Zip($"varA", $"varB"))).select(
   $"userId", $"someString",
   $"vars._1".alias("varA"), $"vars._2".alias("varB")).show

// +------+----------+----+----+
// |userId|someString|varA|varB|
// +------+----------+----+----+
// |     1|  example1|   0|   1|
// |     1|  example1|   2|   2|
// |     1|  example1|   5|   9|
// |     2|  example2|   1|   9|
// |     2|  example2|  20|null|
// |     2|  example2|   5|   6|
// +------+----------+----+----+

生SQLの場合:

sqlContext.udf.register("Zip", (xs: Seq[Long], ys: Seq[Long]) => xs.Zip(ys))
df.registerTempTable("df")

sqlContext.sql(
  """SELECT userId, someString, explode(Zip(varA, varB)) AS vars FROM df""")
34
zero323

また試すことができます

case class Input(
 userId: Integer,
 someString: String,
 varA: Array[Integer],
 varB: Array[Integer])

case class Result(
 userId: Integer,
 someString: String,
 varA: Integer,
 varB: Integer)

def getResult(row : Input) : Iterable[Result] = {
 val user_id = row.user_id
 val someString = row.someString
 val varA = row.varA
 val varB = row.varB
 val seq = for( i <- 0 until varA.size) yield {Result(user_id,someString,varA(i),varB(i))}
 seq
 }

val obj1 = Input(1, "string1", Array(0, 2, 5), Array(1, 2, 9))
val obj2 = Input(2, "string2", Array(1, 3, 6), Array(2, 3, 10))
val input_df = sc.parallelize(Seq(obj1, obj2)).toDS

val res = input_df.flatMap{ row => getResult(row) }
res.show
// +------+----------+----+-----+
// |userId|someString|varA|varB |
// +------+----------+----+-----+
// |     1|  string1 |   0|   1 |
// |     1|  string1 |   2|   2 |
// |     1|  string1 |   5|   9 |
// |     2|  string2 |   1|   2 |
// |     2|  string2 |   3|   3 |
// |     2|  string2 |   6|   10|
// +------+----------+----+-----+
0
Alexandru Ivana