拆分的數據
有時在進行數據時我們需要把一列數據分割成多列數據,把一個字段值,分割成多個值。本節介紹如何通過spark sql提供的函數來進行數據的分割。
1. 數據拆分概述
數據拆分操作
在進行數據處理時,通常我們需要對數據進行拆分。比如:把一列拆分成多行,多列,把一行拆分成多行,多列等。
在spark-sql中提供了多個函數用來進行數據拆分。
數據拆分的函數
- split
- explode
- postexplode
- substring
2. 數據的拆分
2.1 通過explode系列函數進行拆分
- 把一個數組值的列拆分成多行**: explode
通過explode函數可以把一個list類型的值,拆分成多行。
>>> import pyspark.sql.functions as F
>>> list_data = [(1, "abc", ["p", "q", "r"]), (2, "def", ["x", "y", "z"])]
>>> schema = ["id", "col1", "col2"]
>>>
>>> df = spark.createDataFrame(list_data, schema)
>>> df.show()
+---+----+---------+
| id|col1| col2|
+---+----+---------+
| 1| abc|[p, q, r]|
| 2| def|[x, y, z]|
+---+----+---------+
>>> df.withColumn("col2", F.explode("col2")).show()
+---+----+----+
| id|col1|col2|
+---+----+----+
| 1| abc| p|
| 1| abc| q|
| 1| abc| r|
| 2| def| x|
| 2| def| y|
| 2| def| z|
+---+----+----+
- 把一個map值的列拆分成多個列和多行: explode
通過explode函數也可以把一個map值拆分成key,value兩個值。若該map中有多個key-value鍵值對,會對這些key-value值進行全部拆分。
>>> from pyspark.sql import Row
>>>
>>> rows = [
... Row(a=1, mapfield={"a": "b"}),
... Row(a=2, mapfield={"a1": "b1"}),
... Row(a=3, mapfield={"a2": "b2"}),
... Row(a=4, mapfield={"a3": "b3", "a4": "b4", "a5": {"key5": "value5"}})
... ]
>>>
>>> eDF = spark.createDataFrame(rows)
>>> eDF.show(truncate=False)
+---+-----------------------------------------+
|a |mapfield |
+---+-----------------------------------------+
|1 |[a -> b] |
|2 |[a1 -> b1] |
|3 |[a2 -> b2] |
|4 |[a3 -> b3, a4 -> b4, a5 -> {key5=value5}]|
+---+-----------------------------------------+
>>> eDF.select("a", F.explode(eDF.mapfield).alias("key", "value")).show()
+---+---+-------------+
| a|key| value|
+---+---+-------------+
| 1| a| b|
| 2| a1| b1|
| 3| a2| b2|
| 4| a3| b3|
| 4| a4| b4|
| 4| a5|{key5=value5}|
+---+---+-------------+
可以看到:explode函數先把map值的key-value進行拆分成兩列:key,value,若map中有多個key-value值,則會創建新的行,其他列的值保存不變。但注意:該函數只會拆分一層的key-value值,不會對嵌套的map值進行拆分。
- 對list和map值進行拆分,並且添加一個index號(從0開始):posexplode
pyspark.sql.functions.posexplode(col)
通過該函數可以把list或map值的列拆分成兩列,多行。並且會根據值在list或map中的位置添加一列索引值。該索引值從0開始。
>>> eDF = spark.createDataFrame([Row(a=1, intlist=[1,2,3], mapfield={"a": "b", "c": "d"})])
>>> eDF.show()
+---+---------+----------------+
| a| intlist| mapfield|
+---+---------+----------------+
| 1|[1, 2, 3]|[a -> b, c -> d]|
+---+---------+----------------+
# 拆分map值的列,並添加一列pos
>> eDF.select("a","intlist",F.posexplode(eDF.mapfield)).show()
+---+---------+---+---+-----+
| a| intlist|pos|key|value|
+---+---------+---+---+-----+
| 1|[1, 2, 3]| 0| a| b|
| 1|[1, 2, 3]| 1| c| d|
+---+---------+---+---+-----+
# 拆分數組值的列,並添加一列索引值pos
>>> eDF.select("a","mapfield",F.posexplode(eDF.intlist)).show()
+---+----------------+---+---+
| a| mapfield|pos|col|
+---+----------------+---+---+
| 1|[a -> b, c -> d]| 0| 1|
| 1|[a -> b, c -> d]| 1| 2|
| 1|[a -> b, c -> d]| 2| 3|
+---+----------------+---+---+
- **拆分時對空值進行填充:**posexplode_outer(spark-2.3開始)
從spark-2.3開始,提供了一個函數posexplode_outer,該函數的行爲和explode一樣,不同的是,若是數組或map值中存在None,則會以null替換。
df = spark.createDataFrame(
... [(1, ["foo", "bar"], {"x": 1.0}), (2, [], {}), (3, None, None)],
... ("id", "an_array", "a_map")
... )
>>> df.select("id", "an_array", posexplode_outer("a_map")).show()
+---+----------+----+----+-----+
| id| an_array| pos| key|value|
+---+----------+----+----+-----+
| 1|[foo, bar]| 0| x| 1.0|
| 2| []|null|null| null|
| 3| null|null|null| null|
+---+----------+----+----+-----+
>>> df.select("id", "a_map", posexplode_outer("an_array")).show()
+---+----------+----+----+
| id| a_map| pos| col|
+---+----------+----+----+
| 1|[x -> 1.0]| 0| foo|
| 1|[x -> 1.0]| 1| bar|
| 2| []|null|null|
| 3| null|null|null|
+---+----------+----+----+
2.2 split拆分數據
split函數可以把一個字符串值拆分成一個數組值,而且split函數還支持按正則表達式來進行拆分。
pyspark.sql.functions.split(str, pattern)
>>> df = spark.createDataFrame([('ab12cd',"aaaa")], ['c1','c2'])
>>> df.show()
+------+----+
| c1| c2|
+------+----+
|ab12cd|aaaa|
+------+----+
# 以下可以看到,拆分後的值都變成了數組值
>>> df.select('c2', F.split(df.c1, '[0-9]+').alias('r')).show()
+----+--------+
| c2| r|
+----+--------+
|aaaa|[ab, cd]|
+----+--------+
>>> df.select('c1', F.split(df.c2, '[0-9]+').alias('r')).show()
+------+------+
| c1| r|
+------+------+
|ab12cd|[aaaa]|
+------+------+
3. 總結
本節介紹瞭如何通過spark-sql對數據進行拆分操作。