Apache spark 在Spark中对可重复值进行排序
假设我有以下输入数据:Apache spark 在Spark中对可重复值进行排序,apache-spark,Apache Spark,假设我有以下输入数据: ["example.com", Date(2000, 1, 1)] : 100, ["example.com", Date(2000, 2, 1)]: 30, ["example.com", Date(2000, 3, 1)]: 5, ["xyz.com", Date(2000, 1, 1)]: 20, ["xyz.com", Date(2000, 2, 1)]: 10, ["xyz.com", Date(2000, 3, 1)]: 60] 我想按日期分组(降序),然
["example.com", Date(2000, 1, 1)] : 100,
["example.com", Date(2000, 2, 1)]: 30,
["example.com", Date(2000, 3, 1)]: 5,
["xyz.com", Date(2000, 1, 1)]: 20,
["xyz.com", Date(2000, 2, 1)]: 10,
["xyz.com", Date(2000, 3, 1)]: 60]
我想按日期分组(降序),然后按计数排序,给我每个日期的域的有序列表
我想以以下方式结束:
Date(2000, 1, 1), [["example.com", 100], ["xyz.com", 20]]
Date(2000, 2, 1), [["example.com", 30], ["xyz.com", 10]]
Date(2000, 3, 1), [["xyz.com", 60], ["example.com", 5]]
这似乎是一个正常的用例,但我在《编程指南》中找不到这样做的方法
我可以map
[[domain,date]count]->[date,[domain,count]]
这将给我(K,V)
对
Date(2000, 1, 1), ["example.com", 100],
Date(2000, 2, 1), ["example.com", 30],
Date(2000, 3, 1), ["example.com", 5],
Date(2000, 1, 1), ["xyz.com", 20],
Date(2000, 2, 1), ["xyz.com", 10],
Date(2000, 3, 1), ["xyz.com", 60]
[Date(2000, 1, 1), [["example.com", 100], ["xyz.com", 20]]
[Date(2000, 2, 1), [["example.com", 30], ["xyz.com", 10]]
[Date(2000, 3, 1), [["example.com", 5], ["xyz.com", 60]]
然后groupByKey
,给我(K,Iterable)
对
Date(2000, 1, 1), ["example.com", 100],
Date(2000, 2, 1), ["example.com", 30],
Date(2000, 3, 1), ["example.com", 5],
Date(2000, 1, 1), ["xyz.com", 20],
Date(2000, 2, 1), ["xyz.com", 10],
Date(2000, 3, 1), ["xyz.com", 60]
[Date(2000, 1, 1), [["example.com", 100], ["xyz.com", 20]]
[Date(2000, 2, 1), [["example.com", 30], ["xyz.com", 10]]
[Date(2000, 3, 1), [["example.com", 5], ["xyz.com", 60]]
然后如何在键内进行排序
请原谅伪代码,我正在使用Flambo Clojure包装器,我不想仅仅为了问这个问题而用Java重写它
编辑:每个Iterable(即域列表)可能太大,无法放入内存
EDIT2:这都是伪代码。我使用月份名称使其可读,但为了清晰起见,我已将其改为真实日期。在大范围内,我将执行以下操作。(可能不是100%正确,因为我没有编译它,但很接近。)为了简单起见,我假设您从一个
RDD[((String,String,Int)]
开始
首先,groupBy
使用以下内容显示月份:
.groupBy { case ((_, month), _) => month }
并在值中去掉月份:
.mapValues(_.map { case ((domain, _), count) => (domain, count) })
如果需要按月订购,请定义月份的订购:
def monthOfYear(month: String): Int =
month match {
case "January" => 1
case "February" => 2
...
}
并按月对RDD进行排序:
.sortBy { case (month, _) => monthOfYear(month) }
并按递减计数对域进行排序:
.mapValues(_.toSeq.sortBy{ case (domain, count) => count }(Ordering[Int].reverse))
.sortBy(p => p._2, false)
这既直接又有效,但存在一个问题,即一个月内的所有域计数对都必须适合内存
相反,您可以通过按计数降序排序来重新开始:
.mapValues(_.toSeq.sortBy{ case (domain, count) => count }(Ordering[Int].reverse))
.sortBy(p => p._2, false)
然后按月分组。我还没有对此进行测试,我也不认为这种行为是有保证的,但我希望在实践中,即使在分组之后,元素也会按计数顺序出现。它们不适合内存。谢谢您的回答。正如我所说的,我不认为域列表将适合内存,我想利用Spark的设施来做到这一点。此外,我几乎100%确信,在一个团队中,订单不会被保留。哦,还有,月份是真实的(可比较的)日期。我只是想说明一下。好吧,忘了日期那部分。嗯,如果您真正的最终目标只是按计数排名前N,那么您可以使用
combineByKey
来实现这一目标,而不会出现内存问题。