68. [Spark]SparkSQL 列转行的一种方法

68.1. 需求场景

有这样一种场景,数据如下:

+---+---+---+---+---+---+
| id|  A|  B|  C|  D|  E|
+---+---+---+---+---+---+
|  a|  2|  3|  4|  5|  6|
|  b|  4|  2| 10|  3|  4|
|  c|  2|  1|  3|  2|  0|
+---+---+---+---+---+---+

现在要求按照id将id后的A、B、C、D、E字段打开,得到一个只有两列的结果集,样式如下:

+---+---+
| id| ev|
+---+---+
|  a|  2|
|  a|  3|
|  a|  4|
|  a|  5|
|  a|  6|
|  b|  4|
|  b|  2|
|  b| 10|
|  b|  3|
|  b|  4|
|  c|  2|
|  c|  1|
|  c|  3|
|  c|  2|
|  c|  0|
+---+---+

68.2. 实际操作

我们使用jupyterLab来运行PySpark创建数据并进行分析。

创建数据集,并且结果注册成为临时视图source

from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local").getOrCreate()

df = spark.createDataFrame([('a',2,3,4,5,6), ('b',4,2,10,3,4), ('c',2,1,3,2,0)
                           ], ['id','A','B','C','D','E']).orderBy('id')
df.createOrReplaceTempView("source")

df.show()

结果如下:

+---+---+---+---+---+---+
| id|  A|  B|  C|  D|  E|
+---+---+---+---+---+---+
|  a|  2|  3|  4|  5|  6|
|  b|  4|  2| 10|  3|  4|
|  c|  2|  1|  3|  2|  0|
+---+---+---+---+---+---+

68.3. 解决办法

使用侧视图

先将id后面的字段A、B、C、D、E做成一个Array,再侧视图中配合explode函数将行explode成rows。大致实现如下:

sql = """
select id, k
from (
select id,array(A,B,C,D,E) as `ev`
from source
) a 
lateral view explode(a.ev) as k
"""

spark.sql(sql).createOrReplaceTempView("later")
spark.sql("select * from later").show()

结果输出:

+---+---+
| id|  k|
+---+---+
|  a|  2|
|  a|  3|
|  a|  4|
|  a|  5|
|  a|  6|
|  b|  4|
|  b|  2|
|  b| 10|
|  b|  3|
|  b|  4|
|  c|  2|
|  c|  1|
|  c|  3|
|  c|  2|
|  c|  0|
+---+---+

68.4. 隆重介绍

或许应该有别的方法(如上)得到我们想要的结果,但是下面隆重介绍一个sparkSQL中的函数,一步即可得到我们想要的结果集。

该函数即为:STACK(n, expr1, ..., exprk)

stack
stack(n, expr1, ..., exprk) - Separates expr1, ..., exprk into n rows. Uses column names col0, col1, etc. by default unless specified otherwise.

Examples:

> SELECT stack(2, 1, 2, 3);
 1  2
 3  NULL

该函数将一行以n指定的行数分割成n行

使用STACK即可像下面这样即可实现我们的需求:

sql = """
select id,stack(5,A,B,C,D,E) as (`ev`) 
from source
"""

spark.sql(sql).createOrReplaceTempView("ecplode")
spark.sql("select * from ecplode").show()

结果如下:

+---+---+
| id| ev|
+---+---+
|  a|  2|
|  a|  3|
|  a|  4|
|  a|  5|
|  a|  6|
|  b|  4|
|  b|  2|
|  b| 10|
|  b|  3|
|  b|  4|
|  c|  2|
|  c|  1|
|  c|  3|
|  c|  2|
|  c|  0|
+---+---+