cours-2020-2021

Documents de cours 2020-2021 - FX Jollois

View the Project on GitHub fxjollois/cours-2020-2021

TP4 : Connexion R et MongoDB

Le but de ce TP est de voir l’utilisation des commandes MongoDB dans R.

Accès à MongoDB dans R

Nous allons utiliser la librairie mongolite. Pour l’utiliser (après l’avoir installée), on l’importe classiquement comme ci-dessous.

library(mongolite)

Connexion vers la collection

La première opération est de créer une connexion entre R et MongoDB en utilisant la fonction mongo(). Celle-ci prend en paramètre la base et la collection, plus si besoin l’adresse du serveur. S’elle n’y est pas, elle se connecte en local (ce qui est notre cas normalement).

m = mongo(
  collection = "restaurants", 
  db = "test")

Par le biais de l’objet ainsi créé (m), on a accès aux différentes fonctions que l’on a vu dans Mongo (précisemment count(), distinct(), find() et aggregate()).

Type des objets retournés

R ne gérant pas nativement les données JSON, les documents sont traduits, pour la librairie mongolite, en data.frame. Pour récupérer le premier document, nous utilisons la fonction find() de l’objet créé m.

d = m$find(limit = 1)
d
class(d)

Les objets address et grades sont particuliers, comme on peut le voir dans le JSON. Le premier est une liste, et le deuxième est un tableau. Voila leur classe en R.

class(d$address)
d$address
class(d$grades)
d$grades

Dénombrement

Il existe la fonction count() qui compte directement le nombre de document. Dans le cas où l’on veut compter les documents qui respectent une certaine condition, nous utilisons le paramètre query. Comme vous pouvez le voir dans les exemples ci-dessous, il est nécessaire de passer la requête en JSON, dans une chaîne de caractères.

m$count()
m$count(query = '{ "borough": "Brooklyn" }')
m$count(query = '{ "borough": "Brooklyn", "cuisine": "French" }')
m$count(query = '{ "borough": "Brooklyn", "cuisine": { "$in": ["French", "Italian"]} }')
m$count(
  query = '{ 
    "borough": "Brooklyn", 
    "cuisine": { "$in": ["French", "Italian"]}
  }'
)
m$count(
  query = '{ 
    "address.street": "Franklin Street"
  }'
)
m$count(
  query = '{ 
    "grades.score": 0
  }'
)
m$count(
  query = '{ 
    "grades.score": { "$lte": 5 }
  }'
)

Valeurs distinctes

Il existe la même fonction distinct(), avec les mêmes possibilités. On peut ainsi vouloir les valeurs distinctes présentes dans un sous-ensemble de documents (respectant une contrainte particulière). Voici quelques exemples :

m$distinct(key = "borough")
m$distinct(
  key = "cuisine",
  query = '{ "borough": "Brooklyn" }'
)
m$distinct(
  key = "grades.grade",
  query = '{ "borough": "Brooklyn" }'
)

Récupération de données avec find()

Cette fonction permet donc de récupérer tout ou partie des documents, selon éventuellement un critère de restriction (dans le paramètre query) et un critère de projection (dans le paramètre fields). Pour n’avoir que le premier document, on utilise le paramètre limit). Pour le tri, on utilise le paramètre sort. Voici quelques exemples :

m$find(query = '{ "name": "Shake Shack" }', 
       fields = '{ "address.street": 1, "borough": 1 }')
m$find(query = '{ "name": "Shake Shack" }', 
       fields = '{ "_id": 0, "address.street": 1, "borough": 1 }')
m$find(query = '{"borough": "Queens", "grades.score": { "$gte":  50}}',
       fields = '{"_id": 0, "name": 1, "grades.score": 1, "address.street": 1}',
       limit = 10)
m$find(query = '{"name": "Shake Shack", "borough": {"$in": ["Queens", "Brooklyn"]}}', 
       fields = '{"_id": 0, "address.street": 1, "borough": 1}')
m$find(query = '{"borough": "Queens", "grades.score": { "$gt":  50}}',
       fields = '{"_id": 0, "name": 1, "address.street": 1}',
       sort = '{"address.street": -1, "name": 1}')

Agrégats

Bien évidemment, on peut faire des calculs d’agrégats, avec la fonction aggregate(), prenant en paramètre donc le pipeline en chaîne de caractères. Voici encore quelques exemples :

m$aggregate(pipeline = '[
    {"$limit": 10 }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$sort": { "name": 1 }}
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$sort": { "name": 1 }},
    { "$match": { "borough": "Brooklyn" }}
]')
m$aggregate(pipeline = '[
    { "$match": { "borough": "Brooklyn" }},
    { "$limit": 10 },
    { "$sort": { "name": 1 }}
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$unwind": "$grades" }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$unwind": "$grades" },
    { "$match": { "grades.grade": "B" }}
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$match": { "grades.grade": "B" }},
    { "$unwind": "$grades" }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "name": 1, "borough": 1 } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "address": 0, "grades": 0 } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "name": 1, "borough": 1 , "street": "$address.street"} }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "name": 1, "borough": 1, "nb_grades": { "$size": "$grades" } } }
]')
m$aggregate(pipeline = '[
    { "$project": { "name": 1, "borough": 1, "nb_grades": { "$size": "$grades" } } },
    { "$sort": { "nb_grades": 1 }},
    { "$limit": 10 }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "name": 1, "borough": 1, "grade": { "$arrayElemAt": [ "$grades", 0 ]} } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { "nom": { "$toUpper": "$name" }, "borough": 1 } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$addFields": { "nb_grades": { "$size": "$grades" } } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$project": { 
        "nom": { "$toUpper": "$name" }, 
        "quartier": { "$substr": [ "$borough", 0, 3 ] } 
    } }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$addFields": { "quartier": { "$toUpper": { "$substr": [ "$borough", 0, 3 ] } } }},
    { "$project": { 
        "nom": { "$toUpper": "$name" }, 
        "quartier": { "$cond": { "if": { "$eq": ["$borough", "Bronx"] }, "then": "BRX", "else": "$quartier" } },
        "borough": 1
    } }
]')
m$aggregate(pipeline = '[
    {"$group": {"_id": "Total", "NbRestos": {"$sum": 1}}}
]')
m$aggregate(pipeline = '[
    {"$group": {"_id": "$borough", "NbRestos": {"$sum": 1}}}
]')
m$aggregate('[
    { "$match": { "borough": "Queens" }},
    { "$unwind": "$grades" },
    { "$group": { "_id": "null", "score": { "$avg": "$grades.score" }}}
]')
m$aggregate('[
    { "$unwind": "$grades" },
    { "$group": { "_id": "$borough", "score": { "$avg": "$grades.score" }}},
    { "$sort": { "score": -1 }}
]')
m$aggregate(pipeline = '[
    { "$project": { 
        "borough": 1, "street": "$address.street", 
        "eval": { "$arrayElemAt": [ "$grades", 0 ]} 
    } },
    { "$match": { "eval": { "$exists": true } } },
    { "$match": { "eval.score": { "$gte": 0 } } },
    { "$group": { 
        "_id": { "quartier": "$borough", "rue": "$street" }, 
        "score": { "$avg": "$eval.score" }
    }},
    { "$sort": { "score": 1 }},
    { "$limit": 10 }
]')
m$aggregate(pipeline = '[
    { "$limit": 10 },
    { "$unwind": "$grades" },
    { "$group": { 
        "_id": "$name", 
        "avec_addToSet": { "$addToSet": "$grades.grade" },
        "avec_push": { "$push": "$grades.grade" }
    }}
]')

A faire

Dans R

  1. Lister les informations du restaurant “Cafe Henri”
  2. Lister tous les restaurants de la chaîne “Bareburger” (rue, quartier)
  3. Lister les restaurants n’ayant pas de quartier connu (“Missing”)
  4. Lister les restaurants ayant eu un score de 0
  5. Lister les restaurants ayant eu un score entre 0 et 10 (inclus)
  6. Lister les restaurants qui ont le terme “Cafe” dans leur nom
  7. Lister les restaurants faisant de la cuisine de type “Pizza” dans “Brooklyn”
  8. Quelles sont les 10 plus grandes chaines de restaurants (nom identique) ?
  9. Lister par quartier le nombre de restaurants et le score moyen
  10. Donner le Top 5 et le Flop 5 des types de cuisine, en terme de nombre de restaurants
  11. Donner les dates de début et de fin des évaluations
  12. Quels sont les 10 restaurants (nom, quartier, addresse et score) avec le plus petit score moyen ?
  13. Quels sont les restaurants (nom, quartier et addresse) avec uniquement des grades “A” ?
  14. Compter le nombre d’évaluation par jour de la semaine