Spark中DataFrame列转置:Type与距离列组合生成新列
Spark Scala实现行转列并组合列名的方案
需求回顾
先明确下你的需求:把原始的多行多列数据(每个ID对应多个Type,每个Type有5m/10m/15m三个数值列),转换成以ID为主键,每个Type和距离列组合成新列(比如A_5m、B_10m)的宽表。
解决方案步骤
核心思路是先把多列的距离数据转成键值对(unpivot),再组合Type和距离生成目标列名,最后通过pivot转成宽表。
1. 准备示例数据
先模拟你的原始数据,方便后续测试:
import org.apache.spark.sql.functions._ import spark.implicits._ val df = spark.createDataFrame(Seq( (1, "A", 3, 9, 13), (1, "B", 7, 8, 22), (1, "C", 5, 11, 13), (2, "A", 1, 3, 20), (2, "B", 16, 17, 30) )).toDF("ID", "Type", "5m", "10m", "15m")
2. Unpivot距离列(宽表转窄表)
用stack函数把5m、10m、15m这三个列转成两列:distance(存储列名,比如"5m")和value(存储对应数值):
val unpivotedDF = df.select( col("ID"), col("Type"), expr("stack(3, '5m', `5m`, '10m', `10m`, '15m', `15m`) as (distance, value)") )
这里注意:因为列名以数字开头,需要用反引号
`包裹,比如`5m`;stack(3, ...)里的3代表要转换的列数量。
3. 组合Type和distance生成目标列名
用concat函数把Type和distance拼接成新的列名格式(比如"A_5m"):
val combinedColDF = unpivotedDF.withColumn( "combined_col", concat(col("Type"), lit("_"), col("distance")) )
4. Pivot转成目标宽表
以ID为分组键,对combined_col进行pivot操作,并用first函数聚合取对应的值(如果你的数据中每个ID+combined_col是唯一的,用first/max/min都可以):
val finalDF = combinedColDF.groupBy("ID") .pivot("combined_col") .agg(first("value"))
5. 查看结果
执行finalDF.show()就能得到你想要的格式:
+---+----+-----+-----+----+-----+-----+----+-----+-----+ | ID|A_5m|A_10m|A_15m|B_5m|B_10m|B_15m|C_5m|C_10m|C_15m| +---+----+-----+-----+----+-----+-----+----+-----+-----+ | 1| 3| 9| 13| 7| 8| 22| 5| 11| 13| | 2| 1| 3| 20| 16| 17| 30|null| null| null| +---+----+-----+-----+----+-----+-----+----+-----+-----+
补充说明
- 如果你的数据中存在同一个ID+Type+distance的重复记录,需要根据业务场景选择合适的聚合函数,比如
sum(求和)、avg(平均值)等。 - 如果需要指定列的顺序,可以在pivot时传入列名列表,比如
.pivot("combined_col", Seq("A_5m", "A_10m", "A_15m", "B_5m", ...)),避免列顺序随机。
内容的提问来源于stack exchange,提问作者Qwerty
相关产品推荐
相关产品推荐

