Bases de Données / Databases

Site Web de l'équipe BD du LIP6 / LIP6 DB Web Site

Outils pour utilisateurs

Outils du site


site:enseignement:master:bdle:tmes:tme3-prise-main-spark

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
site:enseignement:master:bdle:tmes:tme3-prise-main-spark [28/10/2016 17:40]
hubert [Illustration du flatMap]
site:enseignement:master:bdle:tmes:tme3-prise-main-spark [15/10/2018 09:09] (Version actuelle)
amine [Interrogation des données]
Ligne 1: Ligne 1:
-{{indexmenu_n>​2}}+{{indexmenu_n>​30}}
  
-====== ​Prise en main de Spark ======+====== ​[TME II-1]  Introduction à Spark (Algèbre RDD)   ====== 
 + 
 +**Remarque générale :** Le cours ne peut être //​self-contained//​ --> consulter la documentation en ligne de [[https://​spark.apache.org/​docs/​2.1.1/​api/​scala/​index.html#​package|Spark]].
  
 Pour l'aide sur l'​utilisation de Spark voir [[spark|ici]] Pour l'aide sur l'​utilisation de Spark voir [[spark|ici]]
- 
  
 ===== Exercice 1 ===== ===== Exercice 1 =====
-Commencer par copier et décompresser dans votre espace de travail le fichier ​ 
- 
-<​code>/​Infos/​bd/​spark/​bdle/​2015/​data/​wordcount.txt.bz2</​code>​ 
- 
-Le fichier obtenu, wordcount.txt (45Mo), contient des statistiques d’accès aux pages Wikimdia en différentes langue. ​ 
-Une ligne de la forme  En.d updates 3 24145 indique dans une page écrite en anglais (symbole ‘En’), intitulée ​ ‘updates’ ​ 
-qui a été cliqué 3 fois et qui fait 24145 octets. 
  
 +Copier le fichier ​
 +<​code>​ /​Infos/​bd/​spark/​bdle/​2015/​data/​wordcount.txt.bz2</​code>​ sur votre espace personnel.
 Lancer le spark-shell en mode local ([[spark|voir Doc]]) en suivant les instructions fournies ​ Lancer le spark-shell en mode local ([[spark|voir Doc]]) en suivant les instructions fournies ​
 puis charger le fichier ​ puis charger le fichier ​
Ligne 22: Ligne 18:
 <​code>​ val data = sc.textFile("<​le_chemin_dans_votre_espace_perso>/​wordcount.txt"​)</​code>​ <​code>​ val data = sc.textFile("<​le_chemin_dans_votre_espace_perso>/​wordcount.txt"​)</​code>​
  
-Ici, la variable data est une RDD, et donc, de type Array tel que vous pouvez le constater sur le shell. 
-L’invocation de la méthode take(n) sur cette variable affiche les n premiers éléments sur une seule ligne. Pour un affichage sur plusieurs lignes (tel un head -n sous linux), utiliser plutôt take(n).foreach(println) qui itère sur les éléments de la variable d’où elle est invoquée et affiche chaque élément sur une ligne séparément. 
-Noter également que la méthode count retourne la cardinalité (nombre d’éléments) de la RDD d’où elle est invoquée. 
-Par soucis de lisibilité de votre code,  stocker le résultat de chaque question dans des variables nommées q1, q2, q3 etc. 
-Penser à tester au fur et à mesure le résultat des instructions lorsque celles-ci ne contiennent que des transformations. 
-  
-==== Manipulation de RDDs Simples ==== 
- 
-  - Extraire le 4e champ de chaque élément de data et le convertir en type double 
-  - Construire à partir de q1 une liste contenant les nombres compris strictement entre 1000 et 1300 puis convertir en type entier. 
-  - Construire à partir de q2 une liste contenant les multiples de 3 et l’appeler q33. Faire de même pour les multiples de 4 et l’appeler q34.  
-  - Construire une liste obtenue en divisant par 10 chaque élément de q33. 
-  - Construire à partir de q4 un ensemble d’éléments (liste sans doublons).Construire une liste contenant les éléments de q2 qui sont multiples de 3 et de 4 à la fois. Utiliser impérativement q33 et q34.  
-  - Construire une liste contenant les éléments de q2 qui sont multiples de 3 mais pas de 4. Utiliser impérativement q33 et q34.  
-  - Construire à partir de q2 une liste contenant les éléments de q2 multiples de 3 ou de 10. Mettre les résultats dans la variable q8. 
-  - Convertir les éléments de q8 en type Double puis calculer la somme (q9sum), le minium (q9min) ainsi que le maximum (q9max) des éléments de q8. Calculer ​ la moyenne (q9avg) en utilisant un operateur parmi reduce, fold  et aggregate. 
-  - Retourner pour chaque élément distinct de q2 le nombre de fois qu’il apparaît dans cette liste. 
  
 ==== Manipulation de RDDs sous forme de paires clé-valeur ==== ==== Manipulation de RDDs sous forme de paires clé-valeur ====
Ligne 47: Ligne 26:
   - Grouper les paires par ‘mot’ et additionner leur nombre nb.   - Grouper les paires par ‘mot’ et additionner leur nombre nb.
   - Reprendre les questions 3 et 4 en calculant ‘mot’ différemment : désormais, ‘mot’ doit correspondre au préfixe du premier sous-élément de chaque élément de list, çad, pour en.d, mot doit être en, pour fr.d, mot doit être fr, etc. Comparer les résultats avec ceux obtenus précédemment.   - Reprendre les questions 3 et 4 en calculant ‘mot’ différemment : désormais, ‘mot’ doit correspondre au préfixe du premier sous-élément de chaque élément de list, çad, pour en.d, mot doit être en, pour fr.d, mot doit être fr, etc. Comparer les résultats avec ceux obtenus précédemment.
-**Remarque** pour partitionner une chaîne de caractères en utilisant le point (.) comme délimiteur à l'aide de la méthode split(), il faut protéger le point avec \, i.e split("​\."​)+**Remarque** pour partitionner une chaîne de caractères en utilisant le point (.) comme délimiteur à l'aide de la méthode split(), il faut protéger le point avec \, i.e split("​\\.") 
 + 
 + 
 +<showif isloggedin>​ 
 +**Réponse** 
 +<code scala> 
 +//​1. Structurer le contenu de data de sorte à obtenir un tableau de tableaux de chaines de caractères. Ce dernier devra être stocké ​ dans une nouvelle variable nommée list. 
 + 
 +val list = data.map(_.split("​ ")) 
 + 
 + 
 +//​2. Afficher ensuite les 100 premiers éléments de la 3e colonne de list. 
 + 
 +val q12 = list.map(x=>​x(2)) 
 + 
 +//​3. Transformer le contenu de list en une liste de paires (‘mot’, nb) où mot correspond à la première colonne de list et nb sa troisième colonne.  
 + 
 +val q13 = list.map(x=>​(x(0),​x(2).toInt)) 
 + 
 +//​4. Grouper les paires par ‘mot’ et additionner leur nombre nb. 
 + 
 +val q14 = q13.reduceByKey((x,​y)=>​x+y)  
 +//ou bien q13.reduceByKey(_+_) 
 + 
 +//​5. Reprendre les questions 3 et 4 en calculant ‘mot’ différemment : désormais, ‘mot’ doit correspondre au préfixe du premier sous-élément de chaque élément de list, çad, pour en.d, mot doit être en, pour fr.d, mot doit être fr, etc. 
 + 
 +val q13bis = list.map(x=>​(x(0),​x(2).toInt)).map(x=>​(x._1.split("​\\."​)(0),​ x._2)) 
 + 
 + 
 + 
 + 
 + 
 +</​code>​ 
 +</​showif>​
 ===== Exercice 2 ===== ===== Exercice 2 =====
 Cet exercice s’intéresse à la formulation de jointures simples en Scala. Cet exercice s’intéresse à la formulation de jointures simples en Scala.
Ligne 72: Ligne 84:
   * films (MovieID, Title, Genres)   * films (MovieID, Title, Genres)
  
-=== Structure de donnée : Dataset=== +
-Pour le TME sur les Dataset, récupérer le fichier suivant: +
-<code bash> +
-cp /​Infos/​bd/​spark/​tme-dataset-etudiant.scala <votre repertoire de travail>​ +
-emacs tme-dataset-etudiant.scala & +
-</​code> ​+
  
  
Ligne 94: Ligne 101:
 Par exemple, pour le nuplet (1,Toy Story (1995),​Animation|Children'​s|Comedy) de films, il existe trois nuplets dans films_bis : (1,Toy Story (1995),​Animation),​ (1,Toy Story (1995), Children'​s) et (1,Toy Story (1995), Comedy). ​ Par exemple, pour le nuplet (1,Toy Story (1995),​Animation|Children'​s|Comedy) de films, il existe trois nuplets dans films_bis : (1,Toy Story (1995),​Animation),​ (1,Toy Story (1995), Children'​s) et (1,Toy Story (1995), Comedy). ​
 Indice: pour construire films_bis, il est possible d’imbriquer une fonction map à l’intérieur d’une autre (cf. question 2 de l’exercice 3). Indice: pour construire films_bis, il est possible d’imbriquer une fonction map à l’intérieur d’une autre (cf. question 2 de l’exercice 3).
-===== Exercice subsidiaire ===== 
  
-==== Conversion d'un objet List en un objet Map ==== + 
-Expliquer l'​expression+<showif isloggedin>​ 
 +**Réponse**
 <code scala> <code scala>
-val List(("​ALice"​,22)("​Bob"​,20)("​Charles"​,22)).toMap+//a. le nombre de notes (ratings) réalisées par chaque utilisateur identifié par son UserID 
 +val q2a  ​notes.map{case(userId,movieId,rating,ts)=>(userId,1)}.reduceByKey(_+_)
  
-val List((22"​ALice"​), (20"​Bob"​), (22"​Charles"​)).toMap+//b. le nombre de notes (ratings) réalisées par chaque localisation donnée par le Zip-code 
 +val q2b utilis.map{case(userId,​gender,​age,​occup,​zipcode)=>​(userId,zipcode)} join(notes.map{case(userId,movieId,​rating,​ts)=>​(userId,1)}) map{case (userId,(zipcodenb))=>​(zipcode,​ 1)} reduceByKey(_+_)
  
-</​code>​ 
  
 +//c. le nombre de notes (ratings) réalisées par chaque genre de film
 +//jointure en notes et films 
 +val q2c = notes.map(x=>​(x(1),​1)).join(films.map(x=>​(x(0),​x(2)))).map{case (movieID, (genre,​nb))=>​(genre,​ 1)}.reduceByKey(_+_)
  
-==== Illustration du flatMap ==== 
  
-Expliquer la différence entre les expressions ​map et flatMap suivantes : +//d. les 10 utilisateurs ayant noté le plus de films. 
 +val q2d = notes.map(x=>​(x(0),​1)).reduceByKey(_+_).takeOrdered(10)
  
-<code scala> +//e. Les films ayant reçu le moins de notes 
-val x = List(1, 2, 3).map(x => List(x, 10*x, 100*x) +//f. Les utilisateurs n’ayant noté aucun film
-val x = List(1, 2, 3).flatMap(x => List(x, 10*x, 100*x))+
  
-val y = List(List((1,​ 11), (1, 111)), List((2, 22), (2, 222))).map(x => x) 
-val y = List(List((1,​ 11), (1, 111)), List((2, 22), (2, 222))).flatMap(x => x) 
  
-</​code>​ 
  
-<code scala> +//genre de films 
- ​val ​ys Map("​a"​ -List(1 -> 111 -> 111), "b" ​-> List(2 -> 222, 2 -> 22)).flatMap(_._1  +val films_bis ​films.map(x=>(x._1,x._2,x._3.split("\\|"))).flatMap{case(a,b,l)=>l.map(x=>​(a,​b,​x))}
-</code>+
  
-Attention, le flatMap appliqué sur une Map retourne une Map. Pour garantir l'​unicité de la clé seules la dernière occurence d'une paire (k,v) est conservée. 
-<code scala> 
- val ys = Map("​a"​ -> List(1 -> 11, 1 -> 111), "​b"​ -> List(2 -> 222, 2 -> 22)).flatMap(_._2) 
 </​code>​ </​code>​
 +</​showif>​
  
-La fonction passée en paramêtre au flatMap doit retourner une séquence. Ici une chaine de caractère est considérée implicitment comme une séquence de caractères. 
-<code scala> 
-val pers = Array( (1,"​pierre"​),​ (5,"​alice"​),​ (4, "​paul"​)) 
-val v = pers.flatMap(x=>​x._2) 
-val v = pers.flatMap{ case(numéro,​ prénom) =>​prénom) 
  
-</​code>​ +=== Exercice Subsidiaire :  reprendre les questions précédentes en utilisant l'API Dataset ​=== 
- +Pour utiliser les Datasetrécupérer ​le fichier suivant: 
-==== Composition de Map ====  +<code bash
-En considérant la variable pers de la question précédenteexpliquer ​le résultat des instructions suivantes +cp /​Infos/​bd/​spark/​tme-dataset-etudiant.scala <votre repertoire de travail
-  * pers.flatMap(x=>(x._2) map(y=>​y)) +emacs tme-dataset-etudiant.scala & 
-  * pers.flatMap(x=>(x._2) map(y=>​x._1)) +</code
-  * pers.flatMap(x=>​(x._2) map(y=>x._2)) +
-et expliquer pourquoi les deux instructions suivantes sont erronées +
-  * pers.flatMap(x=>(x._1) map(y=>​y)) +
-  * pers.flatMap(x=>​(x._2) map(y=>​y._1)))+
  
  
site/enseignement/master/bdle/tmes/tme3-prise-main-spark.1477669236.txt.gz · Dernière modification: 28/10/2016 17:40 par hubert