Ceci est une ancienne révision du document !
Remarque générale : Le cours ne peut être self-contained –> consulter la documentation en ligne de Spark.
Cet exercice illustre les différentes structures de contrôle de Scala présentées en cours. Il permet de comprendre le paradigme fonctionnel : seules les fonctions map, reduce, flatten, filter, flatMap sont autorisées.
Tester au fur et à mesure ces fonctions sur listeEntiers construit comme suit :
val listeEntiers = List.range(1,11)
Soit une liste chaine de caractères construite à l'aide de l'instruction suivante
val listeTemp = List("7,2010,04,27,75", "12,2009,01,31,78", "41,2009,03,25,95", "2,2008,04,28,76", "7,2010,02,32,91")
Chaque élément représente un enregistrement fictif de températures avec le format (station, année, mois, température, code_département).
Bien entendu, il faudra faire les transformations et les conversions de type nécessaires!
Réponse
val temp2009 = listeTemp.map(x=>x.split(",")).filter(_(1).toInt==2009).map(x=>x(3).toInt) maxEntiers(temp2009) moyEntiers(temp2009)
Soit une liste chaine de caractères construite à l'aide de l'instruction suivante
val melange = List("1233,100,3,20171010", "1224,22,4,20171009", "100,lala,comedie", "22,loup,documentaire")
Deux types d'éléments existent : ceux de la forme (userID, movieID, rating, timestamp) et ceux de la forme (movieID, title, genre). Le domaine des userID est [1000, 2000] et celui des movieID est [0, 100].
Il est demandé de construire à partir de melange deux listes distinctes :
Soit une liste personnes contenant des tuples ayant trois attributs décrivant des personnes :
les enseignants.
val personnes = List(("Joe", "etu", 3), ("Lee", "etu", 4), ("Sara", "ens", 10), ("John", "ens", 5), ("Bill", "nan",20))
en entrée. Par exemple, le tuple (“Joe”, “etu”, 3) devra être transformé en un objet Etu(“Joe”, 3).
Attention Les personnes de type inconnu ne doivent être dans le résultat!
Astuce Utiliser le pattern matching
Réponse
val personnes = List(("Joe", "etu", 3), ("Lee", "etu", 4), ("Sara", "ens", 10), ("John", "ens", 5), ("Bill", "eng",20)) class Etu(nom:String, annee:Int){override def toString()="Etudiant en " +annee+" annee"} class Ens(nom:String, annee:Int){override def toString()="Enseignant avec " +annee+" annee d'experience"} val classes_personnes = personnes.map(x=> x match { case(a,"ens",b) =>new Ens(a,b); case(a, "etu", b) =>new Etu(a,b); case _=>None}).filter(_!=None)
Commencer par copier et décompresser dans votre espace de travail le fichier
/Infos/bd/spark/bdle/2015/data/wordcount.txt.bz2
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.
Lancer le spark-shell en mode local (voir Doc) en suivant les instructions fournies puis charger le fichier
wordcount.txt
au moyen de la méthode textFile() invoquée à partir de la variable context comme suit :
val data = sc.textFile("<le_chemin_dans_votre_espace_perso>/wordcount.txt")
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.
Expliquer l'expression
val x = List(("ALice",22), ("Bob",20), ("Charles",22)).toMap val x = List((22, "ALice"), (20, "Bob"), (22, "Charles")).toMap
Expliquer la différence entre les expressions map et flatMap suivantes :
val x = List(1, 2, 3).map(x => List(x, 10*x, 100*x) 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)
val ys = Map("a" -> List(1 -> 11, 1 -> 111), "b" -> List(2 -> 222, 2 -> 22)).flatMap(_._1)
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.
val ys = Map("a" -> List(1 -> 11, 1 -> 111), "b" -> List(2 -> 222, 2 -> 22)).flatMap(_._2)
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.
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)
En considérant la variable pers de la question précédente, expliquer le résultat des instructions suivantes
et expliquer pourquoi les deux instructions suivantes sont erronées