私は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コマンドの後に一時テーブルを作成する)、明らかに膨大な数の重複、不要な行。
どうもありがとう!
スパーク> = 2.4
Zip
udf
をスキップして、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""")
また試すことができます
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|
// +------+----------+----+-----+