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

Ceci est une ancienne révision du document !


[TME II-1] Introduction à Scala

Remarque générale : Le cours ne peut être self-contained –> consulter la documentation en ligne de Spark.

Exercice 0

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.

Question 1

  • Définir la fonction maxEntiers qui retourne le plus grand des entiers d'une liste fournie en entrée.
  • Définir la fonction scEntiers qui calcule la somme des carrés des entiers d'une liste fournie en entrée.
  • Définir la fonction moyEntiers qui calcule la moyenne des entiers d'une liste fournie en entrée.

Tester au fur et à mesure ces fonctions sur listeEntiers construit comme suit :

val listeEntiers = List.range(1,11)

Réponse

def maxEntiers(in: List[Int])= in.reduce((a,b)=>(if(a>b)a else b))
def scEntiers(in: List[Int])= in.map(e=>e*e).reduce((a,b)=>(a+b))
def moyEntiers(in: List[Int])={val p = in.map(e=>(1,e)).reduce((a,b)=>(a._1+b._1, a._2+b._2)); p._2/p._1}

Question 2

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

  • Calculer pour l'année 2009 le maximum de ses températures.
  • Idem pour la moyenne de ces température.

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)

Question 3

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 :

  • notes contenant les éléments de la forme (userID, movieID, rating, timestamp) et dont le type est (Int, String, Int, Int) ;
  • films contenant les éléments de la forme (movieID, title, genre) et dont le type est (Int, String, String).

Réponse

val notes = melange.map(_.split(",")).filter(_(0).toInt>=1000).map(x=>(x(0).toInt,x(1), x(2).toInt, x(3).toInt))
val films = melange.map(_.split(",")).filter(_(0).toInt<=100).map(x=>(x(0).toInt,x(1), x(2)))

Question 4

Soit une liste personnes contenant des tuples ayant trois attributs décrivant des personnes :

  • le premier attribut est le nom de la personne;
  • le second attribut est le type de la personne : etudiant (etu), enseignant (ens) ou inconnu (nan);
  • la troisième attribut est l'année d'inscription (s'il s'agit d'un étudiant) ou les années d'ancienneté pour

les enseignants.

val personnes = List(("Joe", "etu", 3), ("Lee", "etu", 4), ("Sara", "ens", 10), ("John", "ens", 5), ("Bill", "nan",20))
  1. Définir une classe Etu(nom:String, annee:Int) et une classe Ens(nom:String, annee:Int)
  2. Transformer personnes en une liste d'objets de la classe Etu ou Ens encapsulant les information des tuples

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)

Exercice 1 : Spark

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.

Manipulation de RDDs Simples

  1. Extraire le 4e champ de chaque élément de data et le convertir en type double
  2. Construire à partir de q1 une liste contenant les nombres compris strictement entre 1000 et 1300 puis convertir en type entier.
  3. 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.
  4. Construire une liste obtenue en divisant par 10 chaque élément de q33.
  5. 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.
  6. Construire une liste contenant les éléments de q2 qui sont multiples de 3 mais pas de 4. Utiliser impérativement q33 et q34.
  7. 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.
  8. 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.
  9. Retourner pour chaque élément distinct de q2 le nombre de fois qu’il apparaît dans cette liste.

Exercice subsidiaire

Conversion d'un objet List en un objet Map

Expliquer l'expression

val x = List(("ALice",22), ("Bob",20), ("Charles",22)).toMap
 
val x = List((22, "ALice"), (20, "Bob"), (22, "Charles")).toMap

Illustration du flatMap

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)

Composition de Map

En considérant la variable pers de la question précédente, expliquer le résultat des instructions suivantes

  • pers.flatMap(x⇒(x._2) map(y⇒y))
  • pers.flatMap(x⇒(x._2) map(y⇒x._1))
  • 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-scala.1507714568.txt.gz · Dernière modification: 11/10/2017 11:36 par amine