J'utilise la commande suivante pour remplir un RDD avec un groupe de tableaux contenant 2 chaînes ["filename", "content"].
Maintenant, je veux parcourir chacune de ces occurrences pour faire quelque chose avec chaque nom de fichier et chaque contenu.
val someRDD = sc.wholeTextFiles("hdfs://localhost:8020/user/cloudera/*")
Je n'arrive pas à trouver de documentation sur la façon de procéder.
Donc, ce que je veux, c'est ceci:
foreach occurrence-in-the-rdd{
//do stuff with the array found on loccation n of the RDD
}
Les opérations fondamentales dans Spark sont map
et filter
.
val txtRDD = someRDD filter { case(id, content) => id.endsWith(".txt") }
la txtRDD
ne contiendra plus que les fichiers portant l'extension ".txt"
Et si vous voulez que Word compte ces fichiers, vous pouvez dire
//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)
Vous souhaitez utiliser mapPartitions
lorsque vous devez effectuer une initialisation coûteuse, par exemple, si vous souhaitez effectuer la reconnaissance d'entité nommée avec une bibliothèque telle que les outils Stanford coreNLP.
Maîtrisez map
, filter
, flatMap
et reduce
et vous êtes sur la bonne voie pour maîtriser Spark.
Vous appelez différentes méthodes sur le RDD qui acceptent des fonctions en tant que paramètres.
// 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()
})
}
Notez que les fonctions que vous écrivez acceptent un seul élément RDD en entrée et renvoient des données d'un type uniforme. Vous créez donc un RDD de ce dernier type. Par exemple, countRDD
est un RDD[Int]
, alors que bigRDD
est toujours un RDD[Array[Int]]
.
Il sera probablement tentant à un moment donné d'écrire une foreach
modifiant d'autres données, mais vous devez résister pour les raisons décrites dans cette question et la réponse .
Edit: n'essayez pas d'imprimer une grande RDD
s
Plusieurs lecteurs ont demandé à utiliser collect()
et println()
pour voir leurs résultats, comme dans l'exemple ci-dessus. Bien sûr, cela ne fonctionne que si vous utilisez un mode interactif tel que Spark REPL (read-eval-print-loop.) Il est préférable d'appeler collect()
sur le RDD pour obtenir un tableau séquentiel pour une impression ordonnée. Mais collect()
peut ramener trop de données et, dans tous les cas, trop peut être imprimé. Voici quelques moyens alternatifs d’obtenir un aperçu de vos RDD
s’elles sont grandes:
RDD.take()
: Ceci vous donne un contrôle précis sur le nombre d'éléments que vous obtenez mais pas d'où ils viennent - défini comme le "premier", concept traité par diverses autres questions et réponses ici.
// take() returns an Array so no need to collect()
myHugeRDD.take(20).foreach(a => println(a))
RDD.sample()
: Ceci vous permet (approximativement) de contrôler la fraction de résultats que vous obtenez, si l’échantillonnage utilise le remplacement, et même éventuellement la graine de nombres aléatoires.
// sample() does return an RDD so you may still want to collect()
myHugeRDD.sample(true, 0.01).collect().foreach(a => println(a))
RDD.takeSample()
: il s'agit d'un hybride: utilisant un échantillonnage aléatoire que vous pouvez contrôler, mais vous permettant tous les deux de spécifier le nombre exact de résultats et renvoyant une Array
.
// takeSample() returns an Array so no need to collect()
myHugeRDD.takeSample(true, 20).foreach(a => println(a))
RDD.count()
: Parfois, la meilleure idée provient du nombre d'éléments avec lesquels vous vous êtes retrouvé - je le fais souvent en premier.
println(myHugeRDD.count())
Je voudrais essayer d'utiliser une fonction de mappage de partition. Le code ci-dessous montre comment un ensemble de données RDD entier peut être traité dans une boucle afin que chaque entrée remplisse la même fonction. Je crains de ne pas connaître Scala. Tout ce que j’ai à offrir est donc du code Java. Cependant, il ne devrait pas être très difficile de le traduire en 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();
ce que la valeur wholeTextFiles
est une paire RDD:
def wholeTextFiles (chemin: String, minPartitions: Int): RDD [(String, String)]
Lisez un répertoire de fichiers texte à partir de HDFS, d'un système de fichiers local (disponible sur tous les nœuds) ou de tout URI de système de fichiers pris en charge par Hadoop. Chaque fichier est lu en tant qu’enregistrement unique et renvoyé dans une paire clé-valeur, où la clé est le chemin de chaque fichier et la valeur, le contenu de chaque fichier.
Voici un exemple de lecture des fichiers sur un chemin local, puis d’impression de chaque nom de fichier et de son contenu.
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));
le résultat:
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}
ou convertir d’abord le RDD Pair en RDD
sc.wholeTextFiles("file:///Users/leon/Documents/test/")
.map(t => t._2)
.collect
.foreach { x => println(x)}
le résultat:
{"name":"tom","age":12}
{"name":"john","age":22}
{"name":"leon","age":18}
Et je pense que wholeTextFiles
est plus conforme pour les petits fichiers.
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
}