如何横向拼接列不同但行数相同的多个PySpark DataFrame?
问题
需要拼接2个或多个列不同但行数相同的PySpark DataFrame(类似Pandas中pd.concat设置axis=1的横向拼接效果)。
假设有以下DataFrame:
spark = SparkSession.builder.getOrCreate() df1 = spark.createDataFrame([[1, 1], [2, 2]], ['a', 'b']) df2 = spark.createDataFrame([[3, 333], [4, 444]], ['c', 'd']) df3 = spark.createDataFrame([[555, 5], [666, 6]], ['e', 'f'])
期望得到的最终DataFrame(行数不变,列合并)如下:
df_final = spark.createDataFrame([[1, 1, 3, 333, 555, 5], [2, 2, 4, 444, 666, 6]], ['a', 'b','c', 'd','e', 'f'])
尝试了以下代码:
df_final = df1.join(df2).join(df3)
但得到了大量重复数据(实际是笛卡尔积导致的行数爆炸),请问该如何解决?
解决方案
直接用join且不指定关联条件时,Spark会执行笛卡尔积:每个DataFrame的行与其他所有DataFrame的行两两组合,行数变成各df行数的乘积(比如3个2行的df会得到8行),这显然不是想要的横向拼接效果。
要实现类似pd.concat(axis=1)的横向拼接,核心是给每个DataFrame添加行号列,通过行号关联对应位置的行,具体步骤如下:
- 导入所需函数:
from pyspark.sql import functions as F from pyspark.sql.window import Window
- 给每个DataFrame添加自增行号:
# 定义窗口,若需要指定行顺序,可在orderBy中添加对应列(比如df1.orderBy("a")) window = Window.orderBy(F.lit(1)) df1_with_idx = df1.withColumn("row_idx", F.row_number().over(window)) df2_with_idx = df2.withColumn("row_idx", F.row_number().over(window)) df3_with_idx = df3.withColumn("row_idx", F.row_number().over(window))
- 按行号关联所有DataFrame,最后删除行号列:
df_final = df1_with_idx.join(df2_with_idx, on="row_idx", how="inner")\ .join(df3_with_idx, on="row_idx", how="inner")\ .drop("row_idx")
- 查看结果:
df_final.show()
输出:
+---+---+---+---+---+---+ | a| b| c| d| e| f| +---+---+---+---+---+---+ | 1| 1| 3|333|555| 5| | 2| 2| 4|444|666| 6| +---+---+---+---+---+---+
关键注意点
- 如果DataFrame有明确的排序逻辑,必须在
Window.orderBy()中指定对应列,避免行号生成顺序出错(比如Window.orderBy("a")让df1按a列排序生成行号)。 - 确保所有DataFrame行数完全一致,用
inner join会丢弃行数不匹配的行;若要保留所有行,可改用outer join,但不匹配的位置会出现null值。
内容的提问来源于stack exchange,提问作者user139442
相关产品推荐
相关产品推荐

