我想在N
的groupByKey之后获得热门RDD
项,并将topNPerGroup
的类型(在下面)转换为RDD[(String, Int)]
其中List[Int]
1}}值为flatten
data
是
val data = sc.parallelize(Seq("foo"->3, "foo"->1, "foo"->2,
"bar"->6, "bar"->5, "bar"->4))
每组的前N
个项目计算如下:
val topNPerGroup: RDD[(String, List[Int]) = data.groupByKey.map {
case (key, numbers) =>
key -> numbers.toList.sortBy(-_).take(2)
}
结果是
(bar,List(6, 5))
(foo,List(3, 2))
由
打印topNPerGroup.collect.foreach(println)
如果我达到,topNPerGroup.collect.foreach(println)
将生成(预期结果!)
(bar, 6)
(bar, 5)
(foo, 3)
(foo, 2)
答案 0 :(得分:7)
Spark 1.4.0解决了这个问题。
查看https://github.com/apache/spark/commit/5e6ad24ff645a9b0f63d9c0f17193550963aa0a7
这会将BoundedPriorityQueue
与aggregateByKey
def topByKey(num: Int)(implicit ord: Ordering[V]): RDD[(K, Array[V])] = {
self.aggregateByKey(new BoundedPriorityQueue[V](num)(ord))(
seqOp = (queue, item) => {
queue += item
},
combOp = (queue1, queue2) => {
queue1 ++= queue2
}
).mapValues(_.toArray.sorted(ord.reverse)) // This is an min-heap, so we reverse the order.
}
答案 1 :(得分:6)
我最近一直在努力解决同样的问题,但我的需求略有不同,因为我需要每个键的最高K值以及(key: Int, (domain: String, count: Long))
这样的数据集。虽然您的数据集更简单,但仍然存在扩展/性能问题,如文档中所述,使用groupByKey。
在(K,V)对的数据集上调用时,返回(K, 对(可迭代)对。注意:如果要进行分组以执行 使用每个密钥的聚合(例如总和或平均值) reduceByKey或combineByKey将产生更好的性能。
在我的情况下,我很快就遇到了问题,因为Iterable
中的(K, Iterable<V>)
非常大,&gt; 100万,所以排名和领先的N变得非常昂贵,并产生潜在的记忆问题。
经过一番挖掘后,请参阅下面的参考资料,这是一个完整的示例,使用combineByKey以一种将执行和扩展的方式完成相同的任务。
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
object TopNForKey {
var SampleDataset = List(
(1, ("apple.com", 3L)),
(1, ("google.com", 4L)),
(1, ("stackoverflow.com", 10L)),
(1, ("reddit.com", 15L)),
(2, ("slashdot.org", 11L)),
(2, ("samsung.com", 1L)),
(2, ("apple.com", 9L)),
(3, ("microsoft.com", 5L)),
(3, ("yahoo.com", 3L)),
(3, ("google.com", 4L)))
//sort and trim a traversable (String, Long) tuple by _2 value of the tuple
def topNs(xs: TraversableOnce[(String, Long)], n: Int) = {
var ss = List[(String, Long)]()
var min = Long.MaxValue
var len = 0
xs foreach { e =>
if (len < n || e._2 > min) {
ss = (e :: ss).sortBy((f) => f._2)
min = ss.head._2
len += 1
}
if (len > n) {
ss = ss.tail
min = ss.head._2
len -= 1
}
}
ss
}
def main(args: Array[String]): Unit = {
val topN = 2
val sc = new SparkContext("local", "TopN For Key")
val rdd = sc.parallelize(SampleDataset).map((t) => (t._1, t._2))
//use combineByKey to allow spark to partition the sorting and "trimming" across the cluster
val topNForKey = rdd.combineByKey(
//seed a list for each key to hold your top N's with your first record
(v) => List[(String, Long)](v),
//add the incoming value to the accumulating top N list for the key
(acc: List[(String, Long)], v) => topNs(acc ++ List((v._1, v._2)), topN).toList,
//merge top N lists returned from each partition into a new combined top N list
(acc: List[(String, Long)], acc2: List[(String, Long)]) => topNs(acc ++ acc2, topN).toList)
//print results sorting for pretty
topNForKey.sortByKey(true).foreach((t) => {
println(s"key: ${t._1}")
t._2.foreach((v) => {
println(s"----- $v")
})
})
}
}
我在返回的rdd中获得了什么...
(1, List(("google.com", 4L),
("stackoverflow.com", 10L))
(2, List(("apple.com", 9L),
("slashdot.org", 15L))
(3, List(("google.com", 4L),
("microsoft.com", 5L))
参考
https://www.mail-archive.com/user@spark.apache.org/msg16827.html
https://stackoverflow.com/a/8275562/807318
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.PairRDDFunctions
答案 2 :(得分:4)
您的问题有点令人困惑,但我认为这可以满足您的需求:
val flattenedTopNPerGroup =
topNPerGroup.flatMap({case (key, numbers) => numbers.map(key -> _)})
并在repl中打印出你想要的内容:
flattenedTopNPerGroup.collect.foreach(println)
(foo,3)
(foo,2)
(bar,6)
(bar,5)
答案 3 :(得分:0)
只需使用topByKey
:
import org.apache.spark.mllib.rdd.MLPairRDDFunctions._
import org.apache.spark.rdd.RDD
val topTwo: RDD[(String, Int)] = data.topByKey(2).flatMapValues(x => x)
topTwo.collect.foreach(println)
(foo,3)
(foo,2)
(bar,6)
(bar,5)
还可以提供备用Ordering
(此处不需要)。例如,如果您想要n个最小值:
data.topByKey(2)(scala.math.Ordering.by[Int, Int](- _))