Scala转型与行动

时间:2019-03-19 11:26:03

标签: scala apache-spark rdd

我有一个RDD列表[(String,List [Int])]像List((“ A”,List(1,2,3,4)),(“ B”,List(5,6,7 )))

如何将它们转换为List((“ A”,1),(“ A”,2),(“ A”,3),(“ A”,4),(“ B”,5), (“ B”,6),(“ B”,7))

然后操作将是通过键减少并生成类似List((“ A”,2.5)(“ B”,6))

的结果

我已经尝试过使用map(e => List(e._1,e._2)),但没有得到想要的结果。

其中“ A”平均为2.5,而“ B”平均为6

通过这些转换和操作帮助我。 预先感谢

3 个答案:

答案 0 :(得分:1)

有几种获取所需内容的方法。您也可以使用for comprehension,但我想到的第一个方法是此实现:

val l = List(("A", List(1, 2, 3)), ("B", List(1, 2, 3)))

val flattenList = l.flatMap {
  case (elem, _elemList) =>
    _elemList.map((elem, _))
}

输出:

List((A,1), (A,2), (A,3), (B,1), (B,2), (B,3))

答案 1 :(得分:1)

如果您想要的是最后每个列表的平均值,则没有必要使用flatMap将它们分解为单个元素。如果使用大列表,则不必要地使用大数据集对大量数据进行混洗。

由于它们已经通过键进行聚合,因此只需使用以下内容对其进行转换:

val l = spark.sparkContext.parallelize(Seq(
  ("A", List(1, 2, 3, 4)),
  ("B", List(5, 6, 7))
))

val avg = l.map(r => {
    (r._1, (r._2.sum.toDouble / r._2.length.toDouble))
})

avg.collect.foreach(println)

请记住,如果您的任何列表的长度为0,此操作都会失败。如果您有一些0长度列表,则必须在地图中放置一个检查条件。

上面的代码为您提供:

(A,2.5)
(B,6.0)

答案 2 :(得分:1)

您可以尝试explode()

scala> val df = List(("A",List(1,2,3,4)),("B",List(5,6,7))).toDF("x","y")
df: org.apache.spark.sql.DataFrame = [x: string, y: array<int>]

scala> df.withColumn("z",explode('y)).show(false)
+---+------------+---+
|x  |y           |z  |
+---+------------+---+
|A  |[1, 2, 3, 4]|1  |
|A  |[1, 2, 3, 4]|2  |
|A  |[1, 2, 3, 4]|3  |
|A  |[1, 2, 3, 4]|4  |
|B  |[5, 6, 7]   |5  |
|B  |[5, 6, 7]   |6  |
|B  |[5, 6, 7]   |7  |
+---+------------+---+


scala> val df2 = df.withColumn("z",explode('y))
df2: org.apache.spark.sql.DataFrame = [x: string, y: array<int> ... 1 more field]

scala> df2.groupBy("x").agg(sum('z)/count('z) ).show(false)
+---+-------------------+
|x  |(sum(z) / count(z))|
+---+-------------------+
|B  |6.0                |
|A  |2.5                |
+---+-------------------+


scala>