Projet de session

Transcription

Projet de session
Projet de session
Cluster Hadoop & Administration / MapReduce & Hive
Auteurs: Charles Brisson, Mario Nadon, Yadong Wang
AEC Spécialiste Intelligence d’affaires & Big Data - Automne 2015
Collège Bois-de-Boulogne
Introduction
Le client, M. Brisson, est un promoteur immobilier spécialisé dans la
construction/rénovation de locaux afin de les rendre attrayants pour des
commerçants à la recherche des meilleurs endroits pour s’installer.
Grâce à son approche « big data », le promoteur cible des endroits
précis pour l’établissement, par exemple, de restaurants d’un type ou
d’un autre (fast food, familial, exotique, etc.) - en utilisant les données
récupérées dans d’autres villes, en établissant des modèles et
prédictions.
L’approche est complexe et dépasse largement l’étendue du présent
travail. Celui-ci demeure toutefois un élément important de la démarche.
Caractéristiques des données utilisées (Dataset)
Les données utilisées proviennent de YELP « The Challenge Dataset »
• 2.2 millions d’évaluations et 591,000 commentaires par 552,000 utilisateurs pour 77,000 entreprises
•
•
•
•
566,000 caractéristiques d’entreprises, ex., heures d’ouverture, stationnement disponible, ambiance.
Réseau social de 552,000 utilisateurs pour un total de 3.5 millions d’arrêtes de graphe social.
Enregistrements (check-in) agrégés à travers le temps pour chacune des 77,000 entreprises
200,000 images des entreprises
Villes :
• Angleterre: Edinburgh
• Allemagne: Karlsruhe
• Canada: Montréal, Waterloo
• États-Unis: Pittsburgh, Charlotte, Urbana-Champaign, Phoenix, Las Vegas, Madison
Source: https://www.yelp.com/dataset_challenge
Caractéristiques des données utilisées (Dataset)
Le dataset est composé de 6 tables:
business
{
'type': 'business',
'business_id': (encrypted business id),
'name': (business name),
'neighborhoods': [(hood names)],
'full_address': (localized address),
'city': (city),
'state': (state),
'latitude': latitude,
'longitude': longitude,
'stars': (star rating, rounded to half-stars),
'review_count': review count,
'categories': [(localized category names)]
'open': True / False (corresponds to closed, not business hours),
'hours': {
(day_of_week): {
'open': (HH:MM), ]
'close': (HH:MM)
},
...
},
'attributes': {
(attribute_name): (attribute_value),
...
},
(les données que nous avons utilisées sont identifiées en bleu)
tip
{
review
{
'type': 'review',
'business_id': (encrypted business id),
'user_id': (encrypted user id),
'stars': (star rating, rounded to half-stars),
'text': (review text),
'date': (date, formatted like '2012-03-14'),
'votes': {(vote type): (count)},
'type': 'tip',
'text': (tip text),
'business_id': (encrypted business id),
'user_id': (encrypted user id),
'date': (date, formatted like '2012-03-14'),
'likes': (count),
}
}
user
{
'type': 'user',
'user_id': (encrypted user id),
'name': (first name),
'review_count': (review count),
'average_stars': (floating point average, like 4.31),
'votes': {(vote type): (count)},
'friends': [(friend user_ids)],
'elite': [(years_elite)],
'yelping_since': (date, formatted like '2012-03'),
'compliments': {
(compliment_type): (num_compliments_of_this_type),
...
},
'fans': (num_fans),
}
}
photos (from the photos auxiliary file)
This file is formatted as a JSON list of objects.
[
{
"photo_id": (encrypted photo id),
"business_id" : (encrypted business id),
"caption" : (the photo caption, if any),
"label" : (the category the photo belongs to, if any)
},
{...}
]
check-in
{
'type': 'checkin',
'business_id': (encrypted business id),
'checkin_info': {
'0-0': (number of checkins from 00:00 to 01:00 on all Sundays),
'1-0': (number of checkins from 01:00 to 02:00 on all Sundays),
...
'14-4': (number of checkins from 14:00 to 15:00 on all Thursdays),
...
'23-6': (number of checkins from 23:00 to 00:00 on all Saturdays)
}, # if there was no checkin for a hour-day block it will not be in the dict
}
Note: Les autres données nous serviront assurément pour de futurs travaux de formation
Le besoin d’affaires
Classer les entreprises avec MapReduce Java :
• selon le score1
• selon quartile
• par ville
• par catégorie
Question à laquelle on pourra répondre avec HIVE :
Quelle est le nombre moyen d’évaluations par Ville et Nombre d’étoiles?
1 : Score:
Le score est une formule mise en place seulement dans un but pédagogique et ne représente aucunement une façon
de bien classer les entreprises.
Formule utilisé pour le calcul du score = ( ([stars] * 10) * 1,000,000 ) + nombre d’évaluations [nb_review]
notes: * 10 pour éliminer les décimales
* 1,000,000 pour ne pas perdre le poids du champs [stars]
Caractéristiques du Cluster Hadoop
Système
Namenode
Datanode #1
Datanode #2
Mémoire
9 Gb ram
2.5 Gb ram
2.5 Gb ram
OS Linux
6.4
Caractéristiques
Outils
6.4
6.4
5.4.3
Diagramme
Yelp
HDFS
HIVE
Développement Hive
Question
Quelle est le nombre moyen d’évaluations par Ville et nombre d’étoiles
pour la Ville de Montréal (par exemple)?
Créer la table
Create table yelpBusiness (json STRING);
Charger les données du dataset
Load data inpath '/user/root/testtp/inputyelp/business.json'
into table yelpBusiness;
Requête pour obtenir le nombre moyen d’évaluations
Select
get_json_object(yelpBusiness.json,'$.city')
as city,
get_json_object(yelpBusiness.json,'$.stars')
as stars,
avg(get_json_object(yelpBusiness.json,'$.review_count')) as nbreviews
From
yelpBusiness
Where get_json_object(yelpBusiness.json,'$.city') = 'Montréal'
Group by get_json_object(yelpBusiness.json,'$.city'),
get_json_object(yelpBusiness.json,'$.stars')
Order by city, stars;
Développement Map/Reduce (java)
Besoin
Pour chaque ville / catégorie nous voulons classer les entreprises en ordre de meilleures nombres d’étoiles
et du plus grand nombre d’évaluations et leur déterminer dans quel Quartille elles se trouvent.
Input
Mapper
Reducer
Dataset en format JSON
Explose les catégories et calcule le score
Trie par score en ordre descendant et calcul quartille
city: « Montréal »,
categories:[Restaurants,Brasserie
]
stars: 3.5
reviews: 1025
businessname: « Les 3
brasseurs »
city: « Montréal »,
categories: [Restaurants,Tapas]
stars: 4.5
reviews: 120
businessname: »Tapas 24 »
Mapper:
Clé: Montréal;Restaurants
Valeur: score:350001025;3.5;1025;Les 3 Brasseurs
Clé: Montréal;Brasserie
Valeur: score:350001025;3.5;1025;Les 3 Brasseurs
Clé: Montréal;Restaurants;1
Valeur: 1 de 4;Q1;45000120;4.5;120;Tapas 24
Clé: Montréal;Restaurants;2
Valeur: 2 de 4;Q2;35001025;3.5;1025;Les 3 Brasseurs
Clé: Montréal;Brasserie;1
Valeur: 1 de 89;Q1;35001025;3.5;1025;Les 3 Brasseurs
Clé: Montréal;Restaurants
Valeur: score:45000120;4.5;120;Tapas 24
Clé: Montréal;Tapas
Valeur: score:45000120;4.5;120;Tapas 24
Clé: Montréal;Tapas;1
Valeur: 1 de 23;Q1;45000120;4.5;120;Tapas 24
Développement MapReduce (java)
Code Mapper:
public static class YelpRankMapper extends Mapper<Object, Text, Text, Text>{
public static final String fieldSep
public static final char stringDelim
public static final String newLine
public static final String comma
private Text keyText = new Text();
private Text valueText = new Text();
= ";";
= '"';
= "\n";
= ",";
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
// Lire les informations qui sont en format JSON
JsonReader reader = Json.createReader(new StringReader(value.toString()));
JsonObject yelpBusiness = reader.readObject();
reader.close();
// Récupérer le VILLE
String ville = yelpBusiness.getString("city");
if (!ville.isEmpty()) {
// Récupérer les informations de la Business
double nbStars
= yelpBusiness.getJsonNumber("stars").doubleValue();
int nbReviews
= yelpBusiness.getInt("review_count");
String businessName
= yelpBusiness.getString("name") .replace(newLine, comma);
String businessAdresse
= yelpBusiness.getString("full_address") .replace(newLine, comma);
String state
= yelpBusiness.getString("state") .replace(newLine, comma);
JsonNumber latitude
= yelpBusiness.getJsonNumber("latitude");
JsonNumber longitude
= yelpBusiness.getJsonNumber("longitude");
// Calculer le score
int score = ((int)(nbStars*10) * 1000000) + nbReviews;
// Bâtir la VALEUR du Tuple
valueText.set(Integer.toString(score)
nbStars
String.valueOf(nbReviews)
stringDelim + businessName + stringDelim
stringDelim + businessAdresse + stringDelim
stringDelim + state
+ stringDelim
latitude.toString()
longitude.toString());
+ fieldSep
+ fieldSep
+ fieldSep
+ fieldSep
+ fieldSep
+ fieldSep
+ fieldSep
+
+
+
+
+
+
+
// Récupérer les catégories de la business et traiter toutes les catégories
JsonArray categoriesArray = yelpBusiness.getJsonArray("categories");
for (JsonValue categorie : categoriesArray)
{ keyText.set(stringDelim + ville
+ stringDelim + fieldSep
+ categorie.toString());
context.write(keyText, valueText);
}
}}}
Développement MapReduce (java)
Code Reducer:
public static class YelpRankReducer extends Reducer<Text,Text,Text, Text>
{ private Text newKey = new Text();
private Text newValue = new Text();
public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
// Récupérer les VALEURS (Iterable) et les ajouter dans une structure List (triable)
List<String> businessList = new ArrayList<String>();
for (Text business : values) { businessList.add(business.toString()); }
// Trier la liste des VALEURS par SCORE en ordre descendant
Collections.sort(businessList, new Comparator<String>() {
public int compare(String s1, String s2)
{ StringTokenizer tokenS1 = new StringTokenizer(s1,";");
StringTokenizer tokenS2 = new StringTokenizer(s2,";");
int scoreS1 = Integer.parseInt(tokenS1.nextToken());
int scoreS2 = Integer.parseInt(tokenS2.nextToken());
return new Integer(scoreS2).compareTo(new Integer(scoreS1));
}});
// Caluler les limites des Quartiles
int nbBusiness = businessList.size();
int q1 = (int) Math.round(nbBusiness * 25 / 100);
int q2 = (int) Math.round(nbBusiness * 50 / 100);
int q3 = (int) Math.round(nbBusiness * 75 / 100);
String quartille;
// Pour toutes les business de la CLÉ (VILLE / CATÉGORIE)
for (int rang = 0; rang < nbBusiness; rang++)
{ // Déterminer le quartile
if
(rang <= q1) {quartile = "Q1";}
else if (rang <= q2) {quartile = "Q2";}
else if (rang <= q3) {quartile = "Q3";}
else
{quartile = "Q4";};
// Nouvelle CLE et VALEUR
newKey.set (key.toString() + fieldSep + (rang + 1));
newValue.set (fieldSep + stringDelim + (rang + 1) + " de " + nbBusiness + stringDelim +
fieldSep + stringDelim + quartille
+ stringDelim +
fieldSep + businessList.get(rang));
context.write(newKey , newValue);
}
}}
Difficultés rencontrées
Installation du cluster
• Communication entre les machines:
- Cartes réseaux (static, dhcp, nat, mac address)
- SSH (Keygen)
- Adresse IP, IPV6 (selinux)
- Configuration dans les fichiers pour namenode, datanode, host, hostname, localhost
- Network time protocol (NTP)
• Manque de RAM de la machine virtuelle utilisée
MapReduce
• Traiter le dataset sous format JSON
• Trier les données par score en ordre descendant
• Se rendre compte qu’il ne faut pas de Combiner pour la logique désiré
Hive
• Traiter le dataset sous format JSON
• Permission pour écriture dans les répertoires
Compétences acquises
Techniques
• Certaine habitude à installer des clusters Cloudera Manager (l’équipe a monté plus de
50 clusters en un mois)
• Déploiement réussi de taches MapReduce en Java
• Utilisation réussie de HIVE pour obtenir d’autres résultats
Conceptuelles et affaires
• Compréhension de l’ampleur du travail pour l’installation et le déploiement d’une
solution big data
Conclusion
Développeur
• Prêt pour le prochain projet!
Client
• Prêt à confier la prochaine étape à l’équipe!