假设我有这个输入数据:
["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]
然后groupByKey
,给我(K, Iterable<V>)
对
[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:这是所有的伪代码。我使用月份名称来使这个可读,但为了清楚起见,我已将其更改为实际日期。
答案 0 :(得分:5)
总的来说,我会做以下几点。 (可能不是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)
然后按月分组。我没有对此进行过测试,我也不认为这种行为是有保证的,但我希望在实践中,即使在分组后,也会按顺序遇到元素。