Skip to content
Snippets Groups Projects
Commit a8e6462c authored by Gilares Paul's avatar Gilares Paul
Browse files

Readme update

parent d4934b3a
No related branches found
No related tags found
No related merge requests found
No preview for this file type
# idées
# But du Projet
par département, faire la moyenne des valeurs, la stocker dans un dictionnaire et faire ensuite un degradé de couleur, faire une fonction qui permet de faire ça sur chaque paramètre
Ce projet vise à analyser la qualité de l'eau courante en France et son évolution entre 2018 et 2024, en se basant sur divers indicateurs tels que le pH, le chlore, les nitrites, les nitrates, les métaux lourds et les pesticides. L'analyse est réalisée à l'échelle départementale pour fournir une évaluation complète et détaillée de la qualité de l'eau au fil du temps afin d'identifier les tendances et les zones nécessitant une attention particulière pour garantir la sécurité sanitaire des populations locales.
# Lancement de la page
# D’où Vient la Data ?
## Lancer le front :
Les données utilisées pour ce projet proviennent du site [data.gouv.fr](https://www.data.gouv.fr/fr/datasets/resultats-du-controle-sanitaire-de-leau-du-robinet/), la plateforme des données publiques françaises, qui fournit les résultats du contrôle sanitaire de l'eau du robinet. Ces données incluent divers indicateurs de qualité de l'eau, permettant une analyse approfondie de son évolution entre 2018 et 2024.
Pour lancer le front, rentrer la commande suivant dans le dossier frontend :
# Prétraitement de la Data
`npm run dev`
## Etat des Lieux de la Data
## Lancer le back
Pour chaque année, on a récupéré un fichier Zip sur [data.gouv.fr](https://www.data.gouv.fr/fr/datasets/resultats-du-controle-sanitaire-de-leau-du-robinet/) contenant entre autres un fichier csv contenant des informations sur les prélévements de l'année (UDI_PLV : une ligne par prélèvement avec date, lieu, coordonnées, référence du prélèvement, etc) et un autre contenant les resultats de ces prélévements (UDI_RES : une ligne par indicateur testé avec codes des paramètres testés, valeurs pour ces paramètres, référence du prélèvement, etc).
Nous avons donc trouvé sur le site de la [Sandre](https://www.sandre.eaufrance.fr/v2/) (Service d'administration nationale des données et référentiels sur l'eau), une table des correspondances entre les paramètres et leurs codes d'identification (seule information disponible dans les fichiers de data.gouv), afin d'interpréter les codes présents dans les fichers.
Nous nous sommes aussi aperçu que les coordonnées des lieux étant encodées en Lambert 93 et non en GPS,que certaines lignes n'avaient pas de coordonnées et même que certaines lignes avaient leurs coordonnées inversées
Aller dans le dossier backend et rentrer la commande suivante :
## Preprocessing et Choix des Indicateurs
`node index.js`
Pour chaque année, les fichiers de données UDI_RES et UDI_PLV ont donc été chargés et nettoyés, avec normalisation des formats et conversion des valeurs numériques et des coordonnées. Une jointure a été effectuée sur ces fichiers en utilisant la référence commune referenceprel. Les lignes ont été filtrées selon les paramètres/indicateurs qui nous intéressaient. Les coordonnées GPS inversées ont été corrigées et les valeurs manquantes complétées en fusionnant avec les coordonnées des communes. Enfin, les doublons ont été supprimés du fichier final.
Grâce à une analyse de la littérature mais également de la densité nos données, nous avons choisi de concentrer notre étude sur les paramètres/indicateurs suivants :
- **pH**
- **Chlore**
- **Nitrites**
- **Nitrates**
- **Plomb**
- **Nickel**
- **Mercure**
- **Cadmium**
- **Arsenic**
- **Pesticides**
## Table Obtenue
Les tables obtenues (une par année) à la suite de ce prétraitement contiennent alors une ligne par paramètre (sélectionnées selon notre intérêt) par prélèvement, avec, entre autres, la date et la référence du prélèvement, le lieu (département, ville, point de surveillance, coordonnées gps) et la valeur du paramètre ainsi que son unité et son seuil.
# Calcul des Scores de Qualité de l'Eau
Une fois la donnée propre et complète, il est alors possible de passer à une étape cruciale du projet, le calcul d'un score de qualité de l'eau.
Le calcul des scores est effectué ligne par ligne pour chaque point de surveillance (cdpointsurv) et chaque paramètre (cdparametre).
Les valeurs moyennes des paramètres sont calculées annuellement pour chaque point de surveillance (i.e. si il y a eu plusieurs prélèvements au cours de l'année pour le même point de surveillance alors la valeur retournée est la moyenne des valeurs du paramètre au cours de cette année).
Les données sont ensuite pivotées pour obtenir une ligne par point de surveillance avec les valeurs moyennes des paramètres.
Les valeurs manquantes après le pivot sont remplacées par 0 (si un paramètre n'a pas été prélevé cette année, alors on lui donne arbitrairement la valeur 0, dans son unité, comme si l'eau ne contenait aucune trace de ce paramètre).
Les scores pour chaque paramètre sont calculés en utilisant une fonction sigmoïde, avec des pondérations spécifiques pour les nitrites, nitrates, et métaux lourds.
## Indicateurs Utilisés et Seuils réglementaires
Les indicateurs suivants ont été sélectionnés pour évaluer la qualité de l'eau, en se basant sur des seuils réglementaires (ARS et/ou OMS) :
- **pH** : Plage optimale entre 6.5 et 8.5.
- **Chlore** : Seuil à 5.0 mg/L.
- **Nitrites** : Seuil à 0.5 mg/L.
- **Nitrates** : Seuil à 50.0 mg/L.
- **Plomb** : Seuil à 5.0 µg/L.
- **Nickel** : Seuil à 20.0 µg/L.
- **Mercure** : Seuil à 1.0 µg/L.
- **Cadmium** : Seuil à 5.0 µg/L.
- **Arsenic** : Seuil à 10.0 µg/L.
- **Pesticides** : Seuil à 0.5 µg/L.
## Normalisation des Scores
Les valeurs mesurées sont normalisées en utilisant une fonction sigmoïde. Cette approche permet de lisser la transition entre les valeurs et d'obtenir des scores compris entre 0 et 1. La fonction sigmoïde est définie comme suit :
\[ \text{score} = \frac{1}{1 + e^{-10 \left(\frac{\text{valeur}}{\text{valeur\_max}} - 0.5\right)}} \]
La fonction sigmoïde a été choisie pour sa capacité à transformer les valeurs de manière non linéaire, ce qui permet de mieux refléter les variations autour des seuils critiques. Elle offre une transition douce et évite une gestion binaire des scores (0 ou 1) et surtout d'avoir un score maximal pour une valeur à peine en dessous du seuil et un score minimal pour une valeur à peine au dessus du seuil, reflétant alors mieux la réalité.
## Pondérations des Scores
Un score pour chaque indicateur est donc calculé avant d'être regroupés dans des scores plus généraux. Pour ce faire, nous pondérons les différents scores en fonction de l'importance relative de chaque indicateur pour la qualité globale de l'eau.
# Analyser les scores
Tout d'abord, nous avons regroupé en deux catégories les **Nitrites & Nitrates** et les **Métaux Lourds**.
Les pondérations permettent de mieux refléter l'impact potentiel de chaque élément dans son groupe, les nitrites étant par exemple environ 10 fois plus toxiques que les nitrates, et le mercure étant environ 5 fois plus toxique que le nickel.
- **Nitrites & Nitrates** : Pondération de 10 pour les nitrites et de 1 pour les nitrates.
- **Métaux Lourds** : Pondération de 5 pour le mercure, 4 pour l'arsenic et le cadmium, 3 pour le plomb et 1 pour le nickel.
Ensuite, afin de calculer un score global de qualité de l'eau, nous avons pondérés les différents scores comme suit :
- **pH** : Pondération modérée (1.5).
- **Chlore** : Pondération modérée (1.0).
- **Nitrites & Nitrates** : Pondération élevée (2.0), en tenant compte de la toxicité plus élevée des nitrites.
- **Métaux Lourds** : Pondération très élevée (3.0), en raison de leur toxicité et de leurs effets cumulatifs.
- **Pesticides** : Pondération élevée (2.5).
## Calcul du Score Global
Le score global est ensuite calculé comme une moyenne pondérée des scores de chaque catégories. Cette approche permet de prendre en compte l'importance relative de chaque indicateur tout en fournissant une évaluation globale de la qualité de l'eau.
# Analyser les scores - Revoir l'analyse !
## 📊 **1️⃣ Interprétation des Scores Individuels**
......@@ -31,12 +106,6 @@ Chaque paramètre est noté entre **0 et 1** :
| **0.2 - 0.5** | Pollution modérée, nécessite des actions |
| **0.0 - 0.2** | Pollution importante, non conforme |
### 🔹 **Paramètres analysés :**
- **`score_pH`** → Équilibre du pH (entre **6.5 et 8.5** idéalement).
- **`score_Chlore`** → Présence de chlore dans l’eau (trop élevé = irritant/toxique).
- **`score_Nitrites_Nitrates`** → Indicateur de pollution agricole/domestique.
- **`score_Metaux_Lourds`** → Toxicité des métaux (plomb, mercure, arsenic...).
## 🏆 **2️⃣ Interprétation du Score Global (`score_global`)**
......@@ -52,3 +121,17 @@ Le **score global** est une **moyenne pondérée** des différents paramètres e
🔍 **Exemple d’analyse :**
- **Si le score global = 0.6**, l’eau est acceptable mais peut contenir des niveaux élevés de nitrates ou de métaux lourds.
- **Analyser les scores individuels** permet d’identifier les causes exactes du problème.
# Rendu Final Lancement de la page
## Lancer le front :
Pour lancer le front, rentrer la commande suivant dans le dossier frontend :
`npm run dev`
## Lancer le back
Aller dans le dossier backend et rentrer la commande suivante :
`node index.js`
No preview for this file type
import pandas as pd
def normaliser_score(valeur, valeur_max, inverse=False):
"""Transforme une valeur en un score entre 0 et 1, évite les NaN"""
if pd.isna(valeur):
return 1
if valeur > 0 and valeur_max == 0:
return 0
if valeur == 0 and valeur_max == 0:
return 1
if inverse:
return min(valeur / valeur_max, 1)
return max(1 - (valeur / valeur_max), 0)
def calculer_scores_eau(fichier_csv, date):
"""
Charge un fichier CSV contenant les valeurs mesurées et max, puis calcule les scores par prélèvement.
"""
df = pd.read_csv(fichier_csv, low_memory=False) # Ajout de low_memory=False pour éviter les warnings
# Définition des codes paramètres et seuils réglementaires (ARS et/ou OMS)
seuils = {
1302.0: (6.5, 8.5), # pH (plage optimale)
1398.0: 5.0, # Chlore (mg/L)
1339.0: 0.5, # Nitrites (mg/L)
1340.0: 50.0, # Nitrates (mg/L)
1382.0: 5.0, # Plomb (µg/L)
1386.0: 20.0, # Nickel (µg/L)
1387.0: 1.0, # Mercure (µg/L)
1388.0: 5.0, # Cadmium (µg/L)
1369.0: 10.0, # Arsenic (µg/L)
6276.0: 0.5 # Pesticides
}
# Transformation du pH en score spécifique (distance à la plage optimale)
def score_pH(valeur):
if pd.isna(valeur):
return 1
min_pH, max_pH = seuils[1302.0]
if valeur >= min_pH and valeur <= max_pH:
return 1
return max(0, 1 - min(abs(valeur - min_pH), abs(valeur - max_pH)) / (max_pH - min_pH))
print("Nombre de lignes avant suppression des NaN :", len(df))
df = df.dropna(subset=["rsana"])
print("Nombre de lignes après suppression des NaN :", len(df))
# Vérification que df n'est pas vide après suppression des NaN
if df.empty:
raise ValueError("Le DataFrame est vide après suppression des NaN. Vérifiez les données !")
# Calcul de la moyenne des paramètres pour chaque cdpointsurv
df_moyenne = df.groupby(["cdpointsurv", "cdparametre"])["rsana"].mean().reset_index()
# Pivot pour obtenir une seule ligne par cdpointsurv avec les valeurs moyennes des paramètres
#df_pivot = df_moyenne.pivot(index="cdpointsurv", columns="cdparametre", values="rsana").fillna(0)
df_pivot = df_moyenne.pivot(index="cdpointsurv", columns="cdparametre", values="rsana")
# Compter les lignes avec des valeurs manquantes
nb_lignes_manquantes = df_pivot.isna().any(axis=1).sum()
print(f"Nombre de lignes avec des valeurs manquantes avant remplacement par 0 : {nb_lignes_manquantes}")
# Remplacer les valeurs manquantes par 0
df_pivot = df_pivot.fillna(0)
print("df_pivot après suppression des NaN :", df_pivot.shape)
# Calcul des scores pour chaque paramètre
df_pivot["score_pH"] = df_pivot[1302.0].apply(score_pH)
df_pivot["score_Chlore"] = df_pivot[1398.0].apply(lambda x: normaliser_score(x, seuils[1398.0]))
df_pivot["score_Pesticides"] = df_pivot[6276.0].apply(lambda x: normaliser_score(x, seuils[6276.0]))
# Score combiné pour Nitrites & Nitrates (pondération selon leur toxicité)
poids_nitrites = 10 # Les nitrites sont environ 10x plus toxiques que les nitrates
poids_nitrates = 1
df_pivot["score_Nitrites_Nitrates"] = df_pivot[[1339.0, 1340.0]].apply(
lambda row: (poids_nitrites * normaliser_score(row[1339.0], seuils[1339.0]) +
poids_nitrates * normaliser_score(row[1340.0], seuils[1340.0])) /
(poids_nitrites + poids_nitrates), axis=1)
# Score des métaux lourds (pondération selon leur toxicité et seuils réglementaires)
poids_metaux = {
1382.0: 3, # Plomb
1386.0: 1, # Nickel
1387.0: 5, # Mercure (très toxique)
1388.0: 4, # Cadmium
1369.0: 4 # Arsenic
}
df_pivot["score_Metaux_Lourds"] = df_pivot[[1382.0, 1386.0, 1387.0, 1388.0, 1369.0]].apply(
lambda row: sum(poids_metaux[param] * normaliser_score(row[param], seuils[param]) for param in poids_metaux) /
sum(poids_metaux.values()), axis=1)
# ✅ Ajout du score global (pondération cohérente des scores)
poids_scores = {
"score_pH": 1.5, # Pondération modérément élevée
"score_Chlore": 1, # Pondération modérée
"score_Nitrites_Nitrates": 2, # Pondération élevée
"score_Metaux_Lourds": 3, # Pondération très élevée
"score_Pesticides": 2.5 # Pondération élevée
}
df_pivot["score_global"] = df_pivot[list(poids_scores.keys())].apply(
lambda row: sum(poids_scores[score] * row[score] for score in poids_scores) /
sum(poids_scores.values()), axis=1)
# Sélectionner les colonnes d'informations générales et enlever les doublons
colonnes_info = ["cddept_x", "inseecommune", "nomcommune", "cdreseau", "cdpointsurv", "nompointsurv", "coord_x", "coord_y"]
df_info = df[colonnes_info].drop_duplicates(subset=["cdpointsurv"])
# Fusionner les informations descriptives avec les moyennes des paramètres
df_final = df_info.merge(df_pivot, on="cdpointsurv", how="left")
# Enregistrer le fichier CSV
df_final.to_csv(f"data/new_ponderation_scores_{date}.csv", index=False)
print(f"fichier {date} créé avec succès !")
return df_final
# Lancer le calcul des scores
dates = ["20" + str(i).zfill(2) for i in range(18, 25)]
processed_dates = []
for date in dates:
print(f"Processing data for {date}")
fichier = f"data/raw/Final{date}.csv" # modifier année
df_scores = calculer_scores_eau(fichier, date)
print(df_scores.head())
\ No newline at end of file
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment