Documents de cours 2020-2021 - FX Jollois
Nous allons réaliser le processus ETL sur le datamart vu en cours. Vous devez d’abord télécharger la base Comptoir2000
au format SQLite
. Pour rappel, voici le schéma du datamart souhaité.
Note : installer les packages DBI
et RSQLite
dans R avant l’exécution du code qui suit.
Il est possible de se connecter à une BD sous R, en grande partie grâce au package DBI
, et à d’autres packages spécifiques pour chaque moteur (SQLite
, Oracle
, SQL Server
, …). Par exemple, pour se connecter à la base Comptoir2000
au format SQLite
, il est possible de procéder ainsi :
library(DBI)
# Connexion à la BD
cpt = dbConnect(RSQLite::SQLite(), "Comptoir2000.sqlite")
# Liste des tables
dbListTables(cpt)
# Déconnexion de la BD
dbDisconnect(cpt)
Pour information, lorsqu’on se connecte à un fichier inexistant, celui-ci est créée automatiquement. De plus, cette connexion est en mode écriture, ce qui fait que toute modification de la base est sauvegardée.
Fonction | Rôle |
---|---|
dbConnect() |
Connexion à la BD si fichier inexistant, création automatique |
dbDisconnect() |
Déconnexion de la BD à faire à la fin du script |
dbListTables() |
Liste des tables de la BD |
dbListFields() |
Liste des champs d’une table spécifique |
dbReadTable() |
Lecture d’une table équivalent à un SELECT * |
dbWriteTable() |
Ecriture d’un data-frame dans une table équivalent à un CREATE TABLE ... AS |
dbCreateTable() |
idem mais avec la possibilité de créer une table vide en listant les champs |
dbRemoveTable() |
Suppression d’une table équivalent à un DROP ... |
sqlCreateTable() |
Création d’une table (avec liste des champs) |
dbGetQuery() |
Exécution directe d’une requête SQL de type SELECT |
dbExecute() |
Exécution directe d’une requête SQL (au format chaîne de caractères) pour tout ce qui est autre que SELECT |
dbSendStatement() |
Exécution différée d’une requête SQL (au format chaîne elle-aussi) à finir avec dbClearResult() |
Voici un code permettant les étapes suivantes :
library(DBI)
cpt = dbConnect(RSQLite::SQLite(), "Comptoir2000.sqlite")
dm = dbConnect(RSQLite::SQLite(), "datamart.sqlite")
dbListTables(cpt)
Messager
de 2 façons
dbReadTable(cpt, "Messager")
dbGetQuery(cpt, "SELECT * FROM Messager;")
aaaaa
dans le data-mart
a
, avec 2 variables
x
: 1, 2, …, 5y
: “a”, “b”, …, “e”aaaaa
a = data.frame(x = 1:5, y = letters[1:5])
dbWriteTable(dm, "aaaaa", a)
dbListTables(dm)
dbReadTable(dm, "aaaaa")
Messager
INSERT INTO ...
dbExecute(cpt, "INSERT INTO Messager (NoMess, NomMess) VALUES (5, 'La Poste')")
dbReadTable(cpt, "Messager")
B
avec le formalisme SQL, et 2 attributs
cle
: clé primaireref
: qui fait référence à l’attribut x
de A
cle
: 1, 2, …, 10ref
: une valeur entre 1 et 5 aléatoiredbExecute(dm, "
CREATE TABLE B (
cle INT NOT NULL PRIMARY KEY,
ref INT REFERENCES A (x)
);
")
for (i in 1:10) {
cle = i
ref = sample(1:5)[1]
requete = paste0("INSERT INTO B VALUES (", cle, ", ", ref, ")")
print(requete)
dbExecute(dm, requete)
}
dbReadTable(dm, "B")
dbGetQuery(dm, "
SELECT x, COUNT(*) AS Nb
FROM aaaaa, B
WHERE aaaaa.x = B.ref
GROUP BY x
ORDER BY 2 DESC
")
dbDisconnect(cpt)
dbDisconnect(dm)
Dans une démarche d’intégration de données (ETL), le process classique est le suivant :
Ici, notre SI opérationnel est la base Comptoir2000
, et au format SQLite
donc. Pour le SI décisionnel, nous allons aussi utiliser un fichier au format SQLite
, que l’on nommera datamart.sqlite
par exemple. La partie Extract sera l’importation des tables du SI utilisées dans des data-frames R. Ensuite, pour la partie Transform, nous devons créer les data-frames tels qu’ils devront être dans le SID. Enfin, la partie Load se fera à base d’ajout de valeur dans les tables du SID, par exemple avec la commande dbExecute()
et des INSERT INTO
(avec des boucles). Mais il existe aussi la fonction dbAppendTable()
qui peut être utile dans ce cas.
Pour créer le data-mart, contenant les 2 processus vus en cours, vous devez donc faire les étapes suivantes :
A l’heure actuelle, il existe un package permettant la création et la manipulation de cube au sens OLAP dans R, c’est hypercube
. Il existe aussi le package data.cube
, mais qui ne semble plus maintenu. Et nous verrons d’autres outils intéressant sur shiny
dans la suite du cours, en particulier rpivotTable
.
R peut stocker des données dans des tableaux à $n$ dimensions, grâce au type array
ce qui s’apparente à des hypercubes donc. La librairie hypercube
surcharge ce type permettant quelques opérations OLAP classiques.
Nous allons utiliser les données CA
, du premier TP, mais au format R cette fois-ci. Elles sont disponibles sur ce lien (fichier .RData
). Une fois chargée dans l’espace de travail de R, vous devriez trouver la table ca_tout
, résultat de la jointure entre toutes les tables du data-mart.
Il est donc possible de créer un cube dans la commande generateCube()
:
groupe
et mois
)library(hypercube)
cub = generateCube(
ca_tout,
columns = list(
groupe = c("departement", "groupe", "sous_groupe"),
mois = c("annee", "mois_numero"),
provenance = "provenance"
),
valueColumn = "ca"
)
L’affichage du cube obtenu n’est pas très intéressant directement.
Pour ne sélectionner qu’une partie du cube, on utilise la fonction agg.selection()
comme ci-dessous :
add.selection(
cub,
criteria = list(
annee = 2004,
departement = "Meubles",
groupe = "Meubles intérieurs",
sous_groupe = as.character(
subset(groupe, departement == "Meubles" & groupe == "Meubles intérieurs")$sous_groupe
)
)
)
Ici, nous avons tous les CA pour 2004, pour le département “Meubles”, et plus particulièrement le groupe “Meubles intérieurs”, pour tous les sous-groupes et toutes les provenances. On voit ici que la notion de hiérarchie n’est pas correctement gérée, puisque nous devons spécifié les sous-groupes à garder (alors que cela devrait être automatique finalement).
Il faut noter qu’on peut ajouter des éléments dans notre sélection, sans avoir besoin de reprendre le cube initial. Toutes les données sont conservées dans le cube résultat. De plus, on peut supprimer une sélection avec la fonction remove.selection()
.
Il est aussi possible de résumer le cube selon un ou plusieurs dimensions, en utilisant une fonction d’aggrégat (généralement sum()
, mais cela peut aussi être min()
, max()
, mean()
, …). Pour cela, nous utilisons la fonction add.aggregation()
:
add.aggregation(
cub,
dimensions = c("departement", "mois_numero"),
fun = "sum"
)
Dans l’exemple ci-dessus, nous avons les CA par département et par mois (les deux années cumulées).
Comme pour la sélection, il est possible d’ajouter un agrégat sur le résultat, ou de “revenir en arrière” et donc retrouver les données initiales ou désagrégées, avec la fonction remove.aggregation()
.