Pyspark > Dataframe 将多个数组列分成多行,每行一个值

lan*_*ooo 5 python dataframe apache-spark apache-spark-sql pyspark

我们有一个 pyspark 数据框,其中有多个列,其中包含具有多个值的数组。我们的目标是将这些列的每个值放在几行中,并保留初始的不同列。所以,从这样的事情开始:

data = [
    ("A", ["a", "c"], ["1", "5"]),
    ("B", ["a", "b"], None),
    ("C", [], ["1"]),
]
Run Code Online (Sandbox Code Playgroud)

什么是:

+---+------+------+
|id |list_a|list_b|
+---+------+------+
|A  |[a, c]|[1, 5]|
|B  |[a, b]|null  |
|C  |[]    |[1]   |
+---+------+------+
Run Code Online (Sandbox Code Playgroud)

我们希望最终得到:

+---+----+----+
|id |col |col |
+---+----+----+
|A  |a   |null|
|A  |c   |null|
|A  |null|1   |
|A  |null|5   |
|B  |a   |null|
|B  |b   |null|
|C  |null|1   |
+---+----+----+
Run Code Online (Sandbox Code Playgroud)

我们正在考虑几种方法:

  1. 用列指示符为每个值添加前缀,将所有数组合并为一个数组,将其分解并将不同的值重新组织到不同的列中
  2. 将数据帧分成几个,每个数据帧都有这些数组列之一,分解数组列,然后连接数据帧

但所有这些方法都给人一种肮脏、复杂、容易出错且低效的解决方案的感觉。

有谁知道如何以优雅的方式解决这个问题?

小智 2

如果 list_a 和 list_b 列都可以为空,我将在数据集中添加第四个案例

data = [
    ("A", ["a", "c"], ["1", "5"]),
    ("B", ["a", "b"], None),
    ("C", [], ["1"]),
    ("D", None, None),
]
df = spark.createDataFrame(data,["id","list_a","list_b"])
Run Code Online (Sandbox Code Playgroud)

然后,我将原始 df 分成 3 个(均为空值、list_a 爆炸和 list_b 爆炸)并执行 unionByName

dfnulls = df.filter(col("list_a").isNull() & col("list_b").isNull())\
    .withColumn("list_a", lit(None))\
    .withColumn("list_b", lit(None))

df1 = df\
    .withColumn("list_a", explode_outer(col("list_a")))\
    .withColumn("list_b", lit(None))\
    .filter(~col("list_a").isNull())

df2 = df\
    .withColumn("list_b", explode_outer(col("list_b")))\
    .withColumn("list_a", lit(None))\
    .filter(~col("list_b").isNull())

merged_df = df1.unionByName(df2).unionByName(dfnulls)

merged_df.show()

+---+------+------+
| id|list_a|list_b|
+---+------+------+
|  A|     a|  null|
|  A|     c|  null|
|  B|     a|  null|
|  B|     b|  null|
|  A|  null|     1|
|  A|  null|     5|
|  C|  null|     1|
|  D|  null|  null|
+---+------+------+
Run Code Online (Sandbox Code Playgroud)