web-dev-qa-db-fra.com

Spark get collection triée par valeur

J'essayais ce tutoriel http://spark.Apache.org/docs/latest/quick-start.html J'ai d'abord créé une collection à partir d'un fichier

textFile = sc.textFile("README.md")

Alors j'ai essayé une commande pour counder les mots:

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

Pour imprimer la collection:

 wordCounts.collect()

J'ai trouvé comment le trier par Word en utilisant la commande sortByKey. Je me demandais comment il pourrait être possible de faire la même chose pour le tri par valeur, que dans ce cas dans le nombre qu'un mot apparaît dans le document.

33
user3702916

Le tri doit généralement être effectué avant l'appel de collect (), car cela renvoie l'ensemble de données au programme du pilote. C'est également la manière dont un travail de réduction de carte hadoop est programmé en Java afin que le résultat final souhaité soit écrit (généralement). à HDFS. Avec l’API spark, cette approche offre la possibilité d’écrire la sortie sous la forme "brute" où vous le souhaitez, par exemple dans un fichier où elle pourrait être utilisée comme entrée pour un traitement ultérieur.

Vous pouvez utiliser le tri de l'API scala de spark avant collect () en suivant la suggestion d'eliasah et en utilisant Tuple2.swap () deux fois, une fois avant et une fois après afin de produire une liste de n-uplets triés par ordre croissant ou décroissant de leur second champ (qui est nommé _2) et contient le nombre de mots dans leur premier champ (nommé _1). Vous trouverez ci-dessous un exemple de script dans 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)

Pour inverser l'ordre du tri, utilisez sortByKey (false, 1) puisque son premier argument est la valeur booléenne de croissant. Son deuxième argument est le nombre de tâches (équivalent au nombre de partitions) qui est défini sur 1 pour les tests avec un petit fichier d'entrée où un seul fichier de données de sortie est souhaité; reductionByKey prend également cet argument optionnel. 

Après cela, le wordCounts RDD peut être sauvegardé sous forme de fichiers texte dans un répertoire avec saveAsTextFile (directory_pathname) dans lequel seront déposés un ou plusieurs fichiers part-xxxxx (À partir de part-00000) en fonction du nombre de réducteurs configurés pour le job (1 fichier de données de sortie par réducteur), un fichier _SUCCESS selon que le travail a réussi ou non et des fichiers .crc. 

En utilisant pyspark, un script python très similaire au script scala présenté ci-dessus produit un résultat identique. Voici la version de pyspark démontrant le tri d’une collection par valeur:

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

Afin de trier par ordre décroissant, son premier argument doit être 0. Puisque python capture les espaces de début et de fin sous forme de données, strip () est inséré avant de scinder chaque ligne sur des espaces, mais ceci n'est pas nécessaire avec spark-Shell/scala. 

La principale différence entre les sorties de la version spark et python de wordCount est celle où spark produit (Word, 3) les sorties python (u'Word ', 3).

Pour plus d'informations sur les méthodes spark RDD, voir http://spark.Apache.org/docs/1.1.0/api/python/pyspark.rdd.RDD-class.html pour python et https: // spark.Apache.org/docs/latest/api/scala/#org.Apache.spark.rdd.RDD pour scala.

Dans spark-Shell, l'exécution de collect () sur wordCounts le transforme d'un RDD en un tableau [(String, Int)] = Array [Tuple2 (String, Int)], qui peut lui-même être trié sur le second champ de chaque élément Tuple2. en utilisant:

Array.sortBy(_._2) 

sortBy prend également une mathématique implicite optionnelle. Un argument semblable à celui que Romeo Kienzler a montré dans une réponse précédente à cette question. Array.sortBy (_._ 2) effectuera un tri inverse des éléments Array Tuple2 sur leurs champs _2 simplement en définissant un ordre inverse implicite avant d'exécuter le script map-reduction car il écrase l'ordre existant d'Int. Le reverse int Ordering déjà défini par Romeo Kienzler est:

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

Une autre manière courante de définir cet ordre inverse consiste à inverser l'ordre de a et b et à supprimer le (-1) du côté droit de la définition de comparaison:

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

Le faire de manière plus pythonique.

# 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])
20
kiran6

Pour ceux qui cherchent à obtenir les N premiers éléments ordonnés par valeur:

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

si vous souhaitez commander par longueur de chaîne.

D'autre part, si les valeurs sont déjà dans le formulaire qui convient à votre ordre souhaité, alors:

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

suffirait.

5
okello

vous pouvez le faire de cette façon

// 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)

Donc, fondamentalement, vous convertissez votre RDD en séquence et utilisez la méthode de tri pour le trier.

Le bloc ci-dessus modifie globalement le comportement de tri afin d'obtenir un ordre de tri décroissant.

4
Romeo Kienzler

Je pense que vous pouvez utiliser la transformation sortBy générique (pas une action, c’est-à-dire qu’elle renvoie un RDD et non un tableau) documentée ici .

Donc, dans votre cas, vous pourriez faire

wordCounts.sortBy(lambda (Word, count): count)
4
stackoverflower

Le moyen le plus simple de trier la sortie par valeurs. Après la réduction, vous pouvez échanger la sortie comme clé en valeur et valeur comme clé, puis vous pouvez appliquer la méthode sortByKey où false trie dans l'ordre décroissant. Par défaut, le tri sera effectué dans l'ordre croissant.

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

La solution de @kef pour python est sur place ... 

Ce qui suit doit être changé -

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

à

.map(lambda a: (a[1], a[0]))
2
Sud
 wordCounts.map(lambda (a,b) : (b,a)).sortByKey(ascending=False).map(lambda (a,b) : (b,a)).collect()

Cette solution fonctionne parce que chaque ligne d'un wordCount rdd ressemble à ceci:

(Word, COUNT) 

la première carte produit un RDD avec l’ordre des tuples inversés, c’est-à-dire qu’ils ressemblent à ceci

(COUNT, Word) 

Maintenant, quand nous faisons sortByKey, le compte est pris comme la clé, ce que nous voulons. La seconde carte mappe ensuite le deuxième dossier maintenant trié au format original de 

(Word, COUNT)

pour chaque ligne mais pas maintenant, les lignes sont triées selon le nombre de mots.

Une hypothèse implicite est que le mappage ne modifie pas l'ordre des lignes RDD, sinon le deuxième mappage risque de gâcher le tri.

1
johannzhaojohann

Une meilleure façon de faire sortByValue en utilisant SCALA est

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

x._2 représente le deuxième élément d'une liste x.

Pour faire le tri dans l'ordre décroissant "-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))
0
Suren

J'ai réussi à le résoudre en utilisant Python. Je crée donc une liste de valeurs de paires et la trie par valeur:

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