在Spark中对可迭代值进行排序

时间:2015-01-07 17:41:07

标签: 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]

我希望按日期分组(降序),然后按计数排序,每个日期给我一个有序的域列表。

我想最终:

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:这是所有的伪代码。我使用月份名称来使这个可读,但为了清楚起见,我已将其更改为实际日期。

1 个答案:

答案 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)

然后按月分组。我没有对此进行过测试,我也不认为这种行为是有保证的,但我希望在实践中,即使在分组后,也会按顺序遇到元素。