Ce étude vise à présenter la platforme Spark et les opérations (Transformation et Action) d'une RDD. Les points abordés sont Spark motivation, Positionnement de Spark dans l'écosystème BigData, Composants de Spark, Drivers et Workers, RDD & caractéristiques, vue logique de spark, vue globale sur les APIs Spark (dépendance et interaction), les différentes opérations de spark. Chaque opération est sur une fiche (objectif, signature, "à retenir" et exemple de code).
Montant moyen du droit d'allocation chômage versé aux demandeurs d'emploi ind...
Spark RDD : Transformations & Actions
1. APACHE SPARK
RDD : TRANSFORMATIONS, ACTIONS PAS À PAS
DR MUSTAPHA MICHRAFY
CONTACT : DATASCIENCE.KM@GMAIL.COM
2. CONTEXTE
Cette étude a été menée dans le cadre des
rencontres de travail organisées au
Laboratoire CERMSEM, Centre d'Économie de
la Sorbonne (CES).
2
M.MICHRAFY
3. PLAN
1. Contexte
2. Objectif et prérequis
3. Spark Définition et motivations
4. Positionnement de Spark dans l’eco-systeme Big Data
5. Composants de spark
6. Spark driver et workers
7. Apache Spark : vue logique et APIs
8. Vue globale sur les API Spark : dépendance et interaction
9. RDD, caractéristiques, création et Operations
10.Opérations de type transformation et action
3
M.MICHRAFY
4. OBJECTIF ET PRÉREQUIS
• Connaissance de l’approche objet
• Connaissance de la programmation
fonctionnelle
• Connaissance du langage Scala
Cette étude vise à mettre en pratique Spark
pas à pas. Il s’agit d’explorer les opérations
(transformations et actions) relatives à la
structure résiliente « RDD ».
PrérequisPrérequisObjectifObjectif
4
M.MICHRAFY
5. SPARK DÉFINITION ET MOTIVATIONS
• Framework open source dédié au calcul distribué
• Extension du design pattern map-reduce
• Différents modes de traitement : interactif et
streaming
• Exécution en mémoire
• Intervient dans le traitement de données dans un
écosystème Big-Data
• Rapide
• 10 fois plus rapide que Hadoop sur disque
• 100 fois plus rapide en mémoire que Hadoop
• Facile à développer
• Riche en terme d’opérations
• Ecriture rapide des programmes
• Mode interactif
• Code concis
• Déploiement flexible : Yarn, Standlone, Local, Mesos
• Stockage : HDFS, S3, Openstack Swift, MapR FS,
Cassandra
• Modèle de développement unifié : Batch, streaming,
interactif
• Multi-langages : Scala, Java, Python, R
Apache spark ?Apache spark ? MotivationsMotivations
5
M.MICHRAFY
8. SPARK DRIVER ET WORKERS
8
SparkContext
Spark driver
Cluster
Manager
Local
Threads
Worker
Executor
Worker
Executor
HDFS, S3, Openstack swift, MapR FS, CassandraHDFS, S3, Openstack swift, MapR FS, Cassandra
• Un programme Spark est composé de deux programmes :
1. Le programme pilote ( driver program )
2. Les programmes travailleurs (workers program)
• Les programmes travailleurs s’exécutent soit sur les nœuds du cluster soit
sur les threads en local
• Le programme pilote interagit avec le cluster via SparkContext
• Pas de communication entre les programmes travailleurs
M.MICHRAFY
9. 9
Spark Core
RDD (Transformations et Action)
Spark SQL Spark MLSpark
Streaming
Spark GraphX SparkR
Système de stockage
Spark supporte plusieurs systèmes de stockage
: HDFS, S3, Openstack swift, MapR FS,
Cassandra
Spark SQL est un module dédié au traitement
des données structurées avec une syntaxe
similaire à SQL. Il permet d’extraire, transformer
et charger des données sous différents formats
(CSV, JSON, Parquet, base de données) et les
exposer pour des requêtes ad-hoc.
Spark ML est une librairie
dédiée aux méthodes
d’apprentissage distribués :
Classification
Clustering
Régression
Filtrage collaboratif
Réduction de dimension
Spark Core est le moteur de calcul et
d’exécution pour la plateforme Spark :
Ordonnancement des taches
Gestion du calcul en mémoire
Recouvrement
Interaction avec le système de stockage
API RDD propose des opérations de type
transformation et Action.
Spark Streaming est dédié au traitement
temps-réel des données en flux. Il offre un
mode de traitement en micro-batch et
supportant différentes sources de données
(Kafka, Flume, Kinesis ou TCP sockets …).
Spark GraphX est dédié au traitement et à la
parallélisation de graphes. Ce module offre des
opérateurs et des algorithmes pour le
traitement des graphes. GraphX étend les RDD
de Spark via la Resilient Distributed Dataset
Graph (RDDG)
SparkR est un package R offrant une interface
légère pour utiliser Spark à partir de R. Dans
Spark 2.0.2, SparkR fournit une implémentation
de la Dataframe distribuée supportant des
opérations telles que la sélection, le filtrage,
l'agrégation. SparkR offre aussi des algorithmes
d’apprentissages distribués.
APACHE SPARK : VUE LOGIQUE ET APIS
M.MICHRAFY
10. VUE GLOBALE SUR LES API SPARK : DÉPENDANCE ET INTERACTION
10
Spark core
RDDRDDRDD
Spark SQL
DataframeDataframeDataframe
Spark ML
Model MLModel MLModel ML
Spark GraphX
Graph RDDGraph RDDGraph RDD
Spark Streaming
DStreamDStreamDStream
Streaming Source
File System Data Source
M.MICHRAFY
11. RDD, CARACTÉRISTIQUES, CRÉATION ET OPERATIONS (1)
11
1. Une RDD est une liste de partitions
2. Une RDD est associée à une liste de
dépendances avec les RDD parents
3. Une RDD dispose d’une fonction pour calculer
une partition
4. Optionnellement, un objet « partionner » pour
les RDD de type clé/valeur
5. Optionnellement, une liste indiquant
l’emplacement pour chaque partition
CaractéristiquesCaractéristiques
RDD supporte deux type d’opérations
1. Transformation
2. Action
Une transformation consiste à
appliquer une fonction sur 1 à n RDD
et à retourner une nouvelle RDD
Une action consiste à appliquer une
fonction et à retourner une valeur
OpérationsOpérations
1. Les transformations sont
paresseuses, évitant le calcul inutile.
Ceci favorise l’optimisation du
traitement.
2. Une RDD transformée est calculée
lorsqu’une action est appliquée sur
cette dernière.
Evaluation LazyEvaluation Lazy
Resilient Distributed Dataset (RDD)
Resilient : supporte la tolérance aux pannes grâce à une
modélisation du processus d’exécution par un DAG (directed
acyclic graph), et au recalcul des partitions manquantes.
Distributed : données distribuées sur les nœuds du cluster.
Dataset : collection de données partitionnée.
M.MICHRAFY
12. RDD, CARACTÉRISTIQUES, CRÉATION ET OPERATIONS (2)
12
Trois manières de créer une RDD
1. A partir d’une source de données
2. En parallélisant une collection via
SparkContext
3. En appliquant une opération de
type transformation sur la RDD
CréationCréation
Certaines fonctions sont disponibles
seulement sur certains types de RDD :
1. mean, variance, stdev pour les RDD
numériques
2. Join pour les RDD clé/valeur
3. L’enregistrement de fichier utilisant
des RDD basées sur des fichiers de
format séquentiel.
Pour plus de détails, voir les RDD de type
o PairRDDFunctions,
o DoubleRDDFunctions,
o SequenceFileRDDFunctions
Fonction/type RDDFonction/type RDD
In-Memory Immutable
Lazy
evaluated
Lazy
evaluated
Cacheable
Parallele
TypedPartitioned
Resilient
RDD
Spark supporte de charger ou
d’enregistrer des fichiers dans
divers formats : non structuré, semi-
structuré, structuré.
Les formats sont :
o Text
o Json
o CSV
o SequenceFile
o Protocole buffers
o Object files
Formats fichiersFormats fichiers
M.MICHRAFY
13. 13
Opérations de type transformation et action
• Cette section présente les différentes opérations
( Transformation, Action) relatives à un type RDD.
• Elle est organisée par fiche.
• Chaque fiche porte sur une opération et contient :
1. Un objectif
2. La signature de l’opération
3. Une section « À retenir »
4. Un exemple exécuté en mode interactif (*)
(*) : Les exemple du code ont été exécutés avec spark-shell, version 2.0.2
M.MICHRAFY
14. OPÉRATION DE TRANSFORMATION : MÉTHODE MAP
La méthode map retourne une RDD
en appliquant une fonction passée
en argument à chaque item de la
RDD source
Il s’agit d’un design pattern
incontournable de la programmation
fonctionnelle.
ObjectifObjectif
def map[U](f: (T) ⇒ U)(implicit arg0: ClassTag[U]):
RDD[U]
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : f est une fonction : (T) => U
Sortie : une RDD de type U
SignatureSignature
// créer un RDD
val x = sc.parallelize(List( 7, 10, 12, 17, 19), 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:24
// definir une fonction f
val f = (a:Int) => (a/2, a%2) // f: Int => (Int, Int) = <function1>
// définir une fonction g
val g = (a:Int) => (a*100.0)/20 // g: Int => Double = <function1>
// applique map avec f et g
val zf = x.map(f) // zf: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[3] at map at <console>:28
val zg = x.map(g) // zg: org.apache.spark.rdd.RDD[Double] = MapPartitionsRDD[4] at map at <console>:28
// afficher zf et zg
zf.collect() // res1: Array[(Int, Int)] = Array((3,1), (5,0), (6,0), (8,1), (9,1))
zg.collect() // res2: Array[Double] = Array(35.0, 50.0, 60.0, 85.0, 95.0)
ExempleExemple
La RDD source et la RDD retournée
ont le même nombre d’item
Si les items de la RDD source sont de
type T alors les items de la RDD
retournée sont de type f(T)
À retenirÀ retenir
14
M.MICHRAFY
15. OPÉRATION TERMINALE : MÉTHODE REDUCE
La méthode reduce agrège les
éléments de la RDD source en
appliquant une fonction
commutative et associative passée
en argument.
C’est une opération terminale
Il s’agit d’un design pattern
incontournable de la programmation
fonctionnelle.
ObjectifObjectif
def reduce(f: (T, T) ⇒ T): T
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : f est une fonction : (T,T) => T
Sortie : une valeur de retour de type T
SignatureSignature
// créer une RDD comportant les éléments de 1 à 10
val x = sc.parallelize(1 to 10, 3) //x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[5] at parallelize at <console>:24
// définir une fonction add
val add = (_:Int, _:Int) => x$1 + x$2 // add: (Int, Int) => Int = <function2>
// calculer la somme de 1 à 10 en appliquant reduce
val s = x.reduce(add) // s: Int = 55
ExempleExemple
Soit une loi * interne sur l’ensemble
E.
La loi * est associative SSI pour tout
x,y,z de E on a : x *(y*z) = (x*y)*z
La loi * est commutative SSI pour
tout x, y de E on a : x * y = y * x
À retenirÀ retenir
15
M.MICHRAFY
16. OPÉRATION DE TERMINALE : MÉTHODE COUNT
La méthode count retourne le
nombre d’éléments d’une RDD
source.
C’est une opération terminale
ObjectifObjectif
def count(): Long
API : scala, Classe : RDD,
Package : org.apache.spark
Sortie : le nombre d’élements de la RRD, de type
Long
SignatureSignature
// créer une RDD comportant les éléments de 1 à 10
val x = sc.parallelize(1 to 10, 3) //x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[6] at parallelize at <console>:24
val y = sc.parallelize(List((1,1), (1,2), (3,5), (3,7), (8,9), (8,13))) // y: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[7] at parallelize at <console>:24
// afficher le nombre d’éléments de chaque rdd
x.count() // res3: Long = 10
y.count() // res4: Long = 6
// appel de count sans parenthèses
x.Count // res5: Long = 10
ExempleExemple
Les parenthèses ne sont pas
obligatoires lors de l’appel de
count.
Ceci relève d’une règle en scala :
pour toute méthode sans
argument, les parenthèse sont
optionnelles.
À retenirÀ retenir
16
M.MICHRAFY
17. OPÉRATION DE TERMINALE : MÉTHODE COUNTBYKEY
La méthode countByKey compte le
nombre de valeur par clé et retoune
une map
Cette méthode nécessite une RDD
de type (K,V)
C’est une opération terminale
ObjectifObjectif
def countByKey(): Map[K, Long]
API : scala, Classe : PairRDDFunctions,
Package : org.apache.spark
Sortie : Map[K, Long] , la valeur représente le
nombre d’éléments par clé K.
SignatureSignature
// créer une x ( chaque entier et ses diviseurs)
val x = sc.parallelize(List((10,2),(10,5), (14,2), (14,7), (30,2), (30,3), (30,5)))
//Sortie : x: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[21] at parallelize at <console>:24
val r = x.countByKey()
// Sortie : r: scala.collection.Map[Int,Long] = Map(30 -> 3, 14 -> 2, 10 -> 2)
ExempleExemple
countByKey est similaire à count
countBuKey est une méthode de la
class PairRDDFunctions.
Attention, cette méthode n’est à
utiliser que si la map retounée est
de volume faible.
À retenirÀ retenir
17
M.MICHRAFY
18. OPÉRATION DE TERMINALE : MÉTHODE FIRST
La méthode first retourne le premier
élément de la RDD source
C’est une opération terminale
ObjectifObjectif
def first(): T
API : scala, Classe : RDD,
Package : org.apache.spark
Sortie : le premier élément de la RDD de type T
SignatureSignature
// créer une RDD comportant les éléments de 1 à 10
val x = sc.parallelize(1 to 10, 3) //x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:24
val y = sc.parallelize(List((1,1), (1,2), (3,5), (3,7), (8,9))) // y: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[9] at parallelize at <console>:24
val z = sc.parallelize(List())
// afficher le nombre d’éléments de chaque rdd
x.first() // res6: Int = 1
y.first() // res7: (Int, Int) = (1,1)
// Lors de cet appel, une exception se déclenche
z.first() // java.lang.ArrayStoreException: [Ljava.lang.Object;
ExempleExemple
Les parenthèses ne sont pas
obligatoires lors de l’appel de first.
Une exception se déclenche si
l’appel de first() se fait sur une RDD
source sans élément.
À retenirÀ retenir
18
M.MICHRAFY
19. OPÉRATION DE TERMINALE : MÉTHODE TAKE
La méthode take retourne un
tableau d’éléments, constitué des n
premiers éléments de la RDD source.
Le nombre d’éléments à retourner est
l’argument de la méthode take
C’est une opération terminale
ObjectifObjectif
def take(num: Int): Array[T]
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : num le nombre de éléments à retourner
Sortie : un tableau de type T
SignatureSignature
// créer une RDD comportant les éléments de 1 à 10
val x = sc.parallelize(1 to 10, 3) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[10] at parallelize at <console>:24
val z = sc.parallelize(List()) // z: org.apache.spark.rdd.RDD[Nothing] = ParallelCollectionRDD[11] at parallelize at <console>:24
// appliquer la méthode take sur la rdd x
x.take(-3) // res8: Array[Int] = Array()
x.take(0) // res9: Array[Int] = Array()
x.take(2) // res10: Array[Int] = Array(1, 2)
z.take(-5) // res11: Array[Nothing] = Array()
z.take(3) // déclenche une exception
ExempleExemple
Si le num est négatif, la valeur de
retour est un tableau vide (Array())
Si num est supérieur au nombre
d’éléments de la RDD, alors tous les
éléments de RDD sont retournés.
Si la RDD est vide (sans élément),
alors l’appel à take avec une
valeur strictement positif lève une
exception
À retenirÀ retenir
19
M.MICHRAFY
20. OPÉRATION DE TERMINALE : MÉTHODE FLATMAP
La méthode flatMap transforme
chaque Item de la RDD source en
0 ou plusieurs items et retourne
une nouvelle RDD.
Il s’agit d’un design pattern
incontournable de la
programmation fonctionnelle.
ObjectifObjectif
def flatMap[U](f: (T) ⇒ TraversableOnce[U])(implicit
arg0: ClassTag[U]): RDD[U]
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : f est une fonction : (T) ⇒ TraversableOnce[U]
Sortie : Une RDD[U]
SignatureSignature
// créer une RDD comportant les éléments de 1 à 10
val x = sc.parallelize(List((1,2),(3,5),(8,10))
// définir une fonction f
val f = (a:Tuple2[Int,Int]) => Seq(a._1, a._2)
// appliquer flatMap sur x
Val y = x.flatMap(f)
// afficher les éléments de y
y.collect()
//Sortie : res1: Array[Int] = Array(1, 2, 3, 5, 8, 10)
ExempleExemple
flatMap est similaire à Map
La fonction f doit retourner un trait
de type TraversableOnce qui
consiste à parcourir une collection
une ou plusieurs fois.
À retenirÀ retenir
val z = parallelize(List("La meilleure facon de predire l’avenir est de le creer", "Celui qui
veut reussir trouve un moyen", "Celui qui veut rien faire trouve une excuse"))
val g = (s:String) => s.split(" ").toList
// appliquer la fonction g sur les éléments de z
val t = z.flatMap(g)
// afficher les éléments de t
t.collect()
//Sortie : res2: Array[String] = Array(La, meilleure, faþon, de, predire, l'avenir, est, de,
le, creer, Celui, qui, veut, reussir, trouve, un, moyen, Celui, qui, veut, rien, faire, trouve,
une, excuse)
1 2
20
M.MICHRAFY
21. OPÉRATION DE TRANSFORMATION : MÉTHODE FILTER
La méthode filter retourne une RDD
ne contenant que les items de la RDD
source satisfaisant le prédicat f , passée
en argument.
Il s’agit aussi d’un design pattern
incontournable de la programmation
fonctionnelle.
ObjectifObjectif
def filter(f: (T) ⇒ Boolean): RDD[T]
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : f est une fonction : (T) => Boolean
Sortie : une RDD de type T
SignatureSignature
// créer un RDD
val x = sc.parallelize(List( 7, 10, 12, 17, 19), 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24
// definir une fonction f
val f = (a:Int) => (a%2==0) // f: Int => Boolean = <function1>
// définir une fonction g
Val g = (a:Int) => (a>50) // g: Int => Boolean = <function1>
// applique filter avec f et g
val zf = x.map(f) // zf: org.apache.spark.rdd.RDD[Boolean] = MapPartitionsRDD[1] at map at <console>:28
val zg = x.map(g) // zg: org.apache.spark.rdd.RDD[Boolean] = MapPartitionsRDD[2] at map at <console>:28
// afficher zf et zg
zf.collect() // res0: Array[Boolean] = Array(false, true, true, false, false)
Zg.collect() // res2: Array[Boolean] = Array(false, false, false, false, false)
ExempleExemple
La RDD source et la RDD obtenue
ont le même type d’item (T)
Le nombre d’éléments de la RDD
obtenue est toujours inférieur au
nombre d’éléments de la RDD
source
Il est possible d’obtenir une RDD
sans item.
À retenirÀ retenir
21
M.MICHRAFY
22. OPÉRATION DE TRANSFORMATION : MÉTHODE SAMPLE
La méthode sample retourne un
échantillon des données – qui est une
sélection aléatoire- de la RDD
source.
ObjectifObjectif
def sample(withRpl: Boolean, fraction: Double,
seed: Long = Utils.random.nextLong): RDD[T]
API : scala, Classe : RDD, Package : org.apache.spark
Entrée : withRpl : avec ou sans répétition
fraction : le % des données, dans [0 1]
seed : pour initialiser le générateur aléatoire
Sortie : une RDD de type T
SignatureSignature
// créer un RDD de 100 entiers
val x = sc.parallelize(1 to 100, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at parallelize at <console>:24
// Faire une selection aléatoire d’un % de 0,1
val smpl1 = x.sample(true, 0.1, 7) // smpl1: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[4] at sample at <console>:26
smpl1.collect() // res3: Array[Int] = Array(3, 6, 14, 47, 48, 72, 78, 84, 87, 92, 100)
// Faire une selection aléatoire d’un % de 0,3
val smpl2 = x.sample(false, 0.3, 17) // smpl2: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[6] at sample at <console>:26
smpl2.collect() // res5: Array[Int] = Array(3, 4, 8, 11, 12, 14, 16, 18, 19, 21, 23, 25, 30, 33, 36, 37, 38, 42, 43, 46, 47, 49, 55,
// 58, 61, 64, 69, 70, 73, 74, 76, 80, 86, 89, 96)
ExempleExemple
Si withRpl = true, on utilise un
générateur basé sur la loi de
Poisson
Si withRpl = false, on utilise un
générateur basé sur la loi de
Bernoulli.
À retenirÀ retenir
22
M.MICHRAFY
23. OPÉRATION DE TRANSFORMATION: MÉTHODE TAKESAMPLE
La méthode takeSample retourne un
tableau – qui est une sélection
aléatoire- de la RDD source.
Le nombre d’éléments du tableau est
un argument de la méthode
takeSample
ObjectifObjectif
def takeSample(withReplacement: Boolean, num:
Int, seed: Long = Utils.random.nextLong): Array[T]
API : scala, Classe : RDD, Package : org.apache.spark
Entrée : withRpl : avec ou sans répitition
num : le nombre d’éléments à retourner
seed : pour initialiser le générateur aléatoire
Sortie : un tableau de type T (Array[T])
SignatureSignature
// créer un RDD de 100 entiers
val x = sc.parallelize(1 to 100, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[7] at parallelize at <console>:24
val smpl1take = x.takeSample(true, 10, 7) // smpl1take: Array[Int] = Array(19, 29, 64, 52, 74, 87, 93, 46, 27, 31)
val smpl2take = x.takeSample(false, 10, 7) // smpl2take: Array[Int] = Array(41, 80, 72, 24, 90, 100, 56, 87, 50, 78)
ExempleExemple
Cette méthode retourne
exactement un tableau
composé de num éléments de la
RDD source.
Elle est différente de la méthode
sample puisque sample prend un
argument fraction indiquant le %
d’éléments à retourner et
retourne une RDD.
À retenirÀ retenir
23
M.MICHRAFY
24. OPÉRATION TERMINALE: MÉTHODE TAKEORDERED
La méthode takeOrdered ordonne
les éléments de données du RDD en
utilisant l’ordre implicite (croissant) et
renvoie les n premiers éléments sous
forme d’un tableau.
ObjectifObjectif
def takeOrdered(num: Int)(implicit ord:
Ordering[T]): Array[T]
API : scala, Classe : RDD, Package : org.apache.spark
Entrée :
num: le nombre d’éléments à retourner
Sortie : un tableau de type T (array[T])
SignatureSignature
sc.parallelize(Seq(6, 8, 9, 1, 2, 3)).takeOrdered(3) // res1: Array[Int] = Array(1, 2, 3)
sc.parallelize(Seq(0, 6, 8, 9, 1, 2, 3)).takeOrdered(3) // res2: Array[Int] = Array(0, 1, 2)
sc.parallelize(Seq((1,2),(1,4),(6,7), (9,11), (8,1))).takeOrdered(3) // res3: Array[(Int, Int)] = Array((1,2), (1,4), (6,7))
sc.parallelize(Seq(("aa",2),("bc",4),("ef",7), ("aa",11), ("ef",1))).takeOrdered(3) // res4: Array[(String, Int)] = Array((aa,2), (aa,11), (bc,4))
ExempleExemple
Cette méthode est à ne pas
utiliser lorsque le tableau retourné
est volumineux car les données
sont chargées sur la mémoire du
driver.
Les éléments de la RDD source
doivent disposer d’une relation
d’ordre
À retenirÀ retenir
24
M.MICHRAFY
25. OPÉRATION TERMINALE : MÉTHODE FOLD
Agréger les données en deux étapes. Il
s’agit d’une opération de type Action.
L’étape 1 consiste à appliquer op par
partition
L’étape 2 consiste appliquer op sur les
résultats de la 1 étape
op doit être un opérateur associatif
ObjectifsObjectifs
def fold(zeroValue: T)(op: (T, T) ⇒ T): T
API : scala, Classe : rdd, Package : org.apache.spark
Entrée :
- zeroValue : de type T, c est la valeur initiale de calcul
- op est une fonction associative de signature : (T,T) => T
Sortie : une valeur de type T
SignatureSignature
val x = sc.parallelize( 1 to 6, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24
// calculer la somme de éléments de x via les partitions
val op = (a:Int,b:Int) => a+b // op: (Int, Int) => Int = <function2>
// zeroValue : 0
x.fold(0)((op) // res10: Int = 21
ExempleExemple
Fold et agrgregate sont
similaires en terme de
processus
fold applique le même
fonction en 1 et 2 étape
Aggregate associe une
fonction à chaque étape
À retenirÀ retenir
25
M.MICHRAFY
26. OPÉRATION DE TRANSFORMATION : MÉTHODE MAPPARTITIONSWITHINDEX
Calculer une nouvelle RDD en
appliquant une fonction f sur chaque
partition
il nécessite au moins un paramètre
de type fonction qui prend en entrée
les éléments d’une partition et son
index
ObjectifsObjectifs
def mapPartitionsWithIndex[U](f: (Int, Iterator[T]) ⇒ Iterator[U],
ind: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U]
API : scala, Classe : rdd, Package : org.apache.spark
Entrée :
- f est une fonction (Int, Iterator[T]) ⇒ Iterator[U]
- ind est boolean, dont la valeur par défaut est false,
Sortie : RDD[U]
SignatureSignature
// définir un RDD ( 1 …10) avec 3 partitions
val x = sc.parallelize(1 to 10, 3) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[12] at parallelize at <console>:24
// affiche une liste de tuple, chaque tuple est composé de l'index de la partition et la valeur de l'élément.
def f[T](i:Int, p:Iterator[T]) : Iterator[(Int,T)] = p.map(w => (i,w)) // f: [T](i: Int, p: Iterator[T])Iterator[(Int, T)]
val y = x.mapPartitionsWithIndex(f) // y: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[13] at mapPartitionsWithIndex at
<console>:28
y.collect() // res8: Array[(Int, Int)] = Array((0,1), (0,2), (0,3), (1,4), (1,5), (1,6), (2,7), (2,8), (2,9), (2,10))
ExempleExemple
Il est similaire à map
map applique une fonction sur
les éléments de la RDD alors
que mapPartitionsWithIndex
applique une fonction f à
chaque partition
À retenirÀ retenir
26
M.MICHRAFY
27. OPÉRATION TERMINALE : MÉTHODE AGGREGATE
Agréger les données en deux étapes. Il
s’agit d’une opération de type Action
L’étape 1 consiste à appliquer seqOp par
partition
L’étape 2 consiste à appliquer combOp
aux résultats de l’étape 1
ObjectifsObjectifs
def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U,
combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): U
API : scala, Classe : rdd, Package : org.apache.spark
Entrée
- zeroValue : la valeur initiale de l’accumulateur de type U
- seqOp : c’est une fonction associative qui sera appliquée sur chaque partition
- comOp : c’est une fonction associative qui s’applique sur les résulats données par seqOp.
Sortie : une valeur de retour de type U
SignatureSignature
val x = sc.parallelize( 1 to 6, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24
// calculer la somme de éléments de x via les partitions
val seqOp = (a:Int,b:Int) => if(a>b) a else b // seqOp: (Int, Int) => Int = <function2>
val compOp = (a:Int,b:Int) => a + b // compOp: (Int, Int) => Int = <function2>
// affiche une liste de tuple, chaque tuple est composé de l'index de la partition et la valeur de l'élément.
def f[T](i:Int, p:Iterator[T]) : Iterator[(Int,T)] = p.map(w => (i,w)) // f: [T](i: Int, p: Iterator[T])Iterator[(Int, T)]
// afficher chaque élément et sa partition
x.mapPartitionsWithIndex(f).collect() // res4: Array[(Int, Int)] = Array((0,1), (0,2), (0,3), (1,4), (1,5), (1,6), (2,7), (2,8), (2,9), (2,10))
// appliquer la méthode aggregate sur la RDD X
x.aggregate(0)( seqOp, compOp) // res10: Int = 19
ExempleExemple
27
M.MICHRAFY
28. OPÉRATION DE TRANSFORMATION : MÉTHODE MAPVALUES
La méthode mapValues, s’applique à
une RDD de type (K,V) et retourne une
RDD de type (K,U).
Cette méthode prend en argument
une fonction f: (V) ⇒ U, et transforme
chaque pair (K,V) en (K, f(U)).
C’est une opération de transformation
ObjectifObjectif
def mapValues[U](f: (V) ⇒ U): RDD[(K, U)]
API : scala, Classe : PairRDDFunctions,
Package : org.apache.spark
Sortie : RDD[(K, U)]
SignatureSignature
// créer une RDD constitué d’une liste de fruits
val x = sc.parallelize(List("Abricot", "Cerise", "Nectarine", "Noisette", "Kiwi"))
x: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[31] at parallelize
at <console>:24
// créer une RDD de type (K,V), avec V = la taille de K
val y = x.map(w => (w,w.length))
y// : org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[30] at map
at <console>:26
// /afficher la RDD y
y.collect() // res1: Array[(String, Int)] = Array((Abricot,7), (Cerise,6),
(Nectarine,9), (Raisin,6), (Kiwi,4))
// créer une fonction f
val f = (n:Int) => (n/2, n%2) // f: Int => (Int, Int) = <function1>
ExempleExemple
La RDD source et la RDD obtenue
ont :
• La même taille.
• Les mêmes clés
• Les mêmes partitions
À retenirÀ retenir
28
// appliquer mapValues sur y avec la fonction f
val z = y.mapValues(f)
//Sortie : z: org.apache.spark.rdd.RDD[(String, (Int, Int))] =
MapPartitionsRDD[33] at mapValues at // <console>:30
// afficher le résultat
z.collect()
res2: Array[(String, (Int, Int))] = Array((Abricot,(3,1)), (Cerise,(3,0)),
(Nectarine,(4,1)), (Noisette,(4,0)), (Kiwi,(2,0)))
y.Count == z.count
res49: Boolean = true
1 2
M.MICHRAFY
29. OPÉRATION TERMINALE : MÉTHODE COLLECT
La méthode collect retourne un tableau (
Array) qui représente les éléments de la
RDD.
Il est utile lors de la mise au point d’un
code.
ObjectifsObjectifs
def collect(): Array[T]
API : scala, Classe : rdd, Package : org.apache.spark
Sortie : Array[T]
SignatureSignature
// définir un RDD ( 1 …10) avec 2 partitions
val x = sc.parallelize(1 to 10, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[14] at parallelize at <console>:24
// appliquer la méthode collect pour obtenir un tableau
val x_arr = x.collect() // x_arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Appel sans parenthèse
x.Collect // res0: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
ExempleExemple
Cette méthode est couteuse
en terme de CPU. Par
conséquent, elle est à utiliser
seulement si la RDD a un faible
volume.
À retenirÀ retenir
29
M.MICHRAFY
30. OPÉRATION TERMINALE : MÉTHODE FOREACH
La méthode foreach applique une
fonction f (T) => Unit, sur chaque élément
de la RDD
Il est utile lors de la mise au point d’un
code.
ObjectifsObjectifs
def foreach(f: (T) ⇒ Unit): Unit
API : scala, Classe : rdd, Package : org.apache.spark
Entrée :
f une fonction ( (T) => Unit)
Sortie : pas de valeur de retour (Unit)
SignatureSignature
// définir des RDD
val x = sc.parallelize(1 to 5, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[15] at parallelize at <console>:24
val y = sc.parallelize(List("Banane", "Kiwi", "Cerise", "Orange", "Fraise"),2)
val z = y.zip(x) // z: org.apache.spark.rdd.RDD[(String, Int)] = ZippedPartitionsRDD2[17] at zip at <console>:28
// afficher les éléments de la RDD x
x.foreach(w => print(w + ", ")) // 1, 3, 4, 5, 2,
// afficher les éléments de la RDD y
y.foreach(w => print(w + ", ")) // Cerise, Orange, Banane, Fraise, Kiwi,
// afficher les éléments de la RDD z
z.foreach(w => print(w + ", ")) // (Banane,1), (Cerise,3), (Orange,4), (Kiwi,2), (Fraise,5),
Exemple
Attention, foreach n’affiche
pas forcement la même chose
que la méthode collect
À retenirÀ retenir
30
M.MICHRAFY
31. OPÉRATION DE TRANSFORMATION : MÉTHODE COLLECT AVEC DES ARGUMENTS
Elle retourne une RDD comportant les
éléments correspondant à
l'application d’une fonction partielle
Cette méthode est couteuse en
terme de CPU. Par conséquent, elle
est à utiliser seulement si la RDD a un
faible volume.
ObjectifsObjectifs
collect[U](f: PartialFunction[T, U])(implicit arg0:
ClassTag[U]): RDD[U]
API : scala, Classe : rdd, Package : org.apache.spark
Sortie : Array[T]
SignatureSignature
// créer une rdd avec des éléments de 1 à 10
val sample = sc.parallelize(1 to 10) // sample: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[18] at parallelize at <console>:24
// Déclarer une fonction partielle
val isEven: PartialFunction[Int,Int] = { case x if x % 2 == 0 => x } // isEven: PartialFunction[Int,Int] = <function1>
// appel de la méthode collect avec l’argument isEven (la fonction partielle)
val sample_fp = sample.collect(isEven) // sample_fp: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[22] at collect at <console>:30
// afficher les élements de la rdd sample_fp
sample_fp.collect() // res11: Array[Int] = Array(2, 4, 6, 8, 10)
ExempleExemple
Elle est similaire à la méthode
collect sans argument.
collect() retourne un tableau
alors que collect(PartialFunction)
retourne une RDD [U] après avoir
appliqué la fonction partielle sur
les items de la RDD.
À retenirÀ retenir
31
M.MICHRAFY
32. OPÉRATION DE TRANSFORMATION : MÉTHODE DISTINCT
La méthode distinct retourne une
RDD qui comporte les éléments
distincts de la RDD source
ObjectifsObjectifs
def distinct(): RDD[T]
API : scala, Classe : rdd, Package : org.apache.spark
Sortie : RDD[T]
SignatureSignature
// créer un rdd avec les éléments : 1,1,2,3,4,5,5
val z = sc.parallelize(List(1,1,2,3,4,5,5)) // z: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[23] at parallelize at <console>:24
// appliquer la méthode distinct sur la rdd z
val zsd = z.distinct() // zsd: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[26] at distinct at <console>:26
// regrouper le resultat dans un tableau
zsd.collect() // res12: Array[Int] = Array(4, 1, 5, 2, 3)
val l = List(List(1,2),List(3,4), List(4,3), List(1,2), List(1,2,3,4)) // l: List[List[Int]] = List(List(1, 2), List(3, 4), List(4, 3), List(1, 2), List(1, 2, 3, 4))
val zobj = sc.parallelize(l) // zobj: org.apache.spark.rdd.RDD[List[Int]] = ParallelCollectionRDD[27] at parallelize at <console>:26
// observer le resultat
val zobjsd = zobj.distinct() // zobjsd: org.apache.spark.rdd.RDD[List[Int]] = MapPartitionsRDD[30] at distinct at <console>:28
zobjsd.collect() // res14: Array[List[Int]] = Array(List(3, 4), List(1, 2, 3, 4), List(4, 3), List(1, 2))
ExempleExemple
Il est possible de mettre en
place la méthode distinct en
utilisant :
• map
• reduceByKey
À retenirÀ retenir
32
M.MICHRAFY
33. OPÉRATION DE TRANSFORMATION : MÉTHODE UNION
La méthode union retourne une RDD
qui est l’union des items de la RDD
source et de la RDD passée en
argument.
il est possible d’avoir des éléments
identiques dans la RDD résultat. Pour
éviter cela, utiliser la méthode distinct
ObjectifsObjectifs
def union(other: RDD[T]): RDD[T]
API : scala, Classe : rdd, Package : org.apache.spark
Entrée : other est de type RDD[T].
Sortie : RDD[T]
SignatureSignature
// créer les rdd avec les éléments : 1 to 10, et 5 to 15
val x = sc.parallelize(1 to 10) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[31] at parallelize at <console>:24
val y = sc.parallelize(5 to 15) // y: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[32] at parallelize at <console>:24
// appliquer la méthode union
val uad = x.union(y) // uad: org.apache.spark.rdd.RDD[Int] = UnionRDD[33] at union at <console>:28
// appliquer la méthode union et supprimer les éléments identiques
val usd = x.union(y).distinct() // usd: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[37] at distinct at <console>:28
// afficher les résultats
uad.collect() // res15: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
usd.collect() // res16: Array[Int] = Array(8, 1, 9, 10, 2, 11, 3, 4, 12, 13, 5, 14, 6, 15, 7)
ExempleExemple
Les éléments de la RDD source
et la rdd other doivent être de
même type (ici T)
Plus précisément, les éléments
de la RDD other doivent être de
même type que ceux de la rdd
source.
À retenirÀ retenir
33
M.MICHRAFY
34. OPÉRATION DE TRANSFORMATION : MÉTHODE INTERSECTION
La méthode intersection retourne une
RDD qui est l’intersection des items du
RDD source et de la RDD passée en
argument.
Attention, le résultat de retour ne
comporte pas de doublant même si
la RDD source ou argument en
comporte.
ObjectifsObjectifs
def intersection(other: RDD[T]): RDD[T]
API : scala, Classe : rdd, Package : org.apache.spark
Entrée : other est de type RDD[T].
Sortie : RDD[T]
SignatureSignature
// créer deux rdds
val x = sc.parallelize(List(1 , 1, 2, 3, 4, 7)) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[38] at parallelize at <console>:24
val y = = sc.parallelize(List(1, 1, 4, 4, 5, 6)) // y: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[39] at parallelize at <console>:24
// appliquer la méthode intersection
val z = x. intersection(y) // z: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[45] at intersection at <console>:28
// afficher les résultats
z.collect() // res17: Array[Int] = Array(4, 1)
ExempleExemple
Les éléments de la RDD other
doivent être de même type que
ceux de la rdd source.
intersection peut être codée
avec :
• map,
• cogroup
• filter
À retenirÀ retenir
34
M.MICHRAFY
35. OPÉRATION DE TRANSFORMATION : MÉTHODE SUBTRACT
La méthode subtract est
l’implémentation de l’opération
soustraction au sens ensemble.
Elle retourne les éléments de la RDD
source mais qui ne sont pas dans la
RDD passée en argument.
ObjectifsObjectifs
def subtract(other: RDD[T]): RDD[T]
API : scala, Classe : rdd, Package : org.apache.spark
Entrée : other est de type RDD[T]
Sortie : RDD[T]
SignatureSignature
// créer les 2 rdds
val x = sc.parallelize(1 to 5, 2)
val y = sc.parallelize(3 to 7, 2)
x.collect() // res18: Array[Int] = Array(1, 2, 3, 4, 5)
y.collect() // res19: Array[Int] = Array(3, 4, 5, 6, 7)
// appliquer la méthode subtract et generer la RDD s
val s = x.subtract(y)
// afficher les éléments
s.collect() // res20: Array[Int] = Array(2, 1)
ExempleExemple
La RDD other doit être de même
type que la RDD source. Dans le
cas contraire, on obtient une
erreur.
Il existe des variante de subtract
portant sur le nombre des
partitions.
À retenirÀ retenir
// créer une RDD
val z = sc.parallelize(List("A","B","D","E"),2)
// appliquer la méthode subtract sur x et z
// cette instruction génère une erreur
val t = x.subtract(z)
error: type mismatch;
found : org.apache.spark.rdd.RDD[String]
required: org.apache.spark.rdd.RDD[Int]
val t = x.subtract(z)
1 2
35
M.MICHRAFY
36. OPÉRATION DE TRANSFORMATION : MÉTHODE ZIP
La méthode zip consiste à construire
une RDD de type (K,V) à partir de la
RDD[K] source et la RDD[V] passée en
argument.
L’élément i de la RDD résultante est
(ki,vi) avec ki, vi les éléments d’indice i
resp. des RDD source et argument.
ObjectifsObjectifs
def zip[U](other: RDD[U])(implicit arg0: ClassTag[U]): RDD[(T, U)]
API : scala, Classe : rdd, Package : org.apache.spark
Entrée : other est de type RDD[U].
Sortie : un RDD de type (T,U)
SignatureSignature
// créer les deux rdds
val key = sc.parallelize(1 to 5, 2)
val value = sc.parallelize(11 to 15, 2 )
// appliquer la méthode zip
val x = key.zip(value)
// afficher les éléments
x.collect()
// res21: Array[(Int, Int)] = Array((1,11), (2,12), (3,13), (4,14), (5,15))
Exemple 1Exemple 1
Pour réaliser une opération zip,
les deux RDD doivent avoir :
• Le même nombre de
partitions
• Le même nombre
d'éléments dans chaque
Partition
À retenirÀ retenir
// créer deux rdds
val value_nes = sc.parallelize(11 to 16, 2 )
val value_nps = sc.parallelize(11 to 15, 3 )
// appliquer la méthode zip
val x_nes = key.zip(value_nes)
val x_nps = key.zip(value_nep)
// Des exceptions se lèvent lors de l’appel de collect
X_nex.collect() // error : Can only zip RDDs with same number of elements in each partition
x_nep.collect() // error : Can't zip RDDs with unequal numbers of partitions
Exemple 2Exemple 2
36
M.MICHRAFY
37. OPÉRATION DE TRANSFORMATION : MÉTHODE GROUPBY
La méthode groupBy regroupe les
éléments de la RDD[T] source par clé
et retourne une RDD [(k, Iterable[T])]
Elle prend en entrée une fonction f :
(T) => K, qui s’applique à chaque
élément de la RDD source pour
générer les clés de la RDD retournée.
ObjectifObjectif
def groupBy[K](f: (T) ⇒ K)(implicit kt:
ClassTag[K]): RDD[(K, Iterable[T])]
API : scala, Classe : RDD
Package : org.apache.spark
Sortie : RDD[(K, Iterable[T])]
SignatureSignature
// créer la rdd de type (Int) avec 10 partitions
val x = sc.parallelize(1 to 20, 10)
// faire une selection aléatoire dans les éléments de x
val y = x.sample(false, 0.1, 19)
// definir la function f
val f = (w:Int) => w%3 // f: Int => Int = <function1>
// définir une fonction g
val g = (w:Int) => 2*w + 7 // g: Int => Int = <function1>
ExempleExemple
Cette méthode est très couteuse.
Il existe deux variantes de cette méthode,
prenant en plus soit le nombre de partitions, soit
un partitionner
Attention la RDD est de type (K, Iterable[T])],
sachant que f : (T) => K
Elle est similaire avec la méthode groupByKey
À retenirÀ retenir
// appliquer la groupBy , avec f, sur les éléments de y
val z = y.groupBy(f)
// appliquer groupBy, avec g, sur les éléments de y
val s = y.groupBy(g)
// afficher z et s
z.collect() // res25: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(6, 15)))
s.collect() // res26: Array[(Int, Iterable[Int])] = Array((37,CompactBuffer(15)), (19,CompactBuffer(6)))
1 2
37
M.MICHRAFY
38. OPÉRATION DE TRANSFORMATION : MÉTHODE GROUPBYKEY
Pour une RDD de type (K, V),
groupByKey retourne une RDD de
type (K,Iterable<V>)
Si le regroupement vise une
agrégation, pour des raisons de
performance, il est conseillé d’utiliser
reduceByKey ou aggregateByKey.
ObjectifObjectif
def groupByKey(): RDD[(K, Iterable[V])]
API : scala,
Classe : PairRDDFunctions,
Package : org.apache.spark
Sortie : RDD[(K, Iterable[V])]
SignatureSignature
// créer la rdd de type (k,v) avec deux partitions
val x = sc.parallelize(List((1,1),(11,2),(11,6),(2,8),(3,5),(3,7)),2) // x: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[67] at parallelize at <console>:24
// appliquer la méthode groupeByKey pour regrouper par clé
val z = x.groupByKey() // z: org.apache.spark.rdd.RDD[(Int, Iterable[Int])] = ShuffledRDD[68] at groupByKey at <console>:26
// afficher les données de la RDD z
z.collect() // res27: Array[(Int, Iterable[Int])] = Array((2,CompactBuffer(8)), (11,CompactBuffer(2, 6)), (1,CompactBuffer(1)), (3,CompactBuffer(5, 7)))
ExempleExemple
groupByKey est une méthode de la classe
PairRDDFunctions. Elle s’applique à des RDD de type
(k,v).
groupByKey utilise un partitionner par défaut
Il existe différente variante de cette méthode :
- def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]
- def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]
À retenirÀ retenir
38
M.MICHRAFY
39. OPÉRATION DE TRANSFORMATION : MÉTHODE REDUCEBYKEY
Pour une RDD de type (k,v),
reduceByKey retourne une RDD de
type (k,v) où les valeurs de chaque
clé sont agrégées en utilisant la
fonction f de type (v,v) => v.
ObjectifObjectif
def reduceByKey(func: (V, V) ⇒ V): RDD[(K, V)]
API : scala, Classe : PairRDDFunctions, Package :
org.apache.spark
Entrée : une fonction associative, commutative de type (v,v) => v
Sortie RDD[(K, V)]
SignatureSignature
// créer la rdd de type (k,v) avec deux partitions
val t = Seq((1,1),(1,8), (2,7), (2,9), (3,13), (3, 10))
val x = sc.parallelize(t, 2) // // x: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[4] at parallelize at <console>:24
// créer la fonction func
val func = (x:Int, y:Int) => x + y // func: (Int, Int) => Int = <function2>
// appliquer la méthode reduceByKey pour regrouper par clé et appliquer func
val y = x.reduceByKey(func) // y: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[5] at reduceByKey at <console>:32
x.collect() // // res2: Array[(Int, Int)] = Array((1,1), (1,8), (2,7), (2,9), (3,13), (3,10))
// afficher les résultats
y.collect() // res3: Array[(Int, Int)] = Array((2,16), (1,9), (3,23))
ExempleExemple
reduceByKey estune méthode
de la classe PairRDDFunctions.
il existe deux variantes de cette
méthode nécessitant en plus soit
le nombre de partitions soit un
partitionner.
À retenirÀ retenir
39
M.MICHRAFY
40. OPÉRATION DE TRANSFORMATION : MÉTHODE AGGREGATEBYKEY
Pour une RDD[(k,v)], aggregateByKey
retourne une RDD[(k,u)] où les valeurs
de chaque clé sont agrégées en
appliquant les fonctions seqOp et
combOp et la valeur neutre
zeroValue.
Elle est similaire à aggregate sauf
que l'agrégation est appliquée aux
valeurs ayant la même clé
ObjectifObjectif
def aggregateByKey[U](zero: U)(seqOp: (U, V) ⇒ U,
combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]):
RDD[(K, U)]
API : scala, Classe : PairRDDFunctions,
Package : org.apache.spark
Entrée : zero valeur neutre de type U
seqOp : (U,V) => U et CombOp : (U,U) => U
Sortie : RDD[(K, V)]
SignatureSignature
// créer la rdd de type (k,v) avec deux partitions
val x = sc.parallelize(List(("Bannane", 4),("Fraise", 1),("Bannane", 3),("Kiwi", 2),("Fraise", 2),("Orange", 5),("Orange",6),("Raisin",4)),2)
// Pour chaque élément de la RDD x, afficher sa partition et sa valeur
x.mapPartitionsWithIndex((i,p) => p.map(w => (i,w))).collect()
// res28: Array[(Int, (String, Int))] = Array((0,(Bannane,4)), (0,(Fraise,1)), (0,(Bannane,3)), (0,(Kiwi,2)), (1,(Fraise,2)), (1,(Orange,5)), (1,(Orange,6)), (1,(Raisin,4)))
// créer deux fonctions seqOp et combOp
val seqOp = (a:Int, b:Int) => Math.max(a,b) // seqOp: (Int, Int) => Int = <function2>
val combOp = (a:Int, b:Int) => a + b // combOp: (Int, Int) => Int = <function2>
// appliquer la méthode aggregateByKey
val y = x.aggregateByKey(0)(seqOp, combOp) // y: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[76] at aggregateByKey at <console>:30
// afficher les résultats
y.collect() // res29: Array[(String, Int)] = Array((Raisin,4), (Kiwi,2), (Fraise,3), (Orange,6), (Bannane,4))
ExempleExemple
aggregateByKey est une
méthode de la classe
PairRDDFunctions.
il existe deux variantes de cette
méthode nécessitant en plus
soit le nombre de partitions soit
un partitionner.
À retenirÀ retenir
40
M.MICHRAFY
41. OPÉRATION DE TRANSFORMATION : MÉTHODE SORTBYKEY
Pour une RDD[(k,v)], sortByKey
retourne une RDD[(k,v)] triée selon un
ordre prédéfini
La clé doit implémenter le trait
Ordered
ObjectifObjectif
def sortByKey(ascending: Boolean = true,
numPartitions: Int = self.partitions.length): RDD[(K, V)]
API : scala, Classe : OrderedRDDFunctions,
Package : org.apache.spark
Entrée : ascending boolean indiquant le type de trie
numPartitions, optionnel, indique le nombre
de partitions
Sortie : RDD[(K, V)]
SignatureSignature
// créer une rdd de type (k,v)
val x = sc.parallelize(List(("B", 4),("K", 2),("F", 2),("O", 5),("R",4)),3)
// appliquer la méthode sortByKey
val yas = x.sortByKey(true)
val yde = x.sortByKey(false)
// afficher les résultats
yas.collect() // res30: Array[(String, Int)] = Array((B,4), (F,2), (K,2), (O,5), (R,4))
yde.collect() // res32: Array[(String, Int)] = Array((R,4), (O,5), (K,2), (F,2), (B,4))
Exemple 1Exemple 1
sortByKey est une méthode de
la classe OrderedRDDFunctions.
Par conséquent, la RDD source
doit avoir des élements de type
(K,V)
À retenirÀ retenir
case class A(tag:String, load:Int) extends Ordered[A] {
def compare( a:A ) = tag.compareTo(a.tag)
}
// créer une rdd de type (k,v)
val xls = List( A("w",50), A("v",2), A("l",7), A("s",6))
val xlsrdd = sc.parallelize(xls,2)
val v = sc.parallelize(1 to 4,2)
val xx = xlsrdd.zip(v)
// appliquer la méthode sortByKey
val yy = xx.sortByKey(true)
// afficher les résultats
yy.collect() // Array[(A, Int)] = Array((A(l,7),3), (A(s,6),4), (A(v,2),2), (A(w,50),1))
Exemple 2Exemple 2
41
M.MICHRAFY
42. OPÉRATION DE TRANSFORMATION : MÉTHODE JOIN
La méthode join nécessite des RDD
de type (K,V)
Elle permet de faire une jointure sur la
clé entre la RDD source et la RDD
passée en argument.
La valeur de retour est une RDD de
type (K, (V, W))
ObjectifObjectif
def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
API : scala, Classe : PairRDDFunctions,
Package : org.apache.spark
Entrée : other est une RDD de type (K,W)
Sortie : RDD[(K, (V, W))]
SignatureSignature
// créer des rdd de type (k,v)
val l1 = List(("A",1),("B",2),("C",3),("D",5),("E",1))
Val l2 = List(("A",2),("E",2),("C",5),("D",5),("G",1))
val x = sc.parallelize(l1,2) //x: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[7] at parallelize at <console>:26
val y = sc.parallelize(l2, 2) // y: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[8] at parallelize at <console>:26
// appliquer la méthode join // z: org.apache.spark.rdd.RDD[(String, (Int, Int))] = MapPartitionsRDD[11] at join at <console>:32
val z = x.join(y)
// afficher les résultats
z.collect() // res1: Array[(String, (Int, Int))] = Array((D,(5,5)), (A,(1,2)), (C,(3,5)), (E,(1,2)))
il existe deux variantes de cette
méthode nécessitant en plus
soit le nombre de partitions soit
un partitionner.
il existe aussi une jointure à
droite ou à gauche
( voir leftOuterJoin, rightOuterJoin)
À retenirÀ retenir
ExempleExemple
42
M.MICHRAFY
43. OPÉRATION DE TRANSFORMATION : MÉTHODE CARTESIAN
La méthode cartesian retourne une
RDD[(K,V)] qui est le produit cartésien
de la RDD source et la RDD
argument.
La RDD retournée est de taille nxm
avec n, m les nombres d’éléments de
la RDD source et la RDD passée en
argument.
ObjectifObjectif
def cartesian[U](other: RDD[U])(implicit arg0:
ClassTag[U]): RDD[(T, U)]
API : scala, Classe : RDD,
Package : org.apache.spark
Entrée : other est une RDD de type U
Sortie : une RDD de type (T,U)
SignatureSignature
// créer deux RDD
val x = sc.parallelize( 1 to 5, 2) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[12] at parallelize at <console>:24
val y = sc.parallelize(List(("A",2),("E",2),("C",5)), 2) // y: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[13] at parallelize at <console>:24
// calculer le produit cartésien de x et y
val z = x. cartesian(y) // z: org.apache.spark.rdd.RDD[(Int, (String, Int))] = CartesianRDD[14] at cartesian at <console>:28
// évaluer la taille de z // res3: Boolean = true
z.count() == x.count () * y.count()
// afficher les éléments de z // res4: Array[(Int, (String, Int))] = Array((1,(A,2)), (2,(A,2)), (1,(E,2)), (1,(C,5)), (2,(E,2)), (2,(C,5)), (3,(A,2)),
z.collect() // (4,(A,2)), (5,(A,2)), (3,(E,2)), (3,(C,5)), (4,(E,2)), (4,(C,5)), (5,(E,2)), (5,(C,5)))
ExempleExemple
La RDD retournée est une
instance de la classe
PairRDDFunctions,
Pour un élément (k,v) de la RDD
retournée, k est un élément de la
RDD source et v est un élément
de la RDD passée en argument
À retenirÀ retenir
43
M.MICHRAFY
44. OPÉRATION DE TRANSFORMATION : MÉTHODE COGROUP
Pour une RDD source (K,V) et une
RDD de type (K,W), la méthode
cogroup retourne une RDD de
type (K, (Iterable<V>,
Iterable<W>)).
ObjectifObjectif
def cogroup[W](other: RDD[(K, W)]): RDD[(K,
(Iterable[V], Iterable[W]))]
API : scala, Classe : PairRDDFunctions,
Package : org.apache.spark
Entrée : other est une RDD de type (K,W)
Sortie : une RDD de type [(K, (Iterable[V],
Iterable[W]))
SignatureSignature
// créer deux RDD de type clé-valeur
val x = sc.parallelize(List(("A",1), ("A",2),("C",5), ("D",10),("D",1)), 2)
val y = sc.parallelize(List(("A",3), ("B",3), ("B",6), ,("C",7)), 2)
// regrouper par clé
val z = x. cogroup(y) // RDD[(String, (Iterable[Int], Iterable[Int]))] = MapPartitionsRDD[18] at cogroup at <console>:28
// comparer le nombre des clés distinct avec celui du nombre d’éléments de z
x.map(w => w._1).union(y.map(w => w._1)).distinct().count() == z.count() // res5: Boolean = true
// calculer le nombre des éléments de z
z.count() // res6: Long = 4
// afficher les éléments de z
z.collect() // res7: Array[(String, (Iterable[Int], Iterable[Int]))] = Array((B,(CompactBuffer(),CompactBuffer(3, 6))), (D,(CompactBuffer(10,1),CompactBuffer())),
// (A,(CompactBuffer(1, 2),CompactBuffer(3))), (C,(CompactBuffer(5),CompactBuffer(7))))
Cette méthode a plusieurs variantes :
2 à 3 arguments de type RDD[(K,W)]
Le nombre de partitions
Le nombre d’éléments de la RDD
obtenue est égal au nombre de clés
distinctes dans les deux RDD.
SI une clé est absente d’une RDD alors sa
valeur est remplacé par vide.
À retenirÀ retenir
ExempleExemple
44
M.MICHRAFY
45. OPÉRATION DE TRANSFORMATION : MÉTHODE COALESCE
La méthode coalesce
regroupe les données de la
RDD source dans un nombre
de données de partitions.
ObjectifObjectif
def coalesce(numPartitions: Int, shuffle: Boolean = false,
partitionCoalescer: Option[PartitionCoalescer] =
Option.empty)(implicit ord: Ordering[T] = null): RDD[T]
API : scala, Classe : RDD, Package : org.apache.spark
Entrée : numPartitions le nombre de partitions visé
shuffle pour l’étape de shuffle, par défaut false
partitionCoalescer optionnel, indique la manière de
fusionner les partitions
Sortie : une RDD de type T
SignatureSignature
// créer une RDD avec 50 partitions
val x = sc.parallelize( 1 to 1000, 50) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[25] at parallelize at <console>:24
// appliquer coalesce
val y = x. coalesce(10, true) // y: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[29] at coalesce at <console>:26
val z = x.coalesce(3, false) // z: org.apache.spark.rdd.RDD[Int] = CoalescedRDD[30] at coalesce at <console>:26
// afficher les partitions
y. getNumPartitions // res8: Int = 10
z. getNumPartitions // res9: Int = 3
ExempleExemple
La méthode coalesce est
similaire à la méthode
repartition
Elle permet de contrôler le
nombre de partitions d’ une
RDD
À retenirÀ retenir
45
M.MICHRAFY
46. OPÉRATION DE TRANSFORMATION : MÉTHODE REPARTITION
La méthode repartition
regroupe les données de la
RDD source dans un nombre
donné de partitions.
ObjectifObjectif
def repartition(numPartitions: Int)(implicit ord: Ordering[T] =
null): RDD[T]
API : scala, Classe : RDD, Package : org.apache.spark
Entrée : numPartitions le nombre de partitions visé
Sortie : une RDD de type T
SignatureSignature
// créer une RDD avec 50 partitions
val x = sc.parallelize( 1 to 1000, 50) // x: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[31] at parallelize at <console>:24
// appliquer repartition
val y = x. repartition(10) // y: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[35] at repartition at <console>:26
// afficher le nombre de partitions
y. getNumPartitions // res10: Int = 10
ExempleExemple
La méthode repartition
est similaire à la méthode
coalesce.
Cette méthode fait appel à
la méthode coalesce avec
les deux arguments :
numPartitions et shuffle=true
À retenirÀ retenir
46
M.MICHRAFY
47. OPÉRATION DE TRANSFORMATION : MÉTHODE SAVEASTEXTFILE
La méthode saveAsTextFile
enregistre le contenu de la
RDD dans le dossier passé en
argument
ObjectifObjectif
def saveAsTextFile(path: String): Unit
API : scala, Classe : RDD, Package : org.apache.spark
Entrée : path le nom fichier
Sortie : pas de valeur de retour (Unit).
SignatureSignature
// créer une RDD avec 50 partitions
val x = sc.parallelize( 1 to 20, 3)
val y = sc.parallelize(List((1,2),(3,5),(6,7),(8,10),(11,15),(18,20)), 2)
// enregistrer les données de la RDD x dans rdd_x
x.saveAsTextFile("rdd_x") // un dossier rdd_x est crée avec des fichiers contenant les données de chaque partition
// enregistrer les données de la RDD y dans rdd_x
y.saveAsTextFile("rdd_x") // Une exception se déclenche puisque le dossier rdd_x existe
ExempleExemple
saveAsTextFile crée un
dossier path et enregistre les
données de chaque partition
dans un fichier part-0000x où
x est un entier.
Si le dossier path existe alors
une exception se déclenche.
À retenirÀ retenir
47
M.MICHRAFY