Projection
Une projection est une modification du schéma d'une table, souvent en retirant des colonnes.
Données inutiles
Supposons que l'on veuille afficher le code postal des communes de France. On peut utiliser les données de la table localisation
et effectuer une sélection dessus.
communes_selectionnees = selection(localisation, lambda l : l["code_postal"][0] == "0" and l["code_postal"][1] == "1")
On se retrouve avec la table communes_selectionnees
suivante :
code_insee | nom | code_postal | latitude | longitude |
---|---|---|---|---|
01001 | L' Abergement-Clémenciat | 01400 | 46.153721024 | 4.925850148 |
01002 | L' Abergement-de-Varey | 01640 | 46.009605679 | 5.428087796 |
... | ... | ... | ... | ... |
Cette table comporte des données dont nous n'avons pas besoin : le code insée, la latitude et la longitude. Ces données en trop vont prendre de la place si on veut exporter la table en CSV. Elles rendent aussi la table plus difficile à lire pour un humain, par exemple affichée par le biais d'un tableur.
TODO (prof) ex calculer la place prise par les colonnes en trop ?
On aimerait avoir une table code_postal
comme suit :
nom | code_postal |
---|---|
L' Abergement-Clémenciat | 01400 |
L' Abergement-de-Varey | 01640 |
... | ... |
Algorithme
Pour projeter une table sur un nouveau schéma, on parcours chaque ligne de la table, et on crée une nouvelle table en ajoutant des lignes qui ne contiennent que les champs qui nous intéressent.
Par exemple, la simplification ci-dessus peut s'effectuer avec un des deux codes suivants:
code_postal = []
for l in localisation:
code_postal.append({"nom":l["nom"], "code_postal":l["code_postal"]})
# on omet le if puisqu'on séléctionne toutes les lignes
code_postal = [{"nom":l["nom"], "code_postal":l["code_postal"]} in l for l in localisation]
Quelques projections
Exercice 7
Implémentez la fonction code_insee
qui retourne une table, projection de la table localisation
sur le schéma (nom, code_insee)
.
Vous devriez obtenir la sortie suivante dans le programme correspondant :
| nom | code_insee |
| ------------------------ | ---------- |
| L' Abergement-Clémenciat | 01001 |
| L' Abergement-de-Varey | 01002 |
| Ambérieu-en-Bugey | 01004 |
| Ambérieux-en-Dombes | 01005 |
| Ambléon | 01006 |
| Ambronay | 01007 |
| Ambutrix | 01008 |
| Andert-et-Condon | 01009 |
| Anglefort | 01010 |
| Apremont | 01011 |
Le code est très similaire à celui de l'exemple précédent !
Exercice 8
Implémentez la fonction coordonnees
qui retourne une table, projection de la table localisation
sur le schéma (nom, code_insee)
.
Vous devriez obtenir la sortie suivante dans le programme correspondant :
| nom | latitude | longitude |
| ------------------------ | ------------ | ----------- |
| L' Abergement-Clémenciat | 46.153721024 | 4.925850148 |
| L' Abergement-de-Varey | 46.009605679 | 5.428087796 |
| Ambérieu-en-Bugey | 45.961048852 | 5.372275427 |
| Ambérieux-en-Dombes | 45.99616357 | 4.911967138 |
| Ambléon | 45.749886304 | 5.594584599 |
| Ambronay | 46.005690539 | 5.357748739 |
| Ambutrix | 45.936683389 | 5.332446671 |
| Andert-et-Condon | 45.787194157 | 5.657799976 |
| Anglefort | 45.909091467 | 5.795056945 |
| Apremont | 46.205877513 | 5.657980818 |
Le principe est toujours le même.
Code générique
De la même manière que pour la sélection, on veut écrire le moins de code possible. Seule la transformation ligne par ligne change d'une fonction à l'autre, le code qui permet de parcourir les lignes reste le même.
Exercice 9
Voici une fonction projection
.
def projection(t, transformation):
"""t une table
transformation, une fonction qui prends en entrée une ligne de la table t, et retourne une nouvelle ligne, projection de la ligne sur un nouveau schéma.
Retourne une table, projection de t sur le schéma de créé par la fonction transformation
>>> projection([{"a":1, "b":2}, {"a":3, "b":4}], lambda l : {"a":l["a"]})
[{"a":1}, {"a":3}]
"""
return [transformation(l) for l in t]
Utilisez-là pour réécrire la projection de l'exercice 8.
Encore plus simple (Difficile)
En fait, on peut faire encore plus simple : on voudrait juste pouvoir spécifier les colonnes à garder dans notre table, un peu comme ça :
Essayons d'écrire une telle fonction.
Exercice 10
Implémentez la fonction projection_ligne
:
def projection_ligne(ligne, schema):
"""Ligne une ligne d'une table sous forme d'un dictionnaire
schema une list de chaines, des noms de colonnes présentes dans le schéma de ligne
Retourne une nouvelle ligne ne comportant que les colonnes de schema
>>> projection_ligne({"a":1, "b":2, "c":3}, ["a", "c"])
{"a":1, "c":3}
"""
Puis utilisez là pour implémenter la fonction projection
:
def projection(t, schema):
"""t une table,
schema une list de chaines, noms de colonnes présentes dans le schéma de t
Retourne une table, projection de t sur schema
>>> projection([{"a":1, "b":2, "c":3}, {"a":3, "b":4, "c":4}], ["a", "c"])
[{"a":1, "c":3}, {"a":3, "c":4}]
"""
Dans le programme correspondant, vous devriez avoir la sortie suivante :
Pour créer une nouvelle ligne projetée sur un schéma, utilisez une boucle for pour itérer sur chaque élément du schéma :)
def projection_ligne(ligne, schema):
"""Ligne une ligne d'une table sous forme d'un dictionnaire
schema une list de chaines, des noms de colonnes présentes dans le schéma de ligne
Retourne une nouvelle ligne ne comportant que les colonnes de schema
>>> projection_ligne({"a":1, "b":2, "c":3}, ["a", "c"])
{"a":1, "c":3}
"""
#vous devez implémenter cette fonction
nouvelle_ligne = {}
for c in schema:
nouvelle_ligne[c] = ligne[c]
return nouvelle_ligne
def projection(t, schema):
"""t une table,
schema une list de chaines, noms de colonnes présentes dans le schéma de t
Retourne une table, projection de t sur schema
>>> projection([{"a":1, "b":2, "c":3}, {"a":3, "b":4, "c":4}], ["a", "c"])
[{"a":1, "c":3}, {"a":3, "c":4}]
"""
#vous devez implémenter cette fonction
proj = []
for l in t:
proj.append(projection_ligne(l, schema))
return proj