Objet : Exemple de spécification de la propriété "relationship"

Objectif

  • montrer sur un exemple réel comment spécifier les liens entre champs
  • identifier les apports que pourraient avoir un outil d'analyse de ces liens

Présentation de l'exemple

Il concerne le fichier IRVE des bornes de recharge (données utilisées : https://static.data.gouv.fr/resources/fichier-consolide-des-bornes-de-recharge-pour-vehicules-electriques/20220629-080611/consolidation-etalab-schema-irve-v-2.0.2-20220628.csv).

Le fichier IRVE contient une liste de stations de recharge avec notamment :

  • pour une station : un Id, un nom, une adresse et des coordonnées
  • pour chaque station plusieurs points de charge identifiés par un Id_pdc
  • un opérateur pour chaque station

Seules, quelques lignes et colonnes ont été extraits pour l'exemple (tableau ci-dessous pour 4 stations) :

nom_operateur id_station_itinerance nom_station adresse_station coordonneesXY id_pdc_itinerance
SEVDEC FRSEVP1SCH01 SCH01 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0101
SEVDEC FRSEVP1SCH03 SCH03 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0301
SEVDEC FRSEVP1SCH02 SCH02 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0201
Sodetrel FRS35PSD35711 RENNES - PLACE HONORE COMMEREUC 13 Place Honoré Commeurec 35000 Rennes [-1.679739, 48.108482] FRS35ESD357111
Sodetrel FRS35PSD35712 RENNES - PLACE HONORE COMMEREUC 13 Place Honoré Commeurec 35000 Rennes [-1.679739, 48.108482] FRS35ESD357112
Virta FRE10E30333 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445075, 41.995246] FRE10E30333
Virta FRE10E20923 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445073, 41.995246] FRE10E20923
Virta FRE10P20922 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445072, 41.995246] FRE10P20922
Virta FRE10P20921 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445071, 41.995246] FRE10P20921
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202603
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202602
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202601

On constate notamment quelques erreurs :

  • l'id et le nom de la station opérée par SEVDEC est différent pour chaque point de charge,
  • l'id de la station opérée par Sodetrel est également différent pour chaque point de charge,
  • pour la station Virta les coordonnées et les id de la station sont également variables en fonction des points de charge

amélioration de la spécification

Les erreurs constatées pourraient être évitées en définissant les règles de dépendance entre colonnes conformément au modèle de données associé au tableau. On distingue trois entités :

  • l'opérateur qui peut opérer plusieurs stations (un seul champ : nom_operateur)
  • les stations qui contiennent plusieurs points de charges (quatre champs : id_station_itinerance, nom_station, adresse_station, coordonnéesXY),
  • les points de charge (un seul champ : id_pdc_itinerance)

Ce modèle de données se traduit par les spécifications suivantes :

  • le champ opérateur est dérivé du champ id_station (relation 1-n)
  • le champ id_station est dérivé du champ id_pdc (relation 1-n)
  • les champs nom_station, adresse_station, coordonnéesXY sont couplés au champ id_station (relation 1-1)

Ceci se traduit en propriétés "relationship" sur chacun des champs :

« name »: « nom_operateur »
« relationship » : {
    « parent » : « id_station_itinerance »,
    « link » : « derived » 
},
« name »: « id_station_itinerance »
« relationship » : {
    « parent » : « id_pdc_itinerance »,
    « link » : « derived » 
},
« name »: « nom_station »
« relationship » : {
    « parent » : « id_station_itinerance »,
    « link » : « derived » 
},
« name »: « adresse_station »
« relationship » : {
    « parent » : « id_station_itinerance »,
    « link » : « derived » 
},
« name »: « coordonnéesXY »
« relationship » : {
    « parent » : « id_station_itinerance »,
    « link » : « derived » 
}

exemple d'outil de contrôle de la spécification

  • un fichier csv est rempli avec le tableau ci-dessus
  • un objet 'Ilist' est initialisé avec ce fichier
In [1]:
from pprint import pprint
import os
os.chdir('C:/Users/a179227/OneDrive - Alliance/perso Wx/ES standard/python ESstandard/ES')
from ilist3 import Ilist3

chemin = 'C:/Users/a179227/OneDrive - Alliance/perso Wx/ES standard/python ESstandard/validation/irve/'
file = chemin + 'IRVE_example.csv'

irve = Ilist3.from_csv(file, header=True, optcsv=None)
print('nombre de lignes : ', len(irve))
print('liste des champs : ')
pprint(irve.indexinfos(keys=['num', 'name'], base=True), width=120)
nombre de lignes :  12
liste des champs : 
[{'name': 'nom_operateur', 'num': 0},
 {'name': 'id_station_itinerance', 'num': 1},
 {'name': 'nom_station', 'num': 2},
 {'name': 'adresse_station', 'num': 3},
 {'name': 'coordonneesXY', 'num': 4},
 {'name': 'id_pdc_itinerance', 'num': 5}]

contrôle initial

Dans l'exemple retenu on a un opérateur par station, la relation entre operateur et station doit donc être 'couplé' plutôt que 'dérivé'. On constate qu'une seule relation est correcte (entre id_station et id_pdc).

In [2]:
operateur, id_station, nom_station, adresse, coord, id_pdc = irve.lindex
print('operateur est couplé avec id_station : ', id_station.iscoupled(operateur))
print('id_station est dérivé de id_pdc : ', id_station.isderived(id_pdc))
print('nom_station est couplé avec id_station : ', nom_station.iscoupled(id_station))
print('adresse_station est couplé avec id_station : ', adresse.iscoupled(id_station))
print('coordonneesXY est couplé avec id_station : ', coord.iscoupled(id_station))
operateur est couplé avec id_station :  False
id_station est dérivé de id_pdc :  True
nom_station est couplé avec id_station :  False
adresse_station est couplé avec id_station :  False
coordonneesXY est couplé avec id_station :  False

correction des données

Les corrections à apporter pour respecter la spécification pourraient être les suivantes :

  • champ id_station : FRSEVP1SCH (3 premiers), FRS35PSD35711 (2 suivants), FRE10E2092 (4 suivants)
  • champ nom_station : SCH (3 premiers)
  • champ coordonneesXY : [9.445071, 41.995246] du 6e au 8e

Le tableau corrigé serait donc :

nom_operateur id_station_itinerance nom_station adresse_station coordonneesXY id_pdc_itinerance
SEVDEC FRSEVP1SCH SCH 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0101
SEVDEC FRSEVP1SCH SCH 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0301
SEVDEC FRSEVP1SCH SCH 151 Rue d'Uelzen 76230 Bois-Guillaume [1.106329, 49.474202] FRSEVE1SCH0201
Sodetrel FRS35PSD35711 RENNES - PLACE HONORE COMMEREUC 13 Place Honoré Commeurec 35000 Rennes [-1.679739, 48.108482] FRS35ESD357111
Sodetrel FRS35PSD35711 RENNES - PLACE HONORE COMMEREUC 13 Place Honoré Commeurec 35000 Rennes [-1.679739, 48.108482] FRS35ESD357112
Virta FRE10E2092 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445071, 41.995246] FRE10E30333
Virta FRE10E2092 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445071, 41.995246] FRE10E20923
Virta FRE10P2092 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445071, 41.995246] FRE10P20922
Virta FRE10P2092 Camping Arinella Route de la mer, Brushetto - 20240 Ghisonaccia [9.445071, 41.995246] FRE10P20921
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202603
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202602
DEBELEC FRSGAP1M2026 M2026 2682 Boulevard François Xavier Fafeur 11000 Carcassonne [2.298185, 43.212574] FRSGAE1M202601
In [3]:
id_station.setvalue(0, 'FRSEVP1SCH')
id_station.setvalue(1, 'FRSEVP1SCH')
id_station.setvalue(2, 'FRSEVP1SCH')
id_station.setvalue(3, 'FRS35PSD35711')
id_station.setvalue(4, 'FRS35PSD35711')
id_station.setvalue(5, 'FRE10E2092')
id_station.setvalue(6, 'FRE10E2092')
id_station.setvalue(7, 'FRE10E2092')
id_station.setvalue(8, 'FRE10E2092')
nom_station.setvalue(0, 'SCH')
nom_station.setvalue(1, 'SCH')
nom_station.setvalue(2, 'SCH')
coord.setvalue(5, [9.445071, 41.995246])
coord.setvalue(6, [9.445071, 41.995246])
coord.setvalue(7, [9.445071, 41.995246])
irve.reindex()
Out[3]:
Ilist3[12, 6]

Nouveau contrôle

La vérification effectuée avec ces nouvelles données montre que la spécification serait alors respectée :

In [4]:
print('operateur est couplé avec id_station : ', id_station.iscoupled(operateur))
print('id_station est dérivé de id_pdc : ', id_station.isderived(id_pdc))
print('nom_station est couplé avec id_station : ', nom_station.iscoupled(id_station))
print('adresse_station est couplé avec id_station : ', adresse.iscoupled(id_station))
print('coordonneesXY est couplé avec id_station : ', coord.iscoupled(id_station))
operateur est couplé avec id_station :  True
id_station est dérivé de id_pdc :  True
nom_station est couplé avec id_station :  True
adresse_station est couplé avec id_station :  True
coordonneesXY est couplé avec id_station :  True