Spark按照值排序集合

时间:2014-07-09 14:38:55

标签: sorting apache-spark word-count

我在尝试这个教程http://spark.apache.org/docs/latest/quick-start.html 我首先从文件

创建了一个集合
textFile = sc.textFile("README.md")

然后我尝试了一个命令来解决这些问题:

wordCounts = textFile.flatMap(lambda line: line.split()).map(lambda word: (word, 1)).reduceByKey(lambda a, b: a+b)

打印收藏品:

 wordCounts.collect()

我找到了如何使用命令sortByKey逐字排序。我想知道如何通过值进行排序可以做同样的事情,在这种情况下,文档中出现一个单词的数字。

11 个答案:

答案 0 :(得分:32)

排序通常应该在调用collect()之前完成,因为它会将数据集返回给驱动程序,这也就是在java中编写hadoop map-reduce作业的方式,这样就可以写出你想要的最终输出(通常)到HDFS。使用spark API,这种方法可以灵活地将输出写入" raw"表单所需的表单,例如可以用作进一步处理的输入的文件。

在收集()之前使用spark的scala API排序可以按照eliasah的建议完成,并使用Tuple2.swap()两次,一次在排序之前,一次之后为了生成一个元组列表排序以第二个字段(名为_2)的递增或递减顺序,并包含第一个字段(名为_1)中的字数。下面是一个如何在spark-shell中编写脚本的示例:

// this whole block can be pasted in spark-shell in :paste mode followed by <Ctrl>D
val file = sc.textFile("some_local_text_file_pathname")
val wordCounts = file.flatMap(line => line.split(" "))
  .map(word => (word, 1))
  .reduceByKey(_ + _, 1)  // 2nd arg configures one task (same as number of partitions)
  .map(item => item.swap) // interchanges position of entries in each tuple
  .sortByKey(true, 1) // 1st arg configures ascending sort, 2nd arg configures one task
  .map(item => item.swap)

为了颠倒排序的顺序,使用sortByKey(false,1),因为它的第一个arg是升序的布尔值。它的第二个参数是任务数(等于分区数),它被设置为1,用于测试一个只需要一个输出数据文件的小输入文件; reduceByKey也接受这个可选参数。

在此之后,wordCounts RDD可以作为文本文件保存到具有saveAsTextFile(directory_pathname)的目录中,其中将存放一个或多个part-xxxxx文件 (从part-00000开始)取决于为作业配置的Reducer数量(每个reducer 1个输出数据文件),_SUCCESS文件取决于作业是否成功以及.crc文件。

使用pyspark一个非常类似于上面显示的scala脚本的python脚本会产生实际上相同的输出。这是pyspark版本,演示按值排序集合:

file = sc.textFile("file:some_local_text_file_pathname")
wordCounts = file.flatMap(lambda line: line.strip().split(" ")) \
    .map(lambda word: (word, 1)) \
    .reduceByKey(lambda a, b: a + b, 1) \ # last arg configures one reducer task
    .map(lambda (a, b): (b, a)) \
    .sortByKey(1, 1) \ # 1st arg configures ascending sort, 2nd configures 1 task
    .map(lambda (a, b): (b, a))

为了按降序排序bybyKey,它的第一个arg应为0.由于python捕获前导和尾随空格作为数据,因此在分割空格上的每一行之前插入strip(),但这不是必须使用spark-shell / scala 。

wordCount的spark和python版本输出的主要区别在于spark输出(word,3)python输出(u&#39; word&#39;,3)。

有关spark RDD方法的更多信息,请参阅py http://spark.apache.org/docs/1.1.0/api/python/pyspark.rdd.RDD-class.html和scala https://spark.apache.org/docs/latest/api/scala/#org.apache.spark.rdd.RDD

在spark-shell中,在wordCounts上运行collect()会将它从RDD转换为Array [(String,Int)] = Array [Tuple2(String,Int)],它本身可以在第二个字段上排序每个Tuple2元素使用:

Array.sortBy(_._2) 

sortBy也采用了一个可选的隐式数学。像罗密欧基恩兹勒这样的订购论证在之前对这个问题的答案中表现出来。 Array.sortBy(_._ 2)将在其_2字段上对Array Tuple2元素进行反向排序,只需在运行map-reduce脚本之前定义隐式反向排序,因为它会覆盖Int的预先存在的排序。由Romeo Kienzler定义的反向排序是:

// for reverse order
implicit val sortIntegersByString = new Ordering[Int] {
  override def compare(a: Int, b: Int) = a.compare(b)*(-1)
}

定义此反向排序的另一种常用方法是反转a和b的顺序并删除比较定义右侧的(-1):

// for reverse order
implicit val sortIntegersByString = new Ordering[Int] {
  override def compare(a: Int, b: Int) = b.compare(a)
}   

答案 1 :(得分:20)

以更加pythonic的方式进行。

# In descending order
''' The first parameter tells number of elements
    to be present in output.
''' 
data.takeOrdered(10, key=lambda x: -x[1])
# In Ascending order
data.takeOrdered(10, key=lambda x: x[1])

答案 2 :(得分:6)

对于那些希望获得按值排序的前N个元素的人:

theRDD.takeOrdered(N, lambda (key, value): -1 * len(value))

如果您希望按字符串长度排序。

另一方面,如果值已经采用适合您所需订购的形式,那么:

theRDD.takeOrdered(N, lambda (key, value): -1 * value)

就足够了。

答案 3 :(得分:5)

你可以这样做

// for reverse order
implicit val sortIntegersByString = new Ordering[Int] {
    override def compare(a: Int, b: Int) = a.compare(b)*(-1)
}

counts.collect.toSeq.sortBy(_._2)

所以基本上你将RDD转换为序列并使用sort方法对其进行排序。

上面的块全局更改排序行为以获得降序排序。

答案 4 :(得分:4)

我认为你可以使用通用的sortBy转换(不是一个动作,即它返回一个RDD而不是一个数组),记录为here

所以在你的情况下,你可以做到

wordCounts.sortBy(lambda (word, count): count)

答案 5 :(得分:3)

按值对输出进行排序的最简单方法。在reduceByKey之后,您可以像键一样将输出交换为值作为键,然后您可以使用降序排序错误排序的sortByKey方法。默认情况下,它将按升序排序。

 val test=textFile.flatMap(line=> line.split(" ")).map(word=> (word, 1)).reduceByKey(_ + _).map(item => item.swap).sortByKey(false)

答案 6 :(得分:2)

@kef for python的解决方案是......

以下内容需要更改 -

.map(lambda (a, b): (b, a))

.map(lambda a: (a[1], a[0]))

答案 7 :(得分:1)

 wordCounts.map(lambda (a,b) : (b,a)).sortByKey(ascending=False).map(lambda (a,b) : (b,a)).collect()

此解决方案有效,因为wordCount rdd的每一行都如下所示:

(WORD,COUNT)

第一张地图生成一个rdd,其中元组的顺序相反,即现在它们看起来像这样

(COUNT,WORD)

现在当我们执行sortByKey时,COUNT被视为我们想要的键。 然后,第二个映射将现在已排序的第二个rdd映射回原始格式

(WORD,COUNT)

对于每一行但不是现在行按字数排序。

这里隐含的假设是映射不会改变RDD行的顺序,否则第二个映射可能会混乱排序。

答案 8 :(得分:0)

我设法用Python解决了这个问题。所以我创建了一对配对值列表并按值排序:

out = wordCounts.collect()
outSort = sorted(out, key=lambda word:word[1])

答案 9 :(得分:0)

使用SCALA进行sortByValue的更好方法是

val count = oozie.flatMap(line => line.split(" ")).map(word => (word,1)).reduceByKey(_ + _).sortBy(x => x._2)

x._2代表任何列表x的第二个元素。

以降序“ -x._2”进行排序

scala> val count = oozie.flatMap(line => line.split(" ")).map(word => (word,1)).reduceByKey(_ + _).sortBy(x => -x._2)

count: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[26] at sortBy at <console>:25

scala> count.take(10)
res6: Array[(String, Int)] = Array((the,4603), (to,1707), (and,1595), (of,1337), (a,1319), (Oozie,1302), (in,1131), (.,994), (is,956), (for,753))

答案 10 :(得分:-2)

Python 方式:现在您可以编写一个映射来获取键(第一个)位置的值(第二个元素)-> 按该键(含义值)排序-> 再次更改位置。简单:)

wordCounts.map(lambda pair: (pair[1], pair[0])).sortByKey().map(lambda pair: (pair[1], pair[0]))