我使用以下命令用一堆包含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
}
答案 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。例如,countRDD
是RDD[Int]
,而bigRDD
仍然是RDD[Array[Int]]
。
在某些时候写一个foreach
来修改其他一些数据可能很诱人,但你应该出于in this question and answer描述的原因而抵制。
修改:不要尝试打印大型RDD
有几位读者询问如何使用collect()
和println()
查看结果,如上例所示。当然,这仅适用于您在Spark REPL(read-eval-print-loop)等交互模式下运行的情况。最好在RDD上调用collect()
以获得有序打印的顺序数组。但collect()
可能会带回太多数据,无论如何都可能会打印太多数据。如果它们很大,可以通过以下方式深入了解RDD
:
RDD.take()
:这可以很好地控制你获得的元素数量,但不能控制它们的来源 - 定义为“第一”元素,这是一个由其他各种问题处理的概念,答案在这里。
// take() returns an Array so no need to collect()
myHugeRDD.take(20).foreach(a => println(a))
RDD.sample()
:这可以让您(粗略地)控制您获得的结果部分,无论采样是否使用替换,甚至是可选的随机数种子。
// sample() does return an RDD so you may still want to collect()
myHugeRDD.sample(true, 0.01).collect().foreach(a => println(a))
RDD.takeSample()
:这是混合型:使用您可以控制的随机抽样,但两者都允许您指定结果的确切数量并返回Array
。
// takeSample() returns an Array so no need to collect()
myHugeRDD.takeSample(true, 20).foreach(a => println(a))
RDD.count()
:有时最好的洞察力来自你最终获得了多少元素 - 我经常先这样做。
println(myHugeRDD.count())
答案 1 :(得分:7)
Spark中的基本操作是map
和filter
。
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工具这样的库执行命名实体识别。
掌握map
,filter
,flatMap
和reduce
,您正在掌握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
}