web-dev-qa-db-fra.com

Décomposer (transposer?) Plusieurs colonnes dans la table Spark SQL

J'utilise Spark SQL (je mentionne qu'il est en Spark au cas où cela affecte la syntaxe SQL - je ne suis pas encore assez familier pour en être sûr)) et j'ai une table que j'essaie de restructurer, mais je suis coincé en essayant de transposer plusieurs colonnes en même temps.

Fondamentalement, j'ai des données qui ressemblent à:

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

et je voudrais exploser simultanément varA et varB (la longueur sera toujours cohérente) - de sorte que la sortie finale ressemble à ceci:

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

mais je ne peux obtenir qu'une seule instruction explode (var) pour fonctionner dans une seule commande, et si j'essaye de les enchaîner (c'est-à-dire créer une table temporaire après la première commande explode), alors j'obtiens évidemment un grand nombre de doublons, inutile Lignes.

Merci beaucoup!

20
anthr

Spark> = 2,4

Vous pouvez ignorer Zipudf et utiliser arrays_Zip une fonction:

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

Spark <2,4

Ce que vous voulez n'est pas possible sans un UDF personnalisé. Dans Scala vous pourriez faire quelque chose comme ceci:

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)

Maintenant, nous pouvons définir 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|
// +------+----------+----+----+

Avec SQL brut:

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

Vous pouvez également essayer

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