如何在apache spark(scala)中迭代RDD&#s; s

时间:2014-09-18 14:00:15

标签: scala apache-spark

我使用以下命令用一堆包含2个字符串的数组填充RDD [" filename"," content"]。

现在我想迭代每一个事件,对每个文件名和内容做一些事情。

val someRDD = sc.wholeTextFiles("hdfs://localhost:8020/user/cloudera/*")

我似乎无法找到有关如何执行此操作的任何文档。

所以我想要的是:

foreach occurrence-in-the-rdd{
   //do stuff with the array found on loccation n of the RDD
} 

5 个答案:

答案 0 :(得分:26)

您可以在RDD上调用接受函数作为参数的各种方法。

// set up an example -- an RDD of arrays
val sparkConf = new SparkConf().setMaster("local").setAppName("Example")
val sc = new SparkContext(sparkConf)
val testData = Array(Array(1,2,3), Array(4,5,6,7,8))
val testRDD = sc.parallelize(testData, 2)

// Print the RDD of arrays.
testRDD.collect().foreach(a => println(a.size))

// Use map() to create an RDD with the array sizes.
val countRDD = testRDD.map(a => a.size)

// Print the elements of this new RDD.
countRDD.collect().foreach(a => println(a))

// Use filter() to create an RDD with just the longer arrays.
val bigRDD = testRDD.filter(a => a.size > 3)

// Print each remaining array.
bigRDD.collect().foreach(a => {
    a.foreach(e => print(e + " "))
    println()
  })
}

请注意,您编写的函数接受单个RDD元素作为输入,并返回某种统一类型的数据,因此您创建后一种类型的RDD。例如,countRDDRDD[Int],而bigRDD仍然是RDD[Array[Int]]

在某些时候写一个foreach来修改其他一些数据可能很诱人,但你应该出于in this question and answer描述的原因而抵制。

修改:不要尝试打印大型RDD

有几位读者询问如何使用collect()println()查看结果,如上例所示。当然,这仅适用于您在Spark REPL(read-eval-print-loop)等交互模式下运行的情况。最好在RDD上调用collect()以获得有序打印的顺序数组。但collect()可能会带回太多数据,无论如何都可能会打印太多数据。如果它们很大,可以通过以下方式深入了解RDD

  1. RDD.take():这可以很好地控制你获得的元素数量,但不能控制它们的来源 - 定义为“第一”元素,这是一个由其他各种问题处理的概念,答案在这里。

    // take() returns an Array so no need to collect()
    myHugeRDD.take(20).foreach(a => println(a))
    
  2. RDD.sample():这可以让您(粗略地)控制您获得的结果部分,无论采样是否使用替换,甚至是可选的随机数种子。

    // sample() does return an RDD so you may still want to collect()
    myHugeRDD.sample(true, 0.01).collect().foreach(a => println(a))
    
  3. RDD.takeSample():这是混合型:使用您可以控制的随机抽样,但两者都允许您指定结果的确切数量并返回Array

    // takeSample() returns an Array so no need to collect() 
    myHugeRDD.takeSample(true, 20).foreach(a => println(a))
    
  4. RDD.count():有时最好的洞察力来自你最终获得了多少元素 - 我经常先这样做。

    println(myHugeRDD.count())       
    

答案 1 :(得分:7)

Spark中的基本操作是mapfilter

val txtRDD = someRDD filter { case(id, content) => id.endsWith(".txt") }

txtRDD现在只包含扩展名为" .txt"

的文件

如果你想对这些文件进行字数统计,你可以说

//split the documents into words in one long list
val words = txtRDD flatMap { case (id,text) => text.split("\\s+") }
// give each word a count of 1
val wordT = words map (x => (x,1))  
//sum up the counts for each word
val wordCount = wordsT reduceByKey((a, b) => a + b)

当您需要执行一些昂贵的初始化时,您希望使用mapPartitions - 例如,如果您想使用像Stanford coreNLP工具这样的库执行命名实体识别。

掌握mapfilterflatMapreduce,您正在掌握Spark。

答案 2 :(得分:2)

我会尝试使用分区映射功能。下面的代码显示了如何在循环中处理整个RDD数据集,以便每个输入都通过相同的函数。我恐怕我对Scala一无所知,所以我所提供的一切都是 java 代码。但是,将它翻译成scala应该不是很困难。

JavaRDD<String> res = file.mapPartitions(new FlatMapFunction <Iterator<String> ,String>(){ 
      @Override
      public Iterable<String> call(Iterator <String> t) throws Exception {  

          ArrayList<String[]> tmpRes = new ArrayList <>();
          String[] fillData = new String[2];

          fillData[0] = "filename";
          fillData[1] = "content";

          while(t.hasNext()){
               tmpRes.add(fillData);  
          }

          return Arrays.asList(tmpRes);
      }

}).cache();

答案 3 :(得分:1)

wholeTextFiles返回的是一对RDD:

  

def wholeTextFiles(path:String,minPartitions:Int):RDD [(String,String)]

     

从HDFS读取文本文件目录,本地文件系统(在所有节点上都可用)或任何Hadoop支持的文件系统URI。每个文件都作为单个记录读取,并以键值对的形式返回,其中键是每个文件的路径,值是每个文件的内容。

以下是在本地路径中读取文件然后打印每个文件名和内容的示例。

val conf = new SparkConf().setAppName("scala-test").setMaster("local")
val sc = new SparkContext(conf)
sc.wholeTextFiles("file:///Users/leon/Documents/test/")
  .collect
  .foreach(t => println(t._1 + ":" + t._2));

结果:

file:/Users/leon/Documents/test/1.txt:{"name":"tom","age":12}

file:/Users/leon/Documents/test/2.txt:{"name":"john","age":22}

file:/Users/leon/Documents/test/3.txt:{"name":"leon","age":18}

或首先将Pair RDD转换为RDD

sc.wholeTextFiles("file:///Users/leon/Documents/test/")
  .map(t => t._2)
  .collect
  .foreach { x => println(x)}

结果:

{"name":"tom","age":12}

{"name":"john","age":22}

{"name":"leon","age":18}

我认为wholeTextFiles更符合小文件。

答案 4 :(得分:0)

for (element <- YourRDD)
{
     // do what you want with element in each iteration, and if you want the index of element, simply use a counter variable in this loop beginning from 0 
     println (element._1) // this will print all filenames
}