Vous êtes sur la page 1sur 48

APACHE SPARK

RDD : TRANSFORMATIONS, ACTIONS PAS À PAS

DR MUSTAPHA MICHRAFY

CONTACT : DATASCIENCE.KM@GMAIL.COM
CONTEXTE M.MICHRAFY

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
PLAN M.MICHRAFY

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
OBJECTIF ET PRÉREQUIS M.MICHRAFY

Objectif Prérequis
Cette étude vise à mettre en pratique Spark • Connaissance de l’approche objet
pas à pas. Il s’agit d’explorer les opérations • Connaissance de la programmation
(transformations et actions) relatives à la fonctionnelle
structure résiliente « RDD ». • Connaissance du langage Scala

4
SPARK DÉFINITION ET MOTIVATIONS M.MICHRAFY

Apache spark ? Motivations


• Framework open source dédié au calcul distribué • Rapide
• Extension du design pattern map-reduce • 10 fois plus rapide que Hadoop sur disque
• Différents modes de traitement : interactif et • 100 fois plus rapide en mémoire que Hadoop
streaming • Facile à développer
• Exécution en mémoire • Riche en terme d’opérations
• Intervient dans le traitement de données dans un • Ecriture rapide des programmes
écosystème Big-Data • 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

5
POSITIONNEMENT DE SPARK DANS L’ECO-SYSTEME BIG DATA M.MICHRAFY

Batch/ETL Spark, MapReduce, Pig, Hive


processing

Stream Spark Streaming, Storm, Flink Streaming


processing

Machine Spark MLib, Mahout, Flink ML, R


Learning

Interrogation Spark SQL, Drill, Hive, Impala


SQL-Like

Graph Spark GraphX, Giraph, GraphLab


processing

HDFS, S3, Openstack swift, MapR FS, Cassandra

Stockage

6
COMPOSANTS DE SPARK M.MICHRAFY

Worker Node

Executor
Task Task
Spark driver
Worker Node
SparkContext Cluster Manager
Executor
Task Task

Worker Node

Executor
Task Task

7
M.MICHRAFY
SPARK DRIVER ET WORKERS

Spark driver
SparkContext
• Un programme Spark est composé de deux programmes :

1. Le programme pilote ( driver program )


2. Les programmes travailleurs (workers program)
Cluster Local
Manager Threads • 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


Worker Worker
Executor Executor
• Pas de communication entre les programmes travailleurs

HDFS, S3, Openstack swift, MapR FS, Cassandra

8
M.MICHRAFY
APACHE SPARK : VUE LOGIQUE ET APIS

Spark supporte plusieurs systèmes de stockage


Spark SQL Spark Spark GraphX Spark ML SparkR : HDFS, S3, Openstack swift, MapR FS,
Streaming Cassandra

RDD (Transformations et Action) Spark Core est le moteur de calcul et


d’exécution pour la plateforme Spark :
Spark Core 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
Système de stockage
transformation et Action.

Spark SQL est un module dédié au traitement SparkR est un package R offrant une interface
des données structurées avec une syntaxe légère pour utiliser Spark à partir de R. Dans
similaire à SQL. Il permet d’extraire, transformer Spark 2.0.2, SparkR fournit une implémentation Spark ML est une librairie
et charger des données sous différents formats de la Dataframe distribuée supportant des dédiée aux méthodes
(CSV, JSON, Parquet, base de données) et les opérations telles que la sélection, le filtrage, d’apprentissage distribués :
exposer pour des requêtes ad-hoc. l'agrégation. SparkR offre aussi des algorithmes Classification
d’apprentissages distribués. Clustering
Spark GraphX est dédié au traitement et à la
parallélisation de graphes. Ce module offre des Spark Streaming est dédié au traitement Régression
opérateurs et des algorithmes pour le temps-réel des données en flux. Il offre un Filtrage collaboratif
traitement des graphes. GraphX étend les RDD mode de traitement en micro-batch et Réduction de dimension
de Spark via la Resilient Distributed Dataset supportant différentes sources de données
Graph (RDDG) (Kafka, Flume, Kinesis ou TCP sockets …). 9
M.MICHRAFY
VUE GLOBALE SUR LES API SPARK : DÉPENDANCE ET INTERACTION

Spark Streaming Spark ML


Streaming Source
DStream
DStream
DStream Model
ModelML
Model ML
ML

Spark GraphX Spark core Spark SQL

Graph RDD
RDD Dataframe
GraphRDD
Graph RDD
RDD RDD Dataframe
Dataframe

File System Data Source

10
M.MICHRAFY
RDD, CARACTÉRISTIQUES, CRÉATION ET OPERATIONS (1)

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.

Caractéristiques Opérations Evaluation Lazy


1. Une RDD est une liste de partitions RDD supporte deux type d’opérations 1. Les transformations sont
2. Une RDD est associée à une liste de 1. Transformation paresseuses, évitant le calcul inutile.
dépendances avec les RDD parents 2. Action Ceci favorise l’optimisation du
3. Une RDD dispose d’une fonction pour calculer Une transformation consiste à traitement.
une partition appliquer une fonction sur 1 à n RDD 2. Une RDD transformée est calculée
4. Optionnellement, un objet « partionner » pour et à retourner une nouvelle RDD lorsqu’une action est appliquée sur
les RDD de type clé/valeur Une action consiste à appliquer une cette dernière.
5. Optionnellement, une liste indiquant fonction et à retourner une valeur
l’emplacement pour chaque partition

11
M.MICHRAFY
RDD, CARACTÉRISTIQUES, CRÉATION ET OPERATIONS (2)

In-Memory Immutable

Lazy
evaluated
Resilient
RDD
Parallele
Cacheable

Partitioned Typed

Fonction/type RDD Formats fichiers Création


Certaines fonctions sont disponibles Spark supporte de charger ou Trois manières de créer une RDD
seulement sur certains types de RDD : d’enregistrer des fichiers dans 1. A partir d’une source de données
1. mean, variance, stdev pour les RDD divers formats : non structuré, semi- 2. En parallélisant une collection via
numériques structuré, structuré. SparkContext
2. Join pour les RDD clé/valeur Les formats sont : 3. En appliquant une opération de
3. L’enregistrement de fichier utilisant o Text type transformation sur la RDD
des RDD basées sur des fichiers de o Json
format séquentiel. o CSV
Pour plus de détails, voir les RDD de type o SequenceFile
o PairRDDFunctions, o Protocole buffers
o DoubleRDDFunctions, o Object files
o SequenceFileRDDFunctions

12
M.MICHRAFY

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 13
OPÉRATION DE TRANSFORMATION : MÉTHODE MAP M.MICHRAFY

Objectif Signature À retenir


La méthode map retourne une RDD def map[U](f: (T) ⇒ U)(implicit arg0: ClassTag[U]): La RDD source et la RDD retournée
en appliquant une fonction passée RDD[U] ont le même nombre d’item
en argument à chaque item de la
RDD source Si les items de la RDD source sont de
API : scala, Classe : RDD, type T alors les items de la RDD
Il s’agit d’un design pattern Package : org.apache.spark retournée sont de type f(T)
incontournable de la programmation
fonctionnelle. Entrée : f est une fonction : (T) => U
Sortie : une RDD de type U

Exemple
// 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) 14
OPÉRATION TERMINALE : MÉTHODE REDUCE M.MICHRAFY

Objectif Signature À retenir


La méthode reduce agrège les def reduce(f: (T, T) ⇒ T): T Soit une loi * interne sur l’ensemble
éléments de la RDD source en E.
appliquant une fonction La loi * est associative SSI pour tout
commutative et associative passée API : scala, Classe : RDD, x,y,z de E on a : x *(y*z) = (x*y)*z
en argument. Package : org.apache.spark
C’est une opération terminale La loi * est commutative SSI pour
Il s’agit d’un design pattern Entrée : f est une fonction : (T,T) => T tout x, y de E on a : x * y = y * x
incontournable de la programmation Sortie : une valeur de retour de type T
fonctionnelle.

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

15
OPÉRATION DE TERMINALE : MÉTHODE COUNT M.MICHRAFY

Objectif Signature À retenir


La méthode count retourne le def count(): Long Les parenthèses ne sont pas
nombre d’éléments d’une RDD obligatoires lors de l’appel de
source. count.
API : scala, Classe : RDD, Ceci relève d’une règle en scala :
C’est une opération terminale Package : org.apache.spark pour toute méthode sans
argument, les parenthèse sont
Sortie : le nombre d’élements de la RRD, de type optionnelles.
Long

Exemple
// 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
16
OPÉRATION DE TERMINALE : MÉTHODE COUNTBYKEY M.MICHRAFY

Objectif Signature À retenir


La méthode countByKey compte le def countByKey(): Map[K, Long] countByKey est similaire à count
nombre de valeur par clé et retoune countBuKey est une méthode de la
une map class PairRDDFunctions.
Cette méthode nécessite une RDD API : scala, Classe : PairRDDFunctions, Attention, cette méthode n’est à
de type (K,V) Package : org.apache.spark utiliser que si la map retounée est
de volume faible.
C’est une opération terminale Sortie : Map[K, Long] , la valeur représente le
nombre d’éléments par clé K.

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

17
OPÉRATION DE TERMINALE : MÉTHODE FIRST M.MICHRAFY

Objectif Signature À retenir


La méthode first retourne le premier def first(): T Les parenthèses ne sont pas
élément de la RDD source obligatoires lors de l’appel de first.

C’est une opération terminale API : scala, Classe : RDD, Une exception se déclenche si
Package : org.apache.spark l’appel de first() se fait sur une RDD
source sans élément.
Sortie : le premier élément de la RDD de type T

Exemple
// 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;
18
OPÉRATION DE TERMINALE : MÉTHODE TAKE M.MICHRAFY

Objectif Signature À retenir


La méthode take retourne un def take(num: Int): Array[T] Si le num est négatif, la valeur de
tableau d’éléments, constitué des n retour est un tableau vide (Array())
premiers éléments de la RDD source. Si num est supérieur au nombre
API : scala, Classe : RDD, d’éléments de la RDD, alors tous les
Le nombre d’éléments à retourner est Package : org.apache.spark éléments de RDD sont retournés.
l’argument de la méthode take Si la RDD est vide (sans élément),
Entrée : num le nombre de éléments à retourner alors l’appel à take avec une
C’est une opération terminale Sortie : un tableau de type T valeur strictement positif lève une
exception

Exemple
// 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
19
OPÉRATION DE TERMINALE : MÉTHODE FLATMAP M.MICHRAFY

Objectif Signature À retenir


La méthode flatMap transforme def flatMap[U](f: (T) ⇒ TraversableOnce[U])(implicit flatMap est similaire à Map
chaque Item de la RDD source en arg0: ClassTag[U]): RDD[U] La fonction f doit retourner un trait
0 ou plusieurs items et retourne de type TraversableOnce qui
une nouvelle RDD. API : scala, Classe : RDD, consiste à parcourir une collection
Il s’agit d’un design pattern Package : org.apache.spark une ou plusieurs fois.
incontournable de la
programmation fonctionnelle. Entrée : f est une fonction : (T) ⇒ TraversableOnce[U]
Sortie : Une RDD[U]

Exemple 1 2
// créer une RDD comportant les éléments de 1 à 10 val z = parallelize(List("La meilleure facon de predire l’avenir est de le creer", "Celui qui
val x = sc.parallelize(List((1,2),(3,5),(8,10)) veut reussir trouve un moyen", "Celui qui veut rien faire trouve une excuse"))

// définir une fonction f val g = (s:String) => s.split(" ").toList


val f = (a:Tuple2[Int,Int]) => Seq(a._1, a._2)
// appliquer la fonction g sur les éléments de z
// appliquer flatMap sur x val t = z.flatMap(g)
Val y = x.flatMap(f)
// afficher les éléments de t
// afficher les éléments de y t.collect()
y.collect() //Sortie : res2: Array[String] = Array(La, meilleure, faþon, de, predire, l'avenir, est, de,
//Sortie : res1: Array[Int] = Array(1, 2, 3, 5, 8, 10) le, creer, Celui, qui, veut, reussir, trouve, un, moyen, Celui, qui, veut, rien, faire, trouve,
une, excuse)
20
OPÉRATION DE TRANSFORMATION : MÉTHODE FILTER M.MICHRAFY

Objectif Signature À retenir


La méthode filter retourne une RDD def filter(f: (T) ⇒ Boolean): RDD[T] La RDD source et la RDD obtenue
ne contenant que les items de la RDD ont le même type d’item (T)
source satisfaisant le prédicat f , passée
en argument. API : scala, Classe : RDD, Le nombre d’éléments de la RDD
Package : org.apache.spark obtenue est toujours inférieur au
Il s’agit aussi d’un design pattern nombre d’éléments de la RDD
incontournable de la programmation Entrée : f est une fonction : (T) => Boolean source
fonctionnelle. Sortie : une RDD de type T
Il est possible d’obtenir une RDD
sans item.

Exemple
// 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) 21
OPÉRATION DE TRANSFORMATION : MÉTHODE SAMPLE M.MICHRAFY

Objectif Signature À retenir


La méthode sample retourne un def sample(withRpl: Boolean, fraction: Double, Si withRpl = true, on utilise un
échantillon des données – qui est une seed: Long = Utils.random.nextLong): RDD[T] générateur basé sur la loi de
sélection aléatoire- de la RDD Poisson
source. API : scala, Classe : RDD, Package : org.apache.spark Si withRpl = false, on utilise un
générateur basé sur la loi de
Entrée : withRpl : avec ou sans répétition Bernoulli.
fraction : le % des données, dans [0 1]
seed : pour initialiser le générateur aléatoire
Sortie : une RDD de type T

Exemple
// 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)
22
OPÉRATION DE TRANSFORMATION: MÉTHODE TAKESAMPLE M.MICHRAFY

Objectif Signature À retenir


La méthode takeSample retourne un def takeSample(withReplacement: Boolean, num: Cette méthode retourne
tableau – qui est une sélection Int, seed: Long = Utils.random.nextLong): Array[T] exactement un tableau
aléatoire- de la RDD source. composé de num éléments de la
Le nombre d’éléments du tableau est API : scala, Classe : RDD, Package : org.apache.spark RDD source.
un argument de la méthode
takeSample Entrée : withRpl : avec ou sans répitition Elle est différente de la méthode
num : le nombre d’éléments à retourner sample puisque sample prend un
seed : pour initialiser le générateur aléatoire argument fraction indiquant le %
Sortie : un tableau de type T (Array[T]) d’éléments à retourner et
retourne une RDD.

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

23
OPÉRATION TERMINALE: MÉTHODE TAKEORDERED M.MICHRAFY

Objectif Signature À retenir


La méthode takeOrdered ordonne def takeOrdered(num: Int)(implicit ord: Cette méthode est à ne pas
les éléments de données du RDD en Ordering[T]): Array[T] utiliser lorsque le tableau retourné
utilisant l’ordre implicite (croissant) et est volumineux car les données
renvoie les n premiers éléments sous API : scala, Classe : RDD, Package : org.apache.spark sont chargées sur la mémoire du
forme d’un tableau. driver.
Entrée :
num: le nombre d’éléments à retourner Les éléments de la RDD source
doivent disposer d’une relation
Sortie : un tableau de type T (array[T]) d’ordre

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

24
OPÉRATION TERMINALE : MÉTHODE FOLD M.MICHRAFY

Objectifs Signature À retenir


Agréger les données en deux étapes. Il def fold(zeroValue: T)(op: (T, T) ⇒ T): T Fold et agrgregate sont
s’agit d’une opération de type Action. similaires en terme de
L’étape 1 consiste à appliquer op par API : scala, Classe : rdd, Package : org.apache.spark processus
partition Entrée : fold applique le même
L’étape 2 consiste appliquer op sur les - zeroValue : de type T, c est la valeur initiale de calcul fonction en 1 et 2 étape
résultats de la 1 étape - op est une fonction associative de signature : (T,T) => T Aggregate associe une
op doit être un opérateur associatif Sortie : une valeur de type T fonction à chaque étape

Exemple
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

25
OPÉRATION DE TRANSFORMATION : MÉTHODE MAPPARTITIONSWITHINDEX M.MICHRAFY

Objectifs Signature À retenir


Calculer une nouvelle RDD en def mapPartitionsWithIndex[U](f: (Int, Iterator[T]) ⇒ Iterator[U], Il est similaire à map
appliquant une fonction f sur chaque ind: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U] map applique une fonction sur
partition API : scala, Classe : rdd, Package : org.apache.spark les éléments de la RDD alors
il nécessite au moins un paramètre Entrée : que mapPartitionsWithIndex
de type fonction qui prend en entrée - f est une fonction (Int, Iterator[T]) ⇒ Iterator[U] applique une fonction f à
les éléments d’une partition et son - ind est boolean, dont la valeur par défaut est false, chaque partition
index Sortie : RDD[U]

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

26
OPÉRATION TERMINALE : MÉTHODE AGGREGATE M.MICHRAFY

Objectifs Signature
Agréger les données en deux étapes. Il def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U,
s’agit d’une opération de type Action combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): U
L’étape 1 consiste à appliquer seqOp par
partition API : scala, Classe : rdd, Package : org.apache.spark
L’étape 2 consiste à appliquer combOp Entrée
aux résultats de l’étape 1 - 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

Exemple
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 27
OPÉRATION DE TRANSFORMATION : MÉTHODE MAPVALUES M.MICHRAFY

Objectif Signature À retenir


La méthode mapValues, s’applique à def mapValues[U](f: (V) ⇒ U): RDD[(K, U)] La RDD source et la RDD obtenue
une RDD de type (K,V) et retourne une ont :
RDD de type (K,U). • La même taille.
API : scala, Classe : PairRDDFunctions, • Les mêmes clés
Cette méthode prend en argument Package : org.apache.spark • Les mêmes partitions
une fonction f: (V) ⇒ U, et transforme
chaque pair (K,V) en (K, f(U)). Sortie : RDD[(K, U)]

C’est une opération de transformation

Exemple
1 2
// créer une RDD constitué d’une liste de fruits // appliquer mapValues sur y avec la fonction f
val x = sc.parallelize(List("Abricot", "Cerise", "Nectarine", "Noisette", "Kiwi")) val z = y.mapValues(f)
x: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[31] at parallelize //Sortie : z: org.apache.spark.rdd.RDD[(String, (Int, Int))] =
at <console>:24 MapPartitionsRDD[33] at mapValues at // <console>:30

// créer une RDD de type (K,V), avec V = la taille de K // afficher le résultat


val y = x.map(w => (w,w.length)) z.collect()
y// : org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[30] at map res2: Array[(String, (Int, Int))] = Array((Abricot,(3,1)), (Cerise,(3,0)),
at <console>:26 (Nectarine,(4,1)), (Noisette,(4,0)), (Kiwi,(2,0)))
// /afficher la RDD y
y.collect() // res1: Array[(String, Int)] = Array((Abricot,7), (Cerise,6), y.Count == z.count
(Nectarine,9), (Raisin,6), (Kiwi,4)) res49: Boolean = true
// créer une fonction f
val f = (n:Int) => (n/2, n%2) // f: Int => (Int, Int) = <function1> 28
OPÉRATION TERMINALE : MÉTHODE COLLECT M.MICHRAFY

Objectifs Signature À retenir


La méthode collect retourne un tableau ( def collect(): Array[T] Cette méthode est couteuse
Array) qui représente les éléments de la en terme de CPU. Par
RDD. API : scala, Classe : rdd, Package : org.apache.spark conséquent, elle est à utiliser
Il est utile lors de la mise au point d’un seulement si la RDD a un faible
code. Sortie : Array[T] volume.

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

29
OPÉRATION TERMINALE : MÉTHODE FOREACH M.MICHRAFY

Objectifs Signature À retenir


La méthode foreach applique une def foreach(f: (T) ⇒ Unit): Unit Attention, foreach n’affiche
fonction f (T) => Unit, sur chaque élément pas forcement la même chose
de la RDD API : scala, Classe : rdd, Package : org.apache.spark que la méthode collect
Il est utile lors de la mise au point d’un
code. Entrée :
f une fonction ( (T) => Unit)
Sortie : pas de valeur de retour (Unit)

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

30
OPÉRATION DE TRANSFORMATION : MÉTHODE COLLECT AVEC DES ARGUMENTS M.MICHRAFY

Objectifs Signature À retenir


Elle retourne une RDD comportant les collect[U](f: PartialFunction[T, U])(implicit arg0: Elle est similaire à la méthode
éléments correspondant à ClassTag[U]): RDD[U] collect sans argument.
l'application d’une fonction partielle collect() retourne un tableau
Cette méthode est couteuse en API : scala, Classe : rdd, Package : org.apache.spark alors que collect(PartialFunction)
terme de CPU. Par conséquent, elle Sortie : Array[T] retourne une RDD [U] après avoir
est à utiliser seulement si la RDD a un appliqué la fonction partielle sur
faible volume. les items de la RDD.

Exemple
// 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)
31
OPÉRATION DE TRANSFORMATION : MÉTHODE DISTINCT M.MICHRAFY

Objectifs Signature À retenir


La méthode distinct retourne une def distinct(): RDD[T] Il est possible de mettre en
RDD qui comporte les éléments place la méthode distinct en
distincts de la RDD source API : scala, Classe : rdd, Package : org.apache.spark utilisant :
• map
Sortie : RDD[T] • reduceByKey

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

32
OPÉRATION DE TRANSFORMATION : MÉTHODE UNION M.MICHRAFY

Objectifs Signature À retenir


La méthode union retourne une RDD def union(other: RDD[T]): RDD[T] Les éléments de la RDD source
qui est l’union des items de la RDD et la rdd other doivent être de
source et de la RDD passée en API : scala, Classe : rdd, Package : org.apache.spark même type (ici T)
argument. Plus précisément, les éléments
il est possible d’avoir des éléments Entrée : other est de type RDD[T]. de la RDD other doivent être de
identiques dans la RDD résultat. Pour Sortie : RDD[T] même type que ceux de la rdd
éviter cela, utiliser la méthode distinct source.

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

33
OPÉRATION DE TRANSFORMATION : MÉTHODE INTERSECTION M.MICHRAFY

Objectifs Signature À retenir


La méthode intersection retourne une def intersection(other: RDD[T]): RDD[T] Les éléments de la RDD other
RDD qui est l’intersection des items du doivent être de même type que
RDD source et de la RDD passée en API : scala, Classe : rdd, Package : org.apache.spark ceux de la rdd source.
argument. intersection peut être codée
Attention, le résultat de retour ne Entrée : other est de type RDD[T]. avec :
comporte pas de doublant même si Sortie : RDD[T] • map,
la RDD source ou argument en • cogroup
comporte. • filter

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

34
OPÉRATION DE TRANSFORMATION : MÉTHODE SUBTRACT M.MICHRAFY

Objectifs Signature À retenir


La méthode subtract est def subtract(other: RDD[T]): RDD[T] La RDD other doit être de même
l’implémentation de l’opération type que la RDD source. Dans le
soustraction au sens ensemble. API : scala, Classe : rdd, Package : org.apache.spark cas contraire, on obtient une
Elle retourne les éléments de la RDD erreur.
source mais qui ne sont pas dans la Entrée : other est de type RDD[T] Il existe des variante de subtract
RDD passée en argument. Sortie : RDD[T] portant sur le nombre des
partitions.

Exemple 2
1
// créer les 2 rdds // créer une RDD
val x = sc.parallelize(1 to 5, 2) val z = sc.parallelize(List("A","B","D","E"),2)
val y = sc.parallelize(3 to 7, 2)
// appliquer la méthode subtract sur x et z
x.collect() // res18: Array[Int] = Array(1, 2, 3, 4, 5) // cette instruction génère une erreur
y.collect() // res19: Array[Int] = Array(3, 4, 5, 6, 7) val t = x.subtract(z)
error: type mismatch;
// appliquer la méthode subtract et generer la RDD s
found : org.apache.spark.rdd.RDD[String]
val s = x.subtract(y)
required: org.apache.spark.rdd.RDD[Int]
val t = x.subtract(z)
// afficher les éléments
s.collect() // res20: Array[Int] = Array(2, 1)
35
OPÉRATION DE TRANSFORMATION : MÉTHODE ZIP M.MICHRAFY

Objectifs Signature À retenir


La méthode zip consiste à construire def zip[U](other: RDD[U])(implicit arg0: ClassTag[U]): RDD[(T, U)] Pour réaliser une opération zip,
une RDD de type (K,V) à partir de la les deux RDD doivent avoir :
RDD[K] source et la RDD[V] passée en API : scala, Classe : rdd, Package : org.apache.spark • Le même nombre de
argument. partitions
L’élément i de la RDD résultante est Entrée : other est de type RDD[U]. • Le même nombre
(ki,vi) avec ki, vi les éléments d’indice i Sortie : un RDD de type (T,U) d'éléments dans chaque
resp. des RDD source et argument. Partition

Exemple 1 Exemple 2
// créer les deux rdds // créer deux rdds
val key = sc.parallelize(1 to 5, 2) val value_nes = sc.parallelize(11 to 16, 2 )
val value = sc.parallelize(11 to 15, 2 ) val value_nps = sc.parallelize(11 to 15, 3 )

// appliquer la méthode zip // appliquer la méthode zip


val x = key.zip(value) val x_nes = key.zip(value_nes)
val x_nps = key.zip(value_nep)
// afficher les éléments
x.collect() // Des exceptions se lèvent lors de l’appel de collect
// res21: Array[(Int, Int)] = Array((1,11), (2,12), (3,13), (4,14), (5,15)) 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

36
OPÉRATION DE TRANSFORMATION : MÉTHODE GROUPBY M.MICHRAFY

Objectif Signature À retenir


La méthode groupBy regroupe les def groupBy[K](f: (T) ⇒ K)(implicit kt:
Cette méthode est très couteuse.
éléments de la RDD[T] source par clé ClassTag[K]): RDD[(K, Iterable[T])]
Il existe deux variantes de cette méthode,
et retourne une RDD [(k, Iterable[T])]
prenant en plus soit le nombre de partitions, soit
Elle prend en entrée une fonction f : API : scala, Classe : RDD
un partitionner
(T) => K, qui s’applique à chaque Package : org.apache.spark
Attention la RDD est de type (K, Iterable[T])],
élément de la RDD source pour
sachant que f : (T) => K
générer les clés de la RDD retournée. Sortie : RDD[(K, Iterable[T])]
Elle est similaire avec la méthode groupByKey

Exemple 1 2
// créer la rdd de type (Int) avec 10 partitions // appliquer la groupBy , avec f, sur les éléments de y
val x = sc.parallelize(1 to 20, 10) val z = y.groupBy(f)

// faire une selection aléatoire dans les éléments de x // appliquer groupBy, avec g, sur les éléments de y
val y = x.sample(false, 0.1, 19) val s = y.groupBy(g)

// definir la function f // afficher z et s


val f = (w:Int) => w%3 // f: Int => Int = <function1> 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)))
// définir une fonction g
val g = (w:Int) => 2*w + 7 // g: Int => Int = <function1>
37
OPÉRATION DE TRANSFORMATION : MÉTHODE GROUPBYKEY M.MICHRAFY

Objectif Signature À retenir


Pour une RDD de type (K, V), def groupByKey(): RDD[(K, Iterable[V])] groupByKey est une méthode de la classe
groupByKey retourne une RDD de PairRDDFunctions. Elle s’applique à des RDD de type
type (K,Iterable<V>) API : scala, (k,v).
Si le regroupement vise une Classe : PairRDDFunctions, groupByKey utilise un partitionner par défaut
agrégation, pour des raisons de Package : org.apache.spark Il existe différente variante de cette méthode :
performance, il est conseillé d’utiliser - def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]
- def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]
reduceByKey ou aggregateByKey. Sortie : RDD[(K, Iterable[V])]

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

38
OPÉRATION DE TRANSFORMATION : MÉTHODE REDUCEBYKEY M.MICHRAFY

Objectif Signature À retenir


Pour une RDD de type (k,v), def reduceByKey(func: (V, V) ⇒ V): RDD[(K, V)] reduceByKey estune méthode
reduceByKey retourne une RDD de de la classe PairRDDFunctions.
type (k,v) où les valeurs de chaque API : scala, Classe : PairRDDFunctions, Package : il existe deux variantes de cette
clé sont agrégées en utilisant la org.apache.spark méthode nécessitant en plus soit
fonction f de type (v,v) => v. le nombre de partitions soit un
Entrée : une fonction associative, commutative de type (v,v) => v partitionner.
Sortie RDD[(K, V)]

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

39
OPÉRATION DE TRANSFORMATION : MÉTHODE AGGREGATEBYKEY M.MICHRAFY

Objectif Signature À retenir


Pour une RDD[(k,v)], aggregateByKey def aggregateByKey[U](zero: U)(seqOp: (U, V) ⇒ U, aggregateByKey est une
retourne une RDD[(k,u)] où les valeurs combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): méthode de la classe
de chaque clé sont agrégées en RDD[(K, U)] PairRDDFunctions.
appliquant les fonctions seqOp et il existe deux variantes de cette
combOp et la valeur neutre API : scala, Classe : PairRDDFunctions, méthode nécessitant en plus
zeroValue. Package : org.apache.spark soit le nombre de partitions soit
Elle est similaire à aggregate sauf Entrée : zero valeur neutre de type U un partitionner.
que l'agrégation est appliquée aux seqOp : (U,V) => U et CombOp : (U,U) => U
valeurs ayant la même clé Sortie : RDD[(K, V)]

Exemple
// 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)) 40
OPÉRATION DE TRANSFORMATION : MÉTHODE SORTBYKEY M.MICHRAFY

Objectif Signature À retenir


Pour une RDD[(k,v)], sortByKey def sortByKey(ascending: Boolean = true, sortByKey est une méthode de
retourne une RDD[(k,v)] triée selon un numPartitions: Int = self.partitions.length): RDD[(K, V)] la classe OrderedRDDFunctions.
ordre prédéfini Par conséquent, la RDD source
La clé doit implémenter le trait API : scala, Classe : OrderedRDDFunctions, doit avoir des élements de type
Ordered Package : org.apache.spark (K,V)
Entrée : ascending boolean indiquant le type de trie
numPartitions, optionnel, indique le nombre
de partitions
Sortie : RDD[(K, V)]

Exemple 1 Exemple 2
// créer une rdd de type (k,v) case class A(tag:String, load:Int) extends Ordered[A] {
val x = sc.parallelize(List(("B", 4),("K", 2),("F", 2),("O", 5),("R",4)),3) def compare( a:A ) = tag.compareTo(a.tag)
}
// appliquer la méthode sortByKey // créer une rdd de type (k,v)
val yas = x.sortByKey(true) val xls = List( A("w",50), A("v",2), A("l",7), A("s",6))
val yde = x.sortByKey(false) val xlsrdd = sc.parallelize(xls,2)
val v = sc.parallelize(1 to 4,2)
// afficher les résultats val xx = xlsrdd.zip(v)
yas.collect() // res30: Array[(String, Int)] = Array((B,4), (F,2), (K,2), (O,5), (R,4)) // appliquer la méthode sortByKey
yde.collect() // res32: Array[(String, Int)] = Array((R,4), (O,5), (K,2), (F,2), (B,4)) 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))
41
OPÉRATION DE TRANSFORMATION : MÉTHODE JOIN M.MICHRAFY

Objectif Signature À retenir


La méthode join nécessite des RDD def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))] il existe deux variantes de cette
de type (K,V) méthode nécessitant en plus
Elle permet de faire une jointure sur la API : scala, Classe : PairRDDFunctions, soit le nombre de partitions soit
clé entre la RDD source et la RDD Package : org.apache.spark un partitionner.
passée en argument. Entrée : other est une RDD de type (K,W) il existe aussi une jointure à
La valeur de retour est une RDD de Sortie : RDD[(K, (V, W))] droite ou à gauche
type (K, (V, W)) ( voir leftOuterJoin, rightOuterJoin)

Exemple
// 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)))
42
OPÉRATION DE TRANSFORMATION : MÉTHODE CARTESIAN M.MICHRAFY

Objectif Signature À retenir


La méthode cartesian retourne une def cartesian[U](other: RDD[U])(implicit arg0: La RDD retournée est une
RDD[(K,V)] qui est le produit cartésien ClassTag[U]): RDD[(T, U)] instance de la classe
de la RDD source et la RDD PairRDDFunctions,
argument. API : scala, Classe : RDD,
Package : org.apache.spark Pour un élément (k,v) de la RDD
La RDD retournée est de taille nxm retournée, k est un élément de la
avec n, m les nombres d’éléments de Entrée : other est une RDD de type U RDD source et v est un élément
la RDD source et la RDD passée en Sortie : une RDD de type (T,U) de la RDD passée en argument
argument.

Exemple
// 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)))
43
OPÉRATION DE TRANSFORMATION : MÉTHODE COGROUP M.MICHRAFY

Objectif Signature À retenir


Pour une RDD source (K,V) et une def cogroup[W](other: RDD[(K, W)]): RDD[(K, Cette méthode a plusieurs variantes :
RDD de type (K,W), la méthode (Iterable[V], Iterable[W]))] 2 à 3 arguments de type RDD[(K,W)]
cogroup retourne une RDD de Le nombre de partitions
type (K, (Iterable<V>, API : scala, Classe : PairRDDFunctions, Le nombre d’éléments de la RDD
Iterable<W>)). Package : org.apache.spark obtenue est égal au nombre de clés
distinctes dans les deux RDD.
Entrée : other est une RDD de type (K,W) SI une clé est absente d’une RDD alors sa
Sortie : une RDD de type [(K, (Iterable[V], valeur est remplacé par vide.
Iterable[W]))

Exemple
// 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))))
44
OPÉRATION DE TRANSFORMATION : MÉTHODE COALESCE M.MICHRAFY

Objectif Signature À retenir


La méthode coalesce def coalesce(numPartitions: Int, shuffle: Boolean = false, La méthode coalesce est
regroupe les données de la partitionCoalescer: Option[PartitionCoalescer] = similaire à la méthode
RDD source dans un nombre Option.empty)(implicit ord: Ordering[T] = null): RDD[T] repartition
de données de partitions.
API : scala, Classe : RDD, Package : org.apache.spark Elle permet de contrôler le
Entrée : numPartitions le nombre de partitions visé nombre de partitions d’ une
shuffle pour l’étape de shuffle, par défaut false RDD
partitionCoalescer optionnel, indique la manière de
fusionner les partitions
Sortie : une RDD de type T

Exemple
// 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
45
OPÉRATION DE TRANSFORMATION : MÉTHODE REPARTITION M.MICHRAFY

Objectif Signature À retenir


La méthode repartition def repartition(numPartitions: Int)(implicit ord: Ordering[T] = La méthode repartition
regroupe les données de la null): RDD[T] est similaire à la méthode
RDD source dans un nombre coalesce.
donné de partitions. API : scala, Classe : RDD, Package : org.apache.spark
Cette méthode fait appel à
Entrée : numPartitions le nombre de partitions visé la méthode coalesce avec
les deux arguments :
Sortie : une RDD de type T numPartitions et shuffle=true

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

46
OPÉRATION DE TRANSFORMATION : MÉTHODE SAVEASTEXTFILE M.MICHRAFY

Objectif Signature À retenir


La méthode saveAsTextFile def saveAsTextFile(path: String): Unit saveAsTextFile crée un
enregistre le contenu de la dossier path et enregistre les
RDD dans le dossier passé en API : scala, Classe : RDD, Package : org.apache.spark données de chaque partition
argument dans un fichier part-0000x où
Entrée : path le nom fichier x est un entier.

Sortie : pas de valeur de retour (Unit). Si le dossier path existe alors


une exception se déclenche.

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

47
DR MUSTAPHA MICHRAFY

CONTACT : DATASCIENCE.KM@GMAIL.COM

Vous aimerez peut-être aussi