首页 > 解决方案 > 水平展开或旋转 spark scala 数据框以创建大型平面数据框

问题描述

我有一个具有以下架构的数据框:

UserID | StartDate | endDate | orderId | OrderCost| OrderItems| OrderLocation| Rank

1 到 10在哪里Rank。我需要将此数据帧转置为等级并以以下格式创建数据帧:

UserID| StartDate_1 | endDate_1 | orderId_1 | OrderCost_1| OrderItems_1| OrderLocation_1|start_2 |endDate_2| orderId_2 | OrderCost_2| OrderItems_2| OrderLocation_2 |............| startDate_N|endDate_N | orderId_N | OrderCost_N| OrderItems_N| OrderLocation_N

如果用户只有两条排名为 3 和 10 的记录,则要求使用后缀填充列,_3并且_10该用户的其余单元格值将为空。

我尝试了 2 种蛮力方法

  1. 过滤 DF 以获取排名,并使用后缀重命名列并执行自连接回 DF。

  2. 按 UserID 分组,收集为列表并将其传递给 map 函数,在该函数中我根据排名填充数组,然后返回字符串的 seq。通过传递所需的模式创建 DF

两者似乎都在工作(不确定它是否正确)但它们不是通用的,我可以重新用于我拥有的不同用例

标签: scalaapache-sparkapache-spark-sqlpivot

解决方案


在这个例子中,我使用了https://github.com/bokeh/bokeh/blob/master/bokeh/sampledata/_data/auto-mpg.csv

默认情况下,Spark 将排名放在前面,因此列名称与您指定的名称“相反”,但这只需几个步骤即可完成。关键是exprs应该动态创建,并且agg需要将其拆分为头部和尾部(这就是为什么.agg(exprs(0), exprs.slice(1, exprs.length)下面有)

scala> df2.columns

res39: Array[String] = Array(mpg, cyl, displ, hp, weight, accel, yr, origin, name, Rank)

// note here, you would use columns.slice with the indices for
// the columns you need, i.e. (1, 7)
val exprs = for (col <- df2.columns.slice(0, 8)) yield expr(s"first(${col}) as ${col}")

exprs: Array[org.apache.spark.sql.Column] = Array(first(mpg, false) AS `mpg`, first(cyl, false) AS `cyl`, first(displ, false) AS `displ`, first(hp, false) AS `hp`, first(weight, false) AS `weight`, first(accel, false) AS `accel`, first(yr, false) AS `yr`, first(origin, false) AS `origin`)

scala> val resultDF = df2.groupBy("name").pivot("Rank").agg(exprs(0), exprs.slice(1, exprs.length):_*)

scala> resultDF.columns
res40: Array[String] = Array(name, 1_mpg, 1_cyl, 1_displ, 1_hp, 1_weight, 1_accel, 1_yr, 1_origin, 2_mpg, 2_cyl, 2_displ, 2_hp, 2_weight, 2_accel, 2_yr, 2_origin, 3_mpg, 3_cyl, 3_displ, 3_hp, 3_weight, 3_accel, 3_yr, 3_origin, 4_mpg, 4_cyl, 4_displ, 4_hp, 4_weight, 4_accel, 4_yr, 4_origin, 5_mpg, 5_cyl, 5_displ, 5_hp, 5_weight, 5_accel, 5_yr, 5_origin, 6_mpg, 6_cyl, 6_displ, 6_hp, 6_weight, 6_accel, 6_yr, 6_origin, 7_mpg, 7_cyl, 7_displ, 7_hp, 7_weight, 7_accel, 7_yr, 7_origin, 8_mpg, 8_cyl, 8_displ, 8_hp, 8_weight, 8_accel, 8_yr, 8_origin, 9_mpg, 9_cyl, 9_displ, 9_hp, 9_weight, 9_accel, 9_yr, 9_origin, 10_mpg, 10_cyl, 10_displ, 10_hp, 10_weight, 10_accel, 10_yr, 10_origin)

推荐阅读