最近看到了一片好文,虽然很简单,但是配上的插图可以让人很好的记住Pyspark 中的多种Join 类型和实际的效果。原英文链接 Introduction to Pyspark join types - Blog | luminousmen 。
假设使用如下的两个DataFrame 来进行展示
heroes_data = [
('Deadpool', 3),
('Iron man', 1),
('Groot', 7),
]
race_data = [
('Kryptonian', 5),
('Mutant', 3),
('Human', 1),
]
heroes = spark.createDataFrame(heroes_data, ['name', 'id'])
races = spark.createDataFrame(race_data, ['race', 'id'])
实际的上的数据展示效果如下:
+--------+---+ +----------+---+
| name| id| | race| id|
+--------+---+ +----------+---+
|Deadpool| 3| |Kryptonian| 5|
|Iron man| 1| | Mutant| 3|
| Groot| 7| | Human| 1|
+--------+---+ +----------+---+
下面的展示图片中,其中相同的颜色表示的是能够Join匹配上的数据。下面的Join都是通过ID的方式来进行关联。
下面除了 Cross Join 之间,其它的都是通过如下说明
heroes.join(races, on='id', how='left').show()
说明在不同的 Join 的方式下不同效果。
这个比较好理解,就是heroes表的数据和races表的数据进行Join,就是将heroes表的每一行数据都同races表的每一行数据进行联合。数据的数量级就是 m*n
。不考虑Join的主键。
>>> heroes.crossJoin(races).show()
+--------+---+----------+---+
| name| id| race| id|
+--------+---+----------+---+
|Deadpool| 3|Kryptonian| 5|
|Deadpool| 3| Mutant| 3|
|Deadpool| 3| Human| 1|
|Iron man| 1|Kryptonian| 5|
|Iron man| 1| Mutant| 3|
|Iron man| 1| Human| 1|
| Groot| 7|Kryptonian| 5|
| Groot| 7| Mutant| 3|
| Groot| 7| Human| 1|
+--------+---+----------+---+
只生成同时匹配表heroes和表races的记录集
>>> heroes.join(races, on='id', how='inner').show()
+---+--------+------+
| id| name| race|
+---+--------+------+
| 1|Iron man| Human|
| 3|Deadpool|Mutant|
+---+--------+------+
left
和 left outer
是一个别名的关系。生成表heroes的所有记录,包括在表races里匹配的记录。如果没有匹配的,右边将是null。就是inner Join 的结果,再加上左边的表未匹配的所有的结果。
>>> heroes.join(races, on='id', how='left').show()
>>> heroes.join(races, on='id', how='leftouter').show()
+---+--------+------+
| id| name| race|
+---+--------+------+
| 7| Groot| null|
| 1|Iron man| Human|
| 3|Deadpool|Mutant|
+---+--------+------+
同上左外联合类似。
>>> heroes.join(races, on='id', how='right').show()
>>> heroes.join(races, on='id, how='rightouter').show()
+---+--------+----------+
| id| name| race|
+---+--------+----------+
| 5| null|Kryptonian|
| 1|Iron man| Human|
| 3|Deadpool| Mutant|
+---+--------+----------+
outer和full 也是别名关系。生成表heroes和表races里的记录全集,包括两边都匹配的记录。如果有一边没有匹配的,缺失的这一边为null。
>>> heroes.join(races, on='id', how='outer').show()
>>> heroes.join(races, on='id', how='full').show()
+---+--------+----------+
| id| name| race|
+---+--------+----------+
| 7| Groot| null|
| 5| null|Kryptonian|
| 1|Iron man| Human|
| 3|Deadpool| Mutant|
+---+--------+----------+
可以简单的看成是,inner join 之后,只保留能够Join上的左边表数据。
>>> heroes.join(races, on='id', how='leftsemi').show()
+---+--------+
| id| name|
+---+--------+
| 1|Iron man|
| 3|Deadpool|
+---+--------+
看成是Left semi-join 的取反操作,将左边中,没有匹配上的数据给取出。
>>> heroes.join(races, on='id', how='leftanti').show()
+---+-----+
| id| name|
+---+-----+
| 7|Groot|
+---+-----+
其它的补充:
在Join的过程中,左边和右边都不能为None,可以是空数据的表但是需要带Schema,且Schema中有指定的关联主键(on)。
使用Pyspark 中创建空的DataFrame
def create_empty_df_without_schema():
# Create an empty RDD
emp_RDD = spark.sparkContext.emptyRDD()
# Create empty schema
columns = StructType([])
return spark.createDataFrame(data=emp_RDD,
schema=columns)
def create_empty_df_with_schema():
columns = StructType([
StructField('name', StringType(), True),
StructField('id', IntegerType(), True),
])
# emp_RDD = spark.sparkContext.emptyRDD()
return spark.createDataFrame(data=[],
schema=columns)
喜欢点个赞再走吧~