Scala Spark - 如何减少在单列中包含多列的数据帧?

Posted

技术标签:

【中文标题】Scala Spark - 如何减少在单列中包含多列的数据帧?【英文标题】:Scala Spark - How reduce a dataframe with many couple columns in a single couple columns? 【发布时间】:2017-03-30 08:55:27 【问题描述】:

我有一个包含许多(计数和得分)列的数据框。 这种情况不是枢轴,而是类似的非枢轴。 示例:


|house_score | house_count | mobile_score | mobile_count | sport_score | sport_count | ....<other couple columns>.....| 
|   20            2              48              6             6             78     |
|   40            78             47              74            69             6     |

我想要一个只有两列的新日期框,得分 e 计数。新的数据框减少了仅有的几列中的所有几列。

_________________
| score | count |
|   20  |   2   |
|   40  |   78  |
|   48  |   6   |
|   47  |   74  |
|   6   |   78  |
|   69  |   6   |
|_______________|

什么是最好的解决方案(优雅的代码/性能)?

【问题讨论】:

This 可能包含您问题的解决方案。您的问题可能重复 Pivot Spark Dataframe的可能重复 【参考方案1】:

您可以在列名上使用 foldLeft 来实现这一点(不包括 _ 之后的部分)。这是相当有效的,因为所有密集的操作都是分布式的,并且代码相当干净简洁。

// df from example
val df = sc.parallelize(List((20,2,48,6,6,78), (40,78,47,74,69,6) )).toDF("house_score", "house_count", "mobile_score", "mobile_count", "sport_score", "sport_count")

// grab column names (part before the _)
val cols = df.columns.map(col => col.split("_")(0)).distinct

// fold left over all columns
val result = cols.tail.foldLeft( 
   // init with cols.head column
   df.select(col(s"$cols.head_score").as("score"), col(s"$cols.head_count").as("count")) 
)case (acc,c) => 
   // union current column c
   acc.unionAll(df.select(col(s"$c_score").as("score"),     col(s"$c_count").as("count")))


result.show

【讨论】:

【参考方案2】:

按照另一个答案中的建议使用 unionAlls 将要求您多次扫描数据,并且在每次扫描项目中,df 仅包含 2 列。从性能的角度来看,如果您可以一次性完成工作,则应避免多次扫描数据,尤其是当您拥有不可缓存的大型数据集或需要进行多次扫描时。

您可以通过生成所有元组(分数、计数)然后对它们进行平面映射,在 1 次中完成。我让你决定它有多优雅:

scala> :paste
// Entering paste mode (ctrl-D to finish)

val df = List((20,2,48,6,6,78), (40,78,47,74,69,6))
    .toDF("house_score", "house_count", "mobile_score", "mobile_count", "sport_score", "sport_count")

df.show

val result = df
    .flatMap(r => Range(0, 5, 2).map(i => (r.getInt(i), r.getInt(i + 1))))
    .toDF("score", "count")

result.show


// Exiting paste mode, now interpreting.

+-----------+-----------+------------+------------+-----------+-----------+
|house_score|house_count|mobile_score|mobile_count|sport_score|sport_count|
+-----------+-----------+------------+------------+-----------+-----------+
|         20|          2|          48|           6|          6|         78|
|         40|         78|          47|          74|         69|          6|
+-----------+-----------+------------+------------+-----------+-----------+

+-----+-----+
|score|count|
+-----+-----+
|   20|    2|
|   48|    6|
|    6|   78|
|   40|   78|
|   47|   74|
|   69|    6|
+-----+-----+

df: org.apache.spark.sql.DataFrame = [house_score: int, house_count: int ... 4 more fields]
result: org.apache.spark.sql.DataFrame = [score: int, count: int]

【讨论】:

以上是关于Scala Spark - 如何减少在单列中包含多列的数据帧?的主要内容,如果未能解决你的问题,请参考以下文章

Spark scala 从列表中选择多列和单列

如何在 Spark Scala SQL 查询中包含 0 值?

Spark:如何在 pyspark 或 scala spark 中分解数据并添加列名?

如何使用 Spark-Scala 解析 JSON 数据

scala spark减少groupby中的列表

Scala/Spark 减少类对象的 RDD