Ce document identifie les points de recharge du jeu de données IRVE présentant des problèmes d'intégrité (ex. une station associée à plusieurs coordonnées différentes, une coordonnée associée à plusieurs adresses).
Il complète l'analyse des données présentée dans une note spécifique.
(liens actifs sur jupyter Notebook ou Nbviewer)
Plusieurs fichiers liés aux défauts d'intégrité sont disponibles ici:
indic = indicateurs(chemin)
Le graphe ci-dessous présente l'évolution du nombre de points de recharge documentés dans le jeu de données avec une distinction entre les données présentant ou non des doublons (doublon : plusieurs point de recharge avec le même identifiant).
Le nombre de doublons était très important avant l'été 2023 (plus de 40%). Les travaux menés pendant l'été ont permis de ramener le nombre de doulons à 5%.
On note néanmoins une remontée depuis la fin 2023 qui a été réduite mais reste encore significative (10 %).
doublons(indic)
Le graphe ci-dessous présente l'évolution du nombre de points de recharge documentés dans le jeu de données avec une distinction entre les données présentant ou non des défauts d'intégrité.
Il est à noter que malgré la structure complexe du jeu de données IRVE, le taux de défaut a pu se maintenir à un niveau faible ( < 5%) en début d'année 2024.
Le nombre de points de recharge est passé de 40 000 en début d'année 2023 à 78 000 fin mars 2024 malgré une diminution pendant l'été 2023 (passage de 45 000 à moins de 30 000) résorbée ensuite.
Le mauvais niveau de qualité des données ajoutées après l'été a dégradé le niveau de qualité (le taux de défaut a atteint 15% en septembre). Ce problème est maintenant corrigé.
La forte augmentation en fin d'année 2023 (65 000 points de recharge) n'a pas augmenté le nombre de défauts et s'est accompagnée également d'une prise en compte d'une partie des défauts signalés. Cependant, les évolutions apportées en mars 2024 ont conduit à un doublement du nombre de défauts. Le taux de défaut est donc maintenant de plus de 6%.
Le taux de défaut pourrait être réduit de 80% en traitant les cinq écarts présentés dans la suite de ce Notebook.
Nota :
evolution(indic)
pdc valide : 72816 pdc defaut : 4823
Les défauts d'intégrité découlent du modèle de données associé au jeu de données :
date = defaut_integrite(indic)
icones rouge : stations avec un écart de relations entre entités (voir introduction)
icones orange : stations avec un défaut d'attributs (voir introduction)
Les libellés en fin de fenêtres contextuelles "champ 1 - champ 2 : False" signifient que le champ 2 est associé à plusieurs champ 1 en fonction des pdc (défaut d'intégrité).
Par exemple : "coordonneesXY - id_station_itinerance : False" signifie qu'il existe plusieurs pdc avec une même la valeur du champ "id_station_itinerance" mais plusieurs valeurs du champ "coordonnéesXY".
Nota :
defaut_carte(date, chemin).show('irve.html')
Sur les 4 823 points de charge en défaut, 3 920 défauts pourraient être éliminés en traitant les 5 points ci-dessous.
Les défauts identifiés concernent des stations utilisant un même identifiant (2 550 points de recharge).
Par exemple, la station 'FRPD1PCHAVDB' contient 28 points de recharge avec un nombre de points de rechage variable. Les informations indiquées pour les PdC (identifiant, nombre) de cet exemple montrent qu'il s'agit en réalité de 6 stations comportant chacune 3, 4, 8, 3, 8 et 2 PdC.
Toutes les stations en défaut sont situées dans des parkings et concernent visiblement des stations implantées sur plusieurs étages et regroupées sous un même identifiant (on devrait donc a minima identifier une station par étage).
450 points de charge TotalEnergie ont une documentation incohérente du champ "station_deux_roues" (voir l'analyse des données). Ceci concerne essentiellement les stations Belib de Paris (voir carte).
Les défauts (430 points de recharge) sont liés à une incohérence entre adresse et coordonnées (une même coordonnée a plusieurs adresses différentes). Ce défaut est lié soit à des erreurs de saisie, soit à des ajouts (ou remplacements) ultérieurs de stations à une même localisation.
Les stations Ionity en erreur ont un id_station_itinerance identique à l'id_pdc_itinerance ainsi qu'un ancien pdc de regroupement par station avec un nom d'enseigne différent (IONITY GMBH), ce qui se traduit par des adresses multiples pour une même coordonnée (voir exemple dans l'analyse des données)
La solution est donc de :
Par exemple, sur l'aire de Mornas [4.732409, 44.194832], on trouve 26 stations avec la même coordonnée, le même identifiant entre point de recharge et station et un nombre de PdC indiqué de 23 pour chaque station.
Les défauts au nombre de 290 sont liés à une incohérence entre adresse et coordonnées (une même coordonnée a plusieurs adresses différentes). Ce défaut est lié soit à des erreurs de saisie, soit à des ajouts (ou remplacements) ultérieurs de stations à une même localisation.
Les stations Izivia en erreur ont un id_station_itinerance identique à l'id_pdc_itinerance, ce qui se traduit par des adresses multiples pour une même coordonnée (voir exemple dans l'analyse des données).
Par exemple, sur un parking [-0.530598, 47.398424], on trouve 17 stations avec la même coordonnée, le même identifiant entre point de recharge et station et un nombre de PdC indiqué entre 1 et 3.
Les défauts identifiés concernent des stations utilisant un même identifiant (200 points de recharge).
Par exemple, la station 'FRADPP91479018' contient 82 points de recharge avec plusieurs adresses et un nombre de points de rechage variable. Cet exemple concerne des stations implantées sur plusieurs étages d'un parking (on devrait donc a minima identifier une station par étage).
import json
import ntv_pandas as npd
import pathlib
chemin = str(pathlib.Path(npd.__file__).parent.parent.parent/"Environmental-Sensing"/"python"/"Validation"/"irve"/"Analyse")
def indicateurs(chemin):
dic = {}
#with open('logfile.txt', 'r', encoding="utf-8") as f:
with open(chemin + '/' + 'logfile.txt', 'r', encoding="utf-8") as f:
for line in f:
log = json.loads(line)
dic[log['date_irve']] = log
return list(tuple(zip(*sorted(zip(list(dic.keys()),dic.values()))))[1])
import matplotlib.pyplot as plt
def defaut_integrite(indic):
'''génération d'un graphe présentant la répartition des défauts d'intégrité'''
defauts = ['Pdc non unique', 'Station multi-operateurs', 'Station multi-enseignes', 'Station multi-localisations',
'Pdc multi-stations', 'station avec plusieurs noms', 'station multi-implantations',
'nombre de pdc par station incoherent', 'station multi-acces', 'station multi-horaires',
'acces deux-roues incoherent', 'localisation multi-adresses']
val = dict(item for item in indic[-1].items() if item[0] in defauts and item[1] > 0)
val = dict(sorted(val.items(), key=lambda item:item[1], reverse=False))
date = indic[-1]['date_irve']
total = indic[-1]['IRVE_itinerance_residuel' + date + '.csv']
fig, (ax, ax2) = plt.subplots(2, 1, layout='constrained', figsize=(8, 7))
ax.pie(list(val.values()), autopct='%.0f%%',labels=list(val.keys()))
ax.set_title("Répartition des défauts d'intégrité - " + date)
operateurs = [oper.split(sep='@')[1].split('.')[0] for oper in indic[-1]['operateurs']] + ['autres']
defauts = indic[-1]['erreurs_operateurs'] + [total - sum(indic[-1]['erreurs_operateurs'])]
ax2.bar(operateurs, defauts, width=0.5)
plt.setp(ax2.get_xticklabels(), rotation=20, ha="right")
ax2.set_title("Principaux défauts par opérateur")
plt.show()
return date
def doublons(log):
'''génération d'un graphe présentant l'évolution du nombre de doublons pdc + stations'''
dic = {}
for i, indic in enumerate(log):
if i == 0:
dic['date'] = []
dic['pdc doublon'] = []
dic['pdc sans doublon'] = []
dic['pdc doublon %'] = []
else:
date = indic['date_irve']
dic['date'].append(date)
doublon = indic['IRVE_itinerance_doublons' + date + '.csv']
dic['pdc doublon'].append(doublon)
dic['pdc sans doublon'].append(indic['IRVE_itinerance_complet' + date + '.csv'] - doublon)
dic['pdc doublon %'].append(100 * dic['pdc doublon'][-1] / (dic['pdc doublon'][-1] + dic['pdc sans doublon'][-1]))
fig, (ax2, ax1) = plt.subplots(2, 1, layout='constrained', figsize=(8, 6))
ax1.plot(dic['date'], dic['pdc doublon %'])
ax1.set_title('Evolution du % de doublons pdc')
plt.setp(ax1.get_xticklabels(), rotation=40, ha="right")
bottom = [0] * (len(log) - 1)
ax2.bar(dic['date'], dic['pdc sans doublon'], width=0.5, label='pdc sans doublon', bottom=[0] * (len(log) - 1))
ax2.bar(dic['date'], dic['pdc doublon'], width=0.5, label='pdc avec doublon', bottom=dic['pdc sans doublon'])
ax2.set_title("Evolution du nombre de points de recharge IRVE (avec doublons)")
plt.setp(ax2.get_xticklabels(), rotation=40, ha="right")
ax2.legend(loc="lower right")
plt.show()
def evolution(log):
'''génération d'un graphe présentant l'évolution du nombre de pdc valides ou présentant des défauts'''
dic = {}
for i, indic in enumerate(log):
if i == 0:
dic['date'] = []
dic['pdc defaut'] = []
dic['pdc valide'] = []
dic['pdc defaut %'] = []
else:
date = indic['date_irve']
dic['date'].append(date)
dic['pdc defaut'].append(indic['IRVE_itinerance_residuel' + date + '.csv'])
dic['pdc valide'].append(indic['IRVE_itinerance_valide' + date + '.csv'])
dic['pdc defaut %'].append(100 * dic['pdc defaut'][-1] / (dic['pdc defaut'][-1] + dic['pdc valide'][-1]))
print('pdc valide : ', dic['pdc valide'][-1])
print('pdc defaut : ', dic['pdc defaut'][-1])
fig, (ax2, ax1, ax3) = plt.subplots(3, 1, layout='constrained', figsize=(8, 9))
ax1.plot(dic['date'], dic['pdc defaut %'])
ax1.set_title('Evolution du % de pdc en défaut (hors doublons)')
plt.setp(ax1.get_xticklabels(), rotation=40, ha="right")
ax3.bar(dic['date'], dic['pdc defaut'], width=1, label='pdc defaut', bottom=[0] * (len(log) - 1))
ax3.set_title('Evolution du nombre de pdc en défaut')
plt.setp(ax3.get_xticklabels(), rotation=40, ha="right")
bottom = [0] * (len(log) - 1)
ax2.bar(dic['date'], dic['pdc valide'], width=0.5, label='pdc valide', bottom=[0] * (len(log) - 1))
ax2.bar(dic['date'], dic['pdc defaut'], width=0.5, label='pdc avec défaut', bottom=dic['pdc valide'])
ax2.set_title("Evolution du nombre de points de recharge IRVE (hors doublons)")
plt.setp(ax2.get_xticklabels(), rotation=40, ha="right")
ax2.legend(loc="lower right")
plt.show()
import pandas as pd
from util_carto import Cart
def defaut_carte(date, chemin):
'''génération d'une carte folium des stations présentant des défauts d'intégrité'''
file = 'IRVE_itinerance_residuel'+date+'.csv'
#chemin = 'https://raw.githubusercontent.com/loco-philippe/Environmental-Sensing/main/python/Validation/irve/Analyse/'
#chemin = 'D:\\philippe\\python ESstandard\\Environmental-Sensing\\python\\Validation\\irve\\Analyse\\'
#chemin = 'C:\\Users\\phili\\github\\Environmental-Sensing\\python\\Validation\\irve\\Analyse\\'
irve = pd.read_csv(chemin + '/' + file, sep=',', low_memory=False)
principal = [17, 18, 19, 20, 21]
irve['principal'] = True
for ind in principal:
irve['principal'] &= irve.iloc[:,ind]
irve_p = irve[~irve['principal']].drop_duplicates('id_station_itinerance').reset_index(drop=True)
secondaire = [22, 23, 24, 25, 26, 27, 28]
irve['secondaire'] = True
for ind in secondaire:
irve['secondaire'] &= irve.iloc[:,ind]
irve['secondaire'] |= (~irve['principal'] & ~irve['secondaire'])
irve_s = irve[~irve['secondaire']].drop_duplicates('id_station_itinerance').reset_index(drop=True)
popup = [[], []]
locat = [[], []]
vide = {' ': ' '}
for ind, irve in enumerate((irve_p, irve_s)):
for i in range(len(irve)):
defauts = {irve_p.columns[col]: False for col in range(17, 29) if not irve.iloc[i,col]}
popup[ind].append( {'id_station': irve['id_station_itinerance'][i],
'id_pdc': irve['id_pdc_itinerance'][i],
'contact_operateur': irve['contact_operateur'][i],
'nom_enseigne': irve['nom_enseigne'][i],
'nom_station': irve['nom_station'][i],
'adresse_station': irve['adresse_station'][i],
'fichier des écarts': '<a href="https://github.com/loco-philippe/Environmental-Sensing/blob/main/python/Validation/irve/Analyse/">IRVE_itinerance_residuel</a>',
'date du fichier': date } | vide | defauts )
coord = json.loads(irve['coordonneesXY'][i])
coord.reverse()
locat[ind].append(coord)
cart = Cart([47, 2.5], zoom_start=6)
cart.add_markers(locat[0], popup=popup[0], color='red', group='écarts entités', max_width=250)
cart.add_markers(locat[1], popup=popup[1], color='orange', group='écarts attributs', max_width=250, icon='bug')
return cart