Spark:按键获得前N名

时间:2015-05-11 10:08:20

标签: scala apache-spark

假设我有一个PairRDD(显然在现实生活中有更多数据,假设有数百万条记录):

val scores = sc.parallelize(Array(
      ("a", 1),  
      ("a", 2), 
      ("a", 3), 
      ("b", 3), 
      ("b", 1), 
      ("a", 4),  
      ("b", 4), 
      ("b", 2)
))

生成每个密钥前2个分数的RDD最有效的方法是什么?

val top2ByKey = ...
res3: Array[(String, Int)] = Array((a,4), (a,3), (b,4), (b,3))

4 个答案:

答案 0 :(得分:11)

我认为这应该非常有效:

根据OP评论编辑:

scores.mapValues(p => (p, p)).reduceByKey((u, v) => {
  val values = List(u._1, u._2, v._1, v._2).sorted(Ordering[Int].reverse).distinct
  if (values.size > 1) (values(0), values(1))
  else (values(0), values(0))
}).collect().foreach(println)

答案 1 :(得分:11)

从版本1.4开始,有一种使用MLLib执行此操作的内置方法:https://github.com/apache/spark/blob/master/mllib/src/main/scala/org/apache/spark/mllib/rdd/MLPairRDDFunctions.scala

import org.apache.spark.mllib.rdd.MLPairRDDFunctions.fromPairRDD
scores.topByKey(2)

答案 2 :(得分:2)

略微修改输入数据。

val scores = sc.parallelize(Array(
      ("a", 1),  
      ("a", 2), 
      ("a", 3), 
      ("b", 3), 
      ("b", 1), 
      ("a", 4),  
      ("b", 4), 
      ("b", 2),
      ("a", 6),
      ("b", 8)
    ))

我解释如何一步一步地做到这一点:

1.按键分组以创建数组

scores.groupByKey().foreach(println)  

结果:

(b,CompactBuffer(3, 1, 4, 2, 8))
(a,CompactBuffer(1, 2, 3, 4, 6))

如您所见,每个值本身都是一个数字数组。 CompactBuffer只是优化的阵列。

2.对于每个键,反向排序值包含

的数字列表
scores.groupByKey().map({ case (k, numbers) => k -> numbers.toList.sorted(Ordering[Int].reverse)} ).foreach(println)

结果:

(b,List(8, 4, 3, 2, 1))
(a,List(6, 4, 3, 2, 1))

3.只保留第2步中的前2个元素,它们将是列表中的前2个分数

scores.groupByKey().map({ case (k, numbers) => k -> numbers.toList.sorted(Ordering[Int].reverse).take(2)} ).foreach(println)

结果:

(a,List(6, 4))
(b,List(8, 4))

4.Flat map为每个键和最高分创建新的配对RDD

scores.groupByKey().map({ case (k, numbers) => k -> numbers.toList.sorted(Ordering[Int].reverse).take(2)} ).flatMap({case (k, numbers) => numbers.map(k -> _)}).foreach(println)

结果:

(b,8)
(b,4)
(a,6)
(a,4)

5.可选步骤 - 如果需要,按键排序

scores.groupByKey().map({ case (k, numbers) => k -> numbers.toList.sorted(Ordering[Int].reverse).take(2)} ).flatMap({case (k, numbers) => numbers.map(k -> _)}).sortByKey(false).foreach(println)

结果:

(a,6)
(a,4)
(b,8)
(b,4)

希望,这种解释有助于理解逻辑。

答案 3 :(得分:0)

 scores.reduceByKey(_ + _).map(x => x._2 -> x._1).sortByKey(false).map(x => x._2 -> x._1).take(2).foreach(println)