scala分解方法笛卡尔积多数组

fjaof16o  于 2021-05-27  发布在  Spark
关注(0)|答案(2)|浏览(549)

试图解决一些Dataframe内的转换,任何帮助都是非常感谢的。
在scala(版本2.3.1)中:我有一个Dataframe,它有一个字符串和long的数组。

+------+---------+----------+---------+---------+
|userId|     varA|      varB|     varC|     varD|
+------+---------+----------+---------+---------+
|     1|[A, B, C]| [0, 2, 5]|[1, 2, 9]|[0, 0, 0]|
|     2|[X, Y, Z]|[1, 20, 5]|[9, 0, 6]|[1, 1, 1]|
+------+---------+----------+---------+---------+

我希望我的输出像下面的Dataframe。

+------+---+---+---+---+
|userId|  A|  B|  C|  D|
+------+---+---+---+---+
|     1|  A|  0|  1|  0|
|     1|  B|  2|  2|  0|
|     1|  C|  5|  9|  0|
|     2|  X|  1|  9|  1|
|     2|  Y| 20|  0|  1|
|     2|  Z|  5|  6|  1|
+------+---+---+---+---+

我试着用explode,得到笛卡尔积。有没有办法将记录数保持在6行,而不是18行。

scala> val data = sc.parallelize(Seq("""{"userId": 1,"varA": ["A", "B", "C"], "varB": [0, 2, 5], "varC": [1, 2, 9], "varD": [0, 0, 0]}""","""{"userId": 2,"varA": ["X", "Y", "Z"], "varB": [1, 20, 5], "varC": [9, 0, 6], "varD": [1, 1, 1]}"""))
scala> val df = spark.read.json(data)
scala> df.show()
+------+---------+----------+---------+---------+
|userId|     varA|      varB|     varC|     varD|
+------+---------+----------+---------+---------+
|     1|[A, B, C]| [0, 2, 5]|[1, 2, 9]|[0, 0, 0]|
|     2|[X, Y, Z]|[1, 20, 5]|[9, 0, 6]|[1, 1, 1]|
+------+---------+----------+---------+---------+
scala>
scala> df.printSchema
root
 |-- userId: long (nullable = true)
 |-- varA: array (nullable = true)
 |    |-- element: string (containsNull = true)
 |-- varB: array (nullable = true)
 |    |-- element: long (containsNull = true)
 |-- varC: array (nullable = true)
 |    |-- element: long (containsNull = true)
 |-- varD: array (nullable = true)
 |    |-- element: long (containsNull = true)
scala>
scala> val zip_str = udf((x: Seq[String], y: Seq[Long]) => x.zip(y))
zip_str: org.apache.spark.sql.expressions.UserDefinedFunction = UserDefinedFunction(<function2>,ArrayType(StructType(StructField(_1,StringType,true), StructField(_2,LongType,false)),true),Some(List(ArrayType(StringType,true), ArrayType(LongType,false))))

scala> val zip_long = udf((x: Seq[Long], y: Seq[Long]) => x.zip(y))
zip_long: org.apache.spark.sql.expressions.UserDefinedFunction = UserDefinedFunction(<function2>,ArrayType(StructType(StructField(_1,LongType,false), StructField(_2,LongType,false)),true),Some(List(ArrayType(LongType,false), ArrayType(LongType,false))))

scala> df.withColumn("zip_1", explode(zip_str($"varA", $"varB"))).withColumn("zip_2", explode(zip_long($"varC", $"varD"))).select($"userId", $"zip_1._1".alias("A"),$"zip_1._2".alias("B"),$"zip_2._1".alias("C"),$"zip_2._2".alias("D")).show()
+------+---+---+---+---+
|userId|  A|  B|  C|  D|
+------+---+---+---+---+
|     1|  A|  0|  1|  0|
|     1|  A|  0|  2|  0|
|     1|  A|  0|  9|  0|
|     1|  B|  2|  1|  0|
|     1|  B|  2|  2|  0|
|     1|  B|  2|  9|  0|
|     1|  C|  5|  1|  0|
|     1|  C|  5|  2|  0|
|     1|  C|  5|  9|  0|
|     2|  X|  1|  9|  1|
|     2|  X|  1|  0|  1|
|     2|  X|  1|  6|  1|
|     2|  Y| 20|  9|  1|
|     2|  Y| 20|  0|  1|
|     2|  Y| 20|  6|  1|
|     2|  Z|  5|  9|  1|
|     2|  Z|  5|  0|  1|
|     2|  Z|  5|  6|  1|
+------+---+---+---+---+
scala>

这里有一些参考资料
https://intellipaat.com/community/17050/explode-transpose-multiple-columns-in-spark-sql-table

tjrkku2a

tjrkku2a1#

您不需要自定义项,它可以使用sparksql实现 arrays_zip 然后 explode :

df.select('userId,explode(arrays_zip('varA,'varB,'varC,'varD)))
   .select("userId","col.varA","col.varB","col.varC","col.varD")
   .show

输出:

+------+----+----+----+----+
|userId|varA|varB|varC|varD|
+------+----+----+----+----+
|     1|   A|   0|   1|   0|
|     1|   B|   2|   2|   0|
|     1|   C|   5|   9|   0|
|     1|   X|   1|   9|   1|
|     1|   Y|  20|   0|   1|
|     1|   Z|   5|   6|   1|
+------+----+----+----+----+
zzwlnbp8

zzwlnbp82#

将posexplode和expr结合起来的方法可能会奏效。
如果我们执行以下操作:

df.select(
  col("userId"),
  posexplode("varA"),
  col("varB"),
  col("varC")
).withColumn(
  "varB", 
  expr("varB[pos]")
).withColumn(
  "varC", 
  expr("varC[pos]")
)

我是凭记忆写的,所以我不是100%肯定。稍后我将运行一个测试,并在验证时使用edit进行更新。
编辑
上面的表达除了需要一个小的正确外都有效。更新的表达式-

df.select(col("userId"),posexplode(col("varA")),col("varB"),col("varC"), col("varD")).withColumn("varB",expr("varB[pos]")).withColumn("varC",expr("varC[pos]")).withColumn("varD",expr("varD[pos]")).show()

输出-

+------+---+---+----+----+----+
|userId|pos|col|varB|varC|varD|
+------+---+---+----+----+----+
|     1|  0|  A|   0|   1|   0|
|     1|  1|  B|   2|   2|   0|
|     1|  2|  C|   5|   9|   0|
|     2|  0|  X|   1|   9|   1|
|     2|  1|  Y|  20|   0|   1|
|     2|  2|  Z|   5|   6|   1|
+------+---+---+----+----+----+

相关问题