Cours 3 - 17/10/2016

Transcription

Cours 3 - 17/10/2016
Hadoop / Big Data
Benjamin Renaut <[email protected]>
MBDS
2016 - 2017
9
Apache Pig
Présentation
9-1
●
●
●
●
Pig est un autre outil associé à Hadoop – au même titre que Sqoop.
Son but est de permettre de développer facilement et rapidement des
programmes map/reduce, sans avoir à développer en Java. Sa syntaxe se
rapproche du langage SQL et il est notamment utile pour les
programmeurs peu familiers avec Java ou qui souhaitent simplement
expérimenter avec la méthodologie map/reduce ou Hadoop en général
rapidement.
A l'origine (2006) un projet développé en interne au département
R&D de Yahoo, il est depuis 2007 un projet de la fondation
Apache.
Le langage de programmation de Pig est appelé Pig Latin.
Utilisation
9-2
●
Pig fonctionne comme un interpréteur de scripts – exactement comme Python.
Il suffit d'écrire un script Pig, de l'exécuter avec l'interpréteur (le programme
en ligne de commande pig), et il exécutera le script dans l'environnement
Hadoop. Pour exécuter un script Pig Latin sur Hadoop, on utilisera la
commande:
pig -x mapreduce fichier.pig
●
Il est également possible de l'exécuter en mode local. Dans ce mode, Pig
ignore complètement Hadoop et HDFS: il chargera ses fichiers depuis le
disque local, et stockera ses résultats au même endroit. Pour ce faire, on
utilise la commande:
pig -x local fichier.pig
Utilisation
9-3
●
Enfin – et toujours comme Python – il peut également s'utiliser en mode
interactif, où on tape ses instructions Pig Latin dans une invite de commande.
Pour ce faire, il suffit de le lancer avec la commande:
pig
Dans ce mode, il détectera automatiquement si oui ou non Hadoop est présent
et se placera dans le mode d'exécution qui correspond (on peut également
forcer le mode à utiliser, toujours avec l'option -x).
Par exemple:
pig -x local
ou encore:
pig -x mapreduce
Fonctionnement
9-4
●
●
Pig constitue une couche d'abstraction au dessus de Hadoop. Un
script écrit en Pig Latin est converti en une ou plusieurs tâches
map/reduce exécutées sur Hadoop.
Tout programme Pig est constitué a minima des étapes suivantes:
●
●
●
●
Chargement des données (LOAD).
Transformations/traitements sur les données.
Sauvegarde des résultats (DUMP/STORE).
Ces trois étapes sont toutes parallélisées par le biais de tâches
map/reduce.
Syntaxe
9-5
●
Chaque ligne d'un script PIG doit être terminée par un point-virgule ';'.
●
Il est possible de commenter le code avec la syntaxe multi-lignes:
/* Ceci est
un commentaire sur
plusieurs lignes. */
(issue du C)
… ou avec la syntaxe sur une ligne:
LOAD...
-- Ceci est un commentaire sur une ligne.
(issue de SQL)
Types
9-6
●
Pig supporte les types de données usuels suivants:
●
●
●
●
●
●
●
int – un entier sur 32 bits.
long – un entier sur 64 bits.
float – un flottant sur 32 bits.
double – un flottant sur 64 bits.
chararray – une chaîne de caractères UTF-8.
bytearray – une chaîne binaire.
boolean – un booléen (true/false).
Types
9-7
●
Pig supporte également les trois types complexes suivants:
●
●
●
tuple: une série de données. Par exemple:
(12, John, 5.3)
bag: un ensemble de tuples. Par exemple:
{(12, John, 5.3), (8), (3.5, TRUE, Bob, 42)}
map: une série de couples (clef;valeur). Par exemple:
[qui#22, le#3]
(ici avec deux couples clef;valeur)
Au sein d'un type map, chaque clef doit être unique.
Types
9-8
●
●
Un tuple peut tout à fait contenir d'autres tuples, ou encore des bags,
ou autres types simples et complexes.
Par exemple:
({(1, 2), (3, John)}, 3, [qui#23])
Ce tuple contient trois membres:
●
●
●
Un bag contenant lui-même trois tuples.
Un entier de valeur 3.
Un map contenant un seul couple (clef;valeur).
Chargement des données
9-9
●
●
●
Les données d'entrée d'un programme pig seront typiquement
chargées au sein d'un bag.
Pour charger des données depuis le système de fichier (HDFS en
mode mapreduce ou système de fichiers local en mode local), on
utilise la commande LOAD.
Synopsis:
LOAD 'source' [USING fonction] [AS schemas]
… où source est le nom du/des fichier(s) ou répertoire(s), fonction le
nom d'une fonction d'importation, et schemas un descriptif du format
des données importées.
Chargement des données
9-10
●
Les paramètres fonction et schemas sont optionnels.
●
Le paramètre source peut être:
●
●
●
Un fichier unique, par exemple « poeme.txt ».
Un répertoire (chargera tous les fichiers du répertoire), par exemple
« data_input ».
Une expression complexe au même format que celles comprises par
Hadoop/HDFS, par exemple:
●
« data_input/input-1-* »
Chargement des données
9-11
●
●
●
C'est le paramètre fonction qui permet d'indiquer la manière de
charger/segmenter les données d'une manière cohérente.
Le paramètre désigne une classe Java en charge d'interpréter les
données depuis le système de fichier et d'en sortir des types Pig
cohérents (tuples / bag / map).
Pig offre une série de fonctions déjà disponibles pour simplifier la vie
du programmeur. La plus couramment utilisée est la fonction
PigStorage. Sa syntaxe:
PigStorage("SEPARATEUR")
Chargement des données - PigStorage
9-12
●
●
●
PigStorage va charger les données textuelles d'entrée sous la forme
d'un bag de tuples: un pour chaque ligne des données.
Le séparateur indique, pour chaque ligne, le caractère unique de
séparation entre les différents champs des membre de chaque tuple.
Imaginons qu'on ait le fichier d'entrée « etudiants.txt » suivant:
ADAM|Guillaume|15
ADAM|Guillaume|15
BERCHANE|Rachid|18
BERCHANE|Rachid|18
BOULLAIRE|Alexandre|16
BOULLAIRE|Alexandre|16
BOYER|Raphael|17
BOYER|Raphael|17
CHAMPOUSSIN|Luca|14
CHAMPOUSSIN|Luca|14
CODA|Stephen|15
CODA|Stephen|15
Chargement des données - PigStorage
9-13
●
Si on exécute la ligne:
A = LOAD 'etudiants.txt' USING PigStorage('|');
… alors A contiendra le bag de tuples suivant:
(ADAM,Guillaume,15)
(BERCHANE,Rachid,18)
(BOULLAIRE,Alexandre,16)
(BOYER,Raphael,17)
(CHAMPOUSSIN,Luca,14)
(CODA,Stephen,15)
(avec trois champs texte, texte et entier par tuple)
Chargement des données - PigStorage
9-14
●
Si en revanche, on utilise:
A = LOAD 'etudiants.txt' USING PigStorage(',');
… alors A contiendra le bag de tuples suivant:
("ADAM|Guillaume|15")
("BERCHANE|Rachid|18")
("BOULLAIRE|Alexandre|16")
("BOYER|Raphael|17")
("CHAMPOUSSIN|Luca|14")
("CODA|Stephen|15")
(avec un seul et unique champs texte par tuple)
Chargement des données - PigStorage
9-15
●
●
●
La fonction de chargement PigStorage permet ainsi facilement de
charger un fichier textuel avec des délimiteurs.
Comme indiqué précédemment, le paramètre fonction est optionnel. Si
on n'indique pas de fonction, alors la fonction utilisée est PigStorage,
avec un séparateur correspondant à une tabulation.
Ainsi, imaginons que notre fichier d'entrée « etudiants.txt » ait le
format suivant:
ADAM[TAB]Guillaume[TAB]15
(où [TAB] dénote une
tabulation)
ADAM[TAB]Guillaume[TAB]15
BERCHANE[TAB]Rachid[TAB]18
BERCHANE[TAB]Rachid[TAB]18
BOULLAIRE[TAB]Alexandre[TAB]16
BOULLAIRE[TAB]Alexandre[TAB]16
BOYER[TAB]Raphael[TAB]17
BOYER[TAB]Raphael[TAB]17
CHAMPOUSSIN[TAB]Luca[TAB]14
CHAMPOUSSIN[TAB]Luca[TAB]14
CODA[TAB]Stephen[TAB]15
CODA[TAB]Stephen[TAB]15
Chargement des données - PigStorage
9-16
●
Si on exécute:
A = LOAD 'etudiants.txt';
… alors A contiendra le bag de tuples suivant:
(ADAM,Guillaume,15)
(BERCHANE,Rachid,18)
(BOULLAIRE,Alexandre,16)
(BOYER,Raphael,17)
(CHAMPOUSSIN,Luca,14)
(CODA,Stephen,15)
(avec trois champs texte, texte et entier par tuple)
Chargement des données
9-17
●
Il existe d'autres fonctions « standard » de chargement mises à
disposition par Pig, parmi lesquelles:
●
●
●
●
TextLoader: aucun paramètre, charge simplement chaque ligne de
texte comme un tuple avec un seul champs – la ligne elle-même.
JsonLoader: permet de charger un fichier JSON de manière
structurée. Un argument, indiquant le format du JSON à charger.
AvroLoader: permet de charger un fichier binaire AVRO – un des
formats supportés par Sqoop ! Un argument également.
… et d'autres …
Il est également possible de développer sa propre fonction de
chargement (sous la forme d'une classe Java compilée en .jar).
Chargement des données - Schémas
9-18
●
●
Par défaut, une fois le bag des données d'entrée chargé, on pourra se
référer à différents membres de chaque tuple en utilisant la syntaxe:
$0, $1, $2, etc.
… pour référencer le premier, second, troisième, etc. membre de
chaque tuple.
Par exemple, si on a chargé les données suivantes dans A:
(ADAM,Guillaume,15)
(BERCHANE,Rachid,18)
(BOULLAIRE,Alexandre,16)
… alors l'expression « A.$2 » designera la note (15, 18, …); de même,
l'expression « A.$0 » désignera le nom de famille (ADAM, …).
Chargement des données - Schémas
9-19
●
Le troisième argument optionnel de LOAD, schemas, permet de:
●
●
●
●
Donner un nom aux champs des tuples chargés.
Leur attribuer un type de données explicite.
Spécifier un type de chargement différent (autre qu'un simple bag de
tuples).
La syntaxe:
(NOM1:TYPE1, NOM2:TYPE2,NOM3:TYPE3…)
Le type est optionnel.
Chargement des données - Schémas
9-20
●
Par exemple, avec les données d'entrée « in.txt »:
ADAM|Guillaume|15
ADAM|Guillaume|15
BERCHANE|Rachid|18
BERCHANE|Rachid|18
BOULLAIRE|Alexandre|16
BOULLAIRE|Alexandre|16
BOYER|Raphael|17
BOYER|Raphael|17
CHAMPOUSSIN|Luca|14
CHAMPOUSSIN|Luca|14
CODA|Stephen|15
CODA|Stephen|15
●
Si on fait:
A = LOAD 'in.txt' USING PigStorage('|') AS (nom, prenom, note);
Alors on pourra se référer par exemple à la note plus loin par le biais
de la syntaxe « A.note ».
Chargement des données - Schémas
9-21
●
On pourrait également forcer le type de la note par le biais de:
A = LOAD 'in.txt' USING PigStorage('|') AS (nom, prenom, note:float);
… et si on exécute par exemple:
A = LOAD 'in.txt' USING PigStorage('|') AS (nom, prenom:int, note:float);
… alors le champs « prenom » de notre fichier d'entrée ne sera pas chargé
(valeur vide), puisque Pig tente de l'interpréter comme un type int et qu'il s'agit
d'une chaîne de caractères dans les données d'entrée.
Chargement des données - Schémas
9-22
●
Un exemple plus complexe. Données d'entrée « in.txt »:
(ADAM,Guillaume)|(15,15,12)
(ADAM,Guillaume)|(15,15,12)
(BERCHANE,Rachid)|(18,15,20)
(BERCHANE,Rachid)|(18,15,20)
(BOULLAIRE,Alexandre)|(16,11,18)
(BOULLAIRE,Alexandre)|(16,11,18)
●
Si on fait:
A = LOAD 'in.txt' Using PigStorage('|') AS \
(infos:tuple(nom,prenom),notes:tuple(n1,n2,n3));
Alors les données obtenues auront la forme:
((ADAM,Guillaume),(15,15,12))
((ADAM,Guillaume),(15,15,12))
((BERCHANE,Rachid),(18,15,20))
((BERCHANE,Rachid),(18,15,20))
((BOULLAIRE,Alexandre),(16,11,18))
((BOULLAIRE,Alexandre),(16,11,18))
Chargement des données - Schémas
9-23
●
●
●
●
Dans l'exemple précédent, en délimitant au sein du fichier source les données
sous la forme de deux champs « tuples », on a pu indiquer à PigStorage lors
du chargement qu'on souhaitait qu'ils soient chargés sous cette forme.
On pourra alors se référer par exemple à « A.infos.prenom », ou encore
« A.notes.n1 », etc.
Il faut évidemment que la fonction de chargement supporte ce type de
schémas plus complexes.
Il est recommander de toujours explicitement définir un schéma de
chargement avec tous les types et noms définis; ceci d'une part pour la
lisibilité, d'autre part pour assurer la cohérence dans le type des données
d'entrée.
Commande DESCRIBE
9-24
●
●
On peut à tout moment obtenir (au sein du shell interactif) une description de
la composition d'un container par le biais de la commande describe.
Par exemple:
A = LOAD 'in.txt' Using PigStorage('|') AS \
(infos:tuple(nom,prenom),notes:tuple(n1,n2,n3));
DESCRIBE A;
●
Donnera à l'écran:
A: {infos: (nom: bytearray,prenom: bytearray),notes: (n1: bytearray,n2:
bytearray,n3: bytearray)}
… c'est notamment utile lors de la rédaction/test d'un programme Pig.
Sauvegarde de données – DUMP
9-25
●
Deux commandes principales existent pour extraire des données depuis Pig.
●
La première est la commande DUMP. Syntaxe:
DUMP <nom du container>;
Par exemple:
DUMP A;
●
Elle ne sauvegarde pas les données sur le système de fichier mais se contente
de les afficher à l'écran. Elle est utile lors de l'utilisation de Pig en mode
interactif. Les sorties affichées plus haut (résultats de LOAD) au sein de ce
cours sont directement issues de la commande DUMP.
Sauvegarde de données – STORE
9-26
●
●
La seconde est la commande STORE, qui sauve véritablement des données au
sein du système de fichier (HDFS ou local selon le mode).
Sa syntaxe:
STORE alias INTO 'repertoire' [USING fonction];
●
●
alias représente le container à sauvegarder (par exemple « A »).
Comme les données sont issues d'une tâche map/reduce, elles seront
stockées sous la forme de plusieurs fichiers « part-r-* ». En conséquence,
repertoire indique le répertoire dans lequel on souhaite que la série de fichiers
résultants de la tâche/du stockage soient écrits.
Sauvegarde de données – STORE
9-27
●
●
●
Là aussi, on peut spécifier une fonction indiquant la manière de formater les
données. Les mêmes fonctions que celles vu précédemment sont utilisables
(PigStorage, JsonStorage, AvroStorage… mais pas par exemple TextLoader).
fonction est ici aussi optionnel; là aussi, si on ne précise pas de fonction, c'est
PigStorage('\t') qui est utilisée.
STORE ne supporte pas de schéma; à la place, si un schéma est nécessaire
(par exemple pour du json), c'est généralement le paramètre de la fonction de
stockage qui est utilisé.
Dans le cas du JSON, par défaut, Pig utilisera les noms de colonnes
directement issues du schéma de l'alias concerné; il stocke par ailleurs le
schéma sous la forme d'un fichier .pig_schema dans le répertoire. Si on
recharge les données avec un JsonStorage, le schéma sera automatiquement
importé par la fonction.
Sauvegarde de données – Exemples
9-28
●
Fichier d'entrée « in.txt »:
●
Chargé dans l'alias A avec:
ADAM|Guillaume|15
ADAM|Guillaume|15
BERCHANE|Rachid|18
BERCHANE|Rachid|18
BOULLAIRE|Alexandre|16
BOULLAIRE|Alexandre|16
BOYER|Raphael|17
BOYER|Raphael|17
A = LOAD 'in.txt' USING PigStorage('|') \
AS (nom:chararray, prenom:chararray, note:float);
Sortie de:
(ADAM,Guillaume,15)
(ADAM,Guillaume,15)
DUMP A; (BERCHANE,Rachid,18)
(BERCHANE,Rachid,18)
(BOULLAIRE,Alexandre,16)
(BOULLAIRE,Alexandre,16)
(BOYER,Raphael,17)
(BOYER,Raphael,17)
●
DESCRIBE A;
A:
A: {nom:chararray,
{nom:chararray,
prenom:chararray,note:float}
prenom:chararray,note:float}
Sauvegarde de données – Exemples
9-29
●
Si on utilise:
STORE A INTO 'results' USING PigStorage(',');
Un répertoire « results/ » sera créé, contenant ici un fichier unique part-r-00000
avec pour contenu:
ADAM,Guillaume,15
ADAM,Guillaume,15
BERCHANE,Rachid,18
BERCHANE,Rachid,18
BOULLAIRE,Alexandre,16
BOULLAIRE,Alexandre,16
BOYER,Raphael,17
BOYER,Raphael,17
●
Si les données avaient été plus conséquentes, on aurait pu avoir de nombreux
fichiers part-r-00000, part-r-00001, etc.
Sauvegarde de données – Exemples
9-30
●
Si on utilise:
STORE A INTO 'results' USING JsonStorage();
Un répertoire « results/ » sera créé, contenant ici un fichier unique part-r-00000
avec pour contenu:
{"nom":"ADAM","prenom":"Guillaume","note":15.0}
{"nom":"ADAM","prenom":"Guillaume","note":15.0}
{"nom":"BERCHANE","prenom":"Rachid","note":18.0}
{"nom":"BERCHANE","prenom":"Rachid","note":18.0}
{"nom":"BOULLAIRE","prenom":"Alexandre","note":16.0}
{"nom":"BOULLAIRE","prenom":"Alexandre","note":16.0}
{"nom":"BOYER","prenom":"Raphael","note":17.0}
{"nom":"BOYER","prenom":"Raphael","note":17.0}
●
Par ailleurs, Pig a également créé un fichier .pig_schema; si on recharge plus
tard les données avec JsonLoader (le pendant « chargement » de
JsonStorage), le schéma sera automatiquement déduit.
Autres commandes utiles – cat
9-31
●
●
●
Le shell interactif de Pig offre quelques commandes pratiques pour le
développeur.
L'une de ces commandes est cat: comme la commande « cat » Unix, elle
affiche le contenu d'un fichier.
En revanche, dans le cadre de Pig, elle est étendue au fonctionnement
Hadoop; si on fait:
cat [REPERTOIRE]
… la commande affichera automatiquement le contenu concaténé de tous les
fichiers du répertoire (utile pour vérifier un ensemble de données d'entrée/de
sortie).
Autres commandes utiles
9-32
●
Autres commandes uniquement disponibles en mode interactif:
●
help: affiche une aide.
●
sh [COMMANDE]: exécute une commande shell.
●
exec [FICHIER.PIG]: exécute le script pig indiqué.
●
history: affiche un historique de toutes les commandes utilisées.
●
ls, mkdir, rm, etc. : équivalentes à leur pendant Unix.
… et d'autres (consulter la documentation).
Traitement des données
9-33
●
●
●
On a vu jusqu'ici comment charger des données depuis le système de
fichiers local/HDFS au sein d'un alias Pig, et comment sauvegarder un
alias au sein du système de fichier ou simplement l'afficher à l'écran
en mode interfactif (DUMP).
On va maintenant voir comment manipuler les données, c'est à dire
effectuer un traitement sur nos données d'entrée.
Toutes les fonctions et opérateurs de Pig ne sont pas décrites ici;
seule une sélection large mais non exhaustive est décrite; se référer à
la documentation de Pig pour plus d'informations.
Opérateur FILTER
9-34
●
●
L'opérateur FILTER permet de filtrer les éléments d'un alias selon une ou
plusieurs conditions.
Syntaxe:
DEST = FILTER SOURCE BY EXPRESSION;
Il s'agit grosso modo d'un équivalent au WHERE de SQL.
On pourra ainsi faire par exemple:
B = FILTER A BY note > 10;
Ou encore:
B = FILTER A BY note > 10 AND ((annee_naissance+age)<2014);
Opérateur ORDER
9-35
●
●
L'opérateur ORDER permet de trier les éléments selon une condition. C'est
l'équivalent du ORDER BY de SQL.
Syntaxe:
DEST = ORDER SOURCE BY FIELD [ASC|DESC];
On pourra ainsi faire par exemple:
B = ORDER A BY note DESC;
Ou encore:
B = ORDER A BY note DESC, nom ASC;
Opérateur GROUP
9-36
●
L'opérateur GROUP permet de grouper les tuples selon un champs.
●
Il génère des tuples constitués de deux champs:
●
●
●
Le premier, nommé « group », est un champs simple, contenant la valeur
unique du champs utilisé lors de l'appel à GROUP.
Le second, qui porte le nom de l'alias sur lequel a été appliqué GROUP, est
un bag qui contient une série de tuples: tous ceux pour lesquels la valeur en
question a été rencontrée.
Syntaxe:
DEST = GROUP SOURCE BY FIELD;
Opérateur GROUP
9-37
●
Par exemple:
●
On exécute: B = GROUP A BY note;
ADAM,Guillaume,15
ADAM,Guillaume,15
BERCHANE,Rachid,15
BERCHANE,Rachid,15
BOULLAIRE,Alexandre,16
BOULLAIRE,Alexandre,16
DUMP A;
(ADAM,Guillaume,15)
(ADAM,Guillaume,15)
(BERCHANE,Rachid,15)
(BERCHANE,Rachid,15)
(BOULLAIRE,Alexandre,16)
(BOULLAIRE,Alexandre,16)
Et on obtient:
DUMP B;
(15,{(BERCHANE,Rachid,15),(ADAM,Guillaume,15)})
(15,{(BERCHANE,Rachid,15),(ADAM,Guillaume,15)})
(16,{(BOULLAIRE,Alexandre,16)})
(16,{(BOULLAIRE,Alexandre,16)})
DESCRIBE B ;
B:
B: {group:float,A:{(nom:chararray,prenom:chararray,note:float)}}
{group:float,A:{(nom:chararray,prenom:chararray,note:float)}}
Opérateur FOREACH… GENERATE
9-38
●
●
L'opérateur FOREACH … GENERATE permet d'itérer chacun des
tuples du bag de l'alias concerné, et de générer d'autres tuples pour
chacun d'entre eux.
Syntaxe:
DEST = FOREACH SOURCE GENERATE EXPRESSION;
●
●
Au sein de l'expression, on peut générer des bags, des tuples, des
maps, etc. et appeler des fonctions à la volée.
On peut également nommer des champs générés de cette manière au
sein de l'appel à GENERATE.
Opérateur FOREACH… GENERATE
9-39
DUMP A;
(ADAM,Guillaume,15)
(ADAM,Guillaume,15)
(BERCHANE,Rachid,15)
(BERCHANE,Rachid,15)
(BOULLAIRE,Alexandre,16)
(BOULLAIRE,Alexandre,16)
●
Par exemple:
●
On exécute: B = FOREACH A GENERATE note;
ADAM,Guillaume,15
ADAM,Guillaume,15
BERCHANE,Rachid,15
BERCHANE,Rachid,15
BOULLAIRE,Alexandre,16
BOULLAIRE,Alexandre,16
Et on obtient:
DUMP B;
(15)
(15)
(15)
(15)
(16)
(16)
DESCRIBE B;
B:
B: {note:
{note: float}
float}
Opérateur FOREACH… GENERATE
9-40
DUMP A;
(ADAM,Guillaume,15)
(ADAM,Guillaume,15)
(BERCHANE,Rachid,15)
(BERCHANE,Rachid,15)
(BOULLAIRE,Alexandre,16)
(BOULLAIRE,Alexandre,16)
●
Par exemple:
●
On exécute: B = FOREACH A GENERATE (nom,prenom) AS infos, note;
Et on obtient:
ADAM,Guillaume,15
ADAM,Guillaume,15
BERCHANE,Rachid,15
BERCHANE,Rachid,15
BOULLAIRE,Alexandre,16
BOULLAIRE,Alexandre,16
DUMP B;
((ADAM,Guillaume),15.0)
((ADAM,Guillaume),15.0)
((BERCHANE,Rachid),15.0)
((BERCHANE,Rachid),15.0)
((BOULLAIRE,Alexandre),16.0)
((BOULLAIRE,Alexandre),16.0)
DESCRIBE B;
B:
B: {infos:
{infos: (nom:
(nom: chararray,prenom:
chararray,prenom: chararray),note:
chararray),note: float}
float}
Opérateur FOREACH… GENERATE
9-40
●
Il est également possible d'imbriquer des traitement plus complexe au sein de
cet opérateur, par exemple pour effectuer des traitements sur les tuples d'un
bag. La syntaxe:
DEST = FOREACH SOURCE {
OPERATION_1;
OPERATION_2;
… etc …
};
●
Imaginons par exemple qu'on ait des données sous la forme:
A:
A: {infos:
{infos: (nom:
(nom: bytearray,prenom:
bytearray,prenom: bytearray),
bytearray), \\
notes:
notes: (n1:
(n1: bytearray,n2:
bytearray,n2: bytearray,n3:
bytearray,n3: bytearray)}
bytearray)}
Opérateur FOREACH… GENERATE
9-40
… on pourrait alors faire:
B = FOREACH A {
C = (notes.n1+notes.n2+notes.n3)/3.0;
GENERATE infos,C as moyenne;
};
Pour générer la moyenne des notes tout en conservant les noms & prénoms
des élèves contenus dans l'alias A.
Opérateur FLATTEN
9-41
●
●
L'opérateur FLATTEN permet de « déplier » un ensemble tuples et bags pour
les transformer en de simples tuples.
Par exemple, si on a une liste de tuples dans un alias B au format suivant:
(15,{(BERCHANE,Rachid,15),(ADAM,Guillaume,15)})
(15,{(BERCHANE,Rachid,15),(ADAM,Guillaume,15)})
(16,{(BOULLAIRE,Alexandre,16)})
(16,{(BOULLAIRE,Alexandre,16)})
B:
B: {group:float,A:{(nom:chararray,prenom:chararray,note:float)}}
{group:float,A:{(nom:chararray,prenom:chararray,note:float)}}
●
Et qu'on exécute:
C = FOREACH B GENERATE FLATTEN(A);
Opérateur FLATTEN
9-42
●
… on obtiendra:
(BERCHANE,Rachid,15.0)
(BERCHANE,Rachid,15.0)
(ADAM,Guillaume,15.0)
(ADAM,Guillaume,15.0)
(BOULLAIRE,Alexandre,16.0)
(BOULLAIRE,Alexandre,16.0)
Autres opérateurs
9-43
●
Il existe divers autres opérateurs utiles dans Pig:
●
●
●
●
●
LIMIT: récupérer les N premiers tuples d'un alias.
SPLIT: générer plusieurs alias à partir d'un alias; selon des expressions
conditionnelles.
CROSS: mélanger les tuples de deux alias.
DISTINCT: éliminer les tuples identiques au sein d'un alias.
SAMPLE: obtenir une sous-sélection aléatoire de tuples depuis un alias (par
exemple, récupérer 10% des tuples d'un alias de manière aléatoire).
… et d'autres (se référer à la documentation officielle de Pig pour plus
d'informations).
Fonctions – Chaînes de caractères
9-44
●
●
Pig propose, en standard, de nombreuses fonctions pour la manipulation des
types chaîne de caractère.
Quelques exemples:
●
TOKENIZE: découpe les différents mots d'une ligne. Génère un bag
contenant un tuple pour chacun des mots.
Par exemple, A:
("exemple
("exemple de
de phrase")
phrase")
("autre
("autre exemple")
exemple")
B = FOREACH A GENERATE TOKENIZE($0);
●
DUMP B:
({("exemple"),
({("exemple"), ("de"),
("de"), ("phrase")})
("phrase")})
({("autre"),("exemple")})
({("autre"),("exemple")})
Fonctions – Chaînes de caractères
9-45
●
SIZE: renvoie la taille d'une chaîne de caractères.
●
SUBSTRING: permet de récupérer une sous-chaîne de caractères.
●
STRSPLIT: permet de découper une chaîne de caractères à partir d'une
expression régulière.
●
LOWER: converti une chaîne en minuscules.
●
REPLACE: remplacement dans une chaîne (supporte là aussi les regexp).
… et d'autres (là aussi, consulter la documentation).
Fonctions – Mathématiques
9-46
●
●
De même, Pig propose un certain nombre de fonction pour la manipulation des
nombres.
Quelques exemples:
●
ABS: renvoie la valeur absolue d'un nombre.
●
COS/SIN/TAN: cosinus/sinus/tangente.
●
RANDOM: génère un flottant aléatoire entre 0 et 1.
●
ROUND: génère l'arrondi d'un nombre.
… et d'autres (même remarque).
Fonctions – Autres
9-47
●
Pig propose enfin quelques autres fonctions utiles:
●
●
●
●
COUNT(bag): renvoie le nombre de tuples contenus au sein d'un bag.
CONCAT(f1, f2, f3, …): concatène une série de champs d'un tuple. Supporte
la concaténation des nombres (leur représentation textuelle).
SUBSTRACT(b1, b2): renvoie un bag constitué des éléments présents dans
le bag b1 mais absents du bag b2.
SUM(bag): renvoie la somme des éléments numériques contenus au sein
d'un bag.
… et d'autres (même remarques).
Exemple – Compteur de mots, version Pig
9-48
●
On reprend l'exemple précédent du compteur d'occurences de mots.
●
Les données d'entrée, au sein d'un fichier poeme.txt:
Celui
Celui qui
qui croyait
croyait au
au ciel
ciel
Celui
Celui qui
qui ny
ny croyait
croyait pas
pas
Tous
Tous deux
deux adoraient
adoraient la
la belle
belle
Prisonniere
Prisonniere des
des soldats
soldats
●
On commence par charger les données:
A = LOAD 'poeme.txt' USING TextLoader AS ligne:chararray;
Exemple – Compteur de mots, version Pig
9-49
●
Aprés chargement:
DUMP A:
(Celui
(Celui qui
qui croyait
croyait au
au ciel)
ciel)
(Celui
(Celui qui
qui ny
ny croyait
croyait pas)
pas)
(Tous
(Tous deux
deux adoraient
adoraient la
la belle)
belle)
(Prisonniere
(Prisonniere des
des soldats)
soldats)
●
DESCRIBE A:
A:
A: {ligne:
{ligne: chararray}
chararray}
Exemple – Compteur de mots, version Pig
9-50
●
On exécute ensuite:
B = FOREACH A GENERATE TOKENIZE(LOWER(ligne)) AS mots;
… ce qui aura pour effet, pour chaque ligne, de la convertir en minuscules,
puis de générer un bag de tuples: un pour chacun des mots de la ligne en
minuscules (fonction TOKENIZE).
●
On donne également un nom au bag en question: « mots ».
Exemple – Compteur de mots, version Pig
9-51
●
Aprés exécution:
DUMP B:
({(celui),(qui),(croyait),(au),(ciel)})
({(celui),(qui),(croyait),(au),(ciel)})
({(celui),(qui),(ny),(croyait),(pas)})
({(celui),(qui),(ny),(croyait),(pas)})
({(tous),(deux),(adoraient),(la),(belle)})
({(tous),(deux),(adoraient),(la),(belle)})
({(prisonniere),(des),(soldats)})
({(prisonniere),(des),(soldats)})
●
DESCRIBE B:
B:
B: {mots:
{mots: {tuple_of_tokens:
{tuple_of_tokens: (token:
(token: chararray)}}
chararray)}}
Exemple – Compteur de mots, version Pig
9-52
●
On exécute ensuite:
C = FOREACH B GENERATE FLATTEN(mots) AS mot;
… ce qui aura pour effet, pour chaque tuple, de « déplier » le bag contenant
chacun des mots et de générer pour chacun des tuples contenu dans ce bag
un tuple indépendant.
●
On donne également un nom à ces tuples désormais indépendants: « mot ».
Exemple – Compteur de mots, version Pig
9-53
●
Aprés exécution:
DUMP C:
(celui)
(celui)
(qui)
(qui)
(croyait)
(croyait)
(au)
(au)
(ciel)
(ciel)
(celui)
(celui)
(qui)
(qui)
(ny)
(ny)
(croyait)
(croyait)
(pas)
(pas)
(tous)
(tous)
...
...
DESCRIBE C:
C:
C: {mot:
{mot: chararray}
chararray}
Exemple – Compteur de mots, version Pig
9-54
●
On exécute ensuite:
D = GROUP C BY mot;
… ce qui aura pour effet de regrouper les tuples par mot commun.
●
On rappelle que l'opérateur GROUP va générer un tuple constitué d'un premier
membre « group » qui contient le mot unique en question, et d'un second
membre « C » de type bag qui contient tous les tuples correspondant à cette
valeur.
Exemple – Compteur de mots, version Pig
9-55
●
Aprés exécution:
DUMP D:
(au,{(au)})
(au,{(au)})
(la,{(la)})
(la,{(la)})
(ny,{(ny)})
(ny,{(ny)})
(qui,{(qui),(qui)})
(qui,{(qui),(qui)})
(tous,{(tous)})
(tous,{(tous)})
(belle,{(belle)})
(belle,{(belle)})
(celui,{(celui),(celui)})
(celui,{(celui),(celui)})
(croyait,{(croyait),(croyait)})
(croyait,{(croyait),(croyait)})
...
...
DESCRIBE D:
D:
D: {group:
{group: chararray,C:
chararray,C: {(mot:
{(mot: chararray)}}
chararray)}}
Exemple – Compteur de mots, version Pig
9-56
●
On exécute enfin:
E = FOREACH D GENERATE group AS mot, COUNT(C) as occurrences;
… ce qui aura pour effet de parcourir l'alias D et, pour chaque tuple, de
récupérer le premier membre « group » et le nombre de tuples du second
membre de type bag « C ».
●
On génère un nouveau tuple à partir de ces deux informations: on donne au
premier champs le nom « mot », et au second le nom « occurences ».
Exemple – Compteur de mots, version Pig
9-57
●
Aprés exécution:
DUMP E:
(au,1)
(au,1)
(la,1)
(la,1)
(qui,2)
(qui,2)
(belle,1)
(belle,1)
(celui,2)
(celui,2)
(croyait,2)
(croyait,2)
(soldats,1)
(soldats,1)
(prisonniere,1)
(prisonniere,1)
...
...
DESCRIBE D:
E:
E: {mot:
{mot: chararray,occurrences:
chararray,occurrences: long}
long}
Exemple – Compteur de mots, version Pig
9-58
●
Il reste enfin à sauvegarder les données:
STORE E INTO '/results';
●
●
Puisqu'on ne précise pas de fonction de stockage, Pig utilisera par défaut
PigStorage('\'): il devrait écrire chacun des tuple sur une ligne textuelle, en
séparant chacun des champs par une tabulation.
La méthode générera un répertoire /results (sur HDFS ou le système de
fichiers local), qui contiendra une série de fichier part-r-XXXXX, contenant les
différents tuples de E.
Exemple – Compteur de mots, version Pig
9-59
●
Contenu du fichier /results/part-r-00000:
au
11
au
la
11
la
ny
11
ny
des
11
des
pas
11
pas
qui
22
qui
ciel
11
ciel
deux
11
deux
tous
11
tous
belle
11
belle
celui
22
celui
croyait
croyait 22
soldats
soldats 11
adoraient
adoraient
prisonniere
prisonniere
11
11
Etendre Pig
9-60
●
●
●
●
●
On l'a vu, Pig propose de nombreuses fonctions en standard.
En revanche, dans beaucoup de cas complexes, on a besoin d'effectuer des
traitements qui sont impossible avec les seules fonctions standard de Pig.
Pour pallier à ce manque, Pig implémente le concept de User Defined
Functions. Il s'agit d'un mécanisme permettant d'étendre Pig.
Le fonctionnement est simple: il suffit de développer la fonction dont on a
besoin en Java (en implémentant certaines interfaces Pig), et une fois
compilées, elles deviennent disponibles au sein de tout programme Pig
chargeant le .jar correspondant.
Il est également possible de développer ces fonctions en Python / autres
langages (par un mécanisme similaire à Streaming pour Hadoop).
User Defined Functions
9-61
●
Pour charger un .jar additionnel depuis un programme Pig, on utilise la
commande REGISTER. Par exemple:
REGISTER udf.jar;
(sans guillemets autour du nom du .jar)
●
On peut ensuite simplement invoquer la fonction à partir de son classpath
directement depuis le langage Pig Latin. Par exemple:
B = FOREACH A GENERATE org.mbds.udf.UpperCase(nom);
(en supposant qu'une classe org.mbds.udf.UpperCase existe au sein du fichier
udf.jar d'exemple)
User Defined Functions - Exemple
9-62
●
●
●
Un exemple: on ne peut pas sans fonction additionnelle implémenter un
programme Pig pour résoudre le problème des anagrammes vu
précédemment. En effet, il n'y a pas de moyen depuis les fonctions standard
Pig de générer la clef (les lettres du mot, classées par ordre alphabétique).
En conséquence, on imagine qu'on va rédiger une fonction Pig additionnelle
pour implémenter cette opération: elle prendra en entrée une chaîne de
caractères, et fournira en sortie la même chaîne avec les lettres ordonnées
alphabétiquement.
On décide d'appeler cette classe SortLetters au sein d'un package:
org.mbds.hadoop.pigudfs
User Defined Functions - Exemple
9-63
User Defined Functions - Exemple
9-64
●
Une fois ce code compilé dans un .jar, par exemple un fichier mbds_udfs.jar,
on pourra faire au sein de tout programme Pig:
REGISTER mbds_udfs.jar;
●
Et si on utilise:
org.mbds.hadoop.pigudfs.SortLetter(field)
… alors on recevra en valeur de retour la valeur de la chaîne field, avec les
lettres triées alphabétiquement.
Conclusion
9-65
●
●
●
On a vu que Pig constituait un outil puissant et accessible pour la
rédaction et l'exécution de programmes map/reduce via Hadoop.
Il est par ailleurs infiniment extensible via le mécanisme des User
Defined Functions.
Si beaucoup d'opérateurs et de fonctions « standards » de Pig ont été
abordés, il y en a beaucoup d'autres: pour approfondir le sujet, se
référer à la documentation officielle de Pig (+ guide de lectures
additionnelles du cours).