1

Corrélations multiples

This commit is contained in:
Richard Dern 2025-11-25 15:40:49 +01:00
parent 1932938fd6
commit 18afeb1e8b
10 changed files with 211 additions and 36 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -2,12 +2,42 @@
## Hexbin colorés
Les nuages de points tri-variés saturent vite : on regroupe ici les points sur une grille hexagonale et on colore chaque case par une statistique de la 3ᵉ variable (max/médiane/moyenne selon les besoins). Cela limite le bruit des outliers et met en évidence les régimes dominants plutôt que les valeurs isolées. Les scénarios sont décrits dans `meteo/correlation_presets.py` et exécutés via un helper générique (`meteo.plots.hexbin`) pour rester réutilisables ailleurs dans le dépôt.
```shell
python "docs/06 - Corrélations multiples/scripts/plot_hexbin_explorations.py"
python "docs/07 - Corrélations multiples/scripts/plot_hexbin_explorations.py"
```
![](figures/hexbin_explorations/hexbin_lux_humidity_color_temp.png)
### Température vs humidité — couleur = pluie (max)
![](figures/hexbin_explorations/hexbin_pressure_rain_color_wind.png)
Plafond d'humidité quasi systématique sous 510 °C, et même quand la température remonte, la pluie ne survient que dans une bande 816 °C où l'humidité reste >90 %. L'agrégat `max` met en valeur les épisodes pluvieux rares.
![](figures/hexbin_explorations/hexbin_temp_humidity_color_rain.png)
![Température vs humidité, couleur pluie](./figures/hexbin_explorations/hexbin_temp_humidity_color_rain.png)
### Température vs élévation solaire — couleur = humidité (médiane)
La courbe en cloche suit le soleil : températures les plus hautes autour de 60° d'élévation, tandis que l'humidité médiane dégringole dès que le soleil est positif puis remonte au crépuscule, ce qui matérialise l'assèchement diurne.
![Température vs élévation solaire, couleur humidité](./figures/hexbin_explorations/hexbin_sunelev_temp_color_humidity.png)
### Pression vs pluie — couleur = vent (médiane)
La matrice est clairsemée (pluie rare), mais les cases actives se concentrent sous ~1015 hPa avec des médianes de vent plus élevées, signe que les épisodes pluvieux et venteux coïncident surtout avec des pressions modestes.
![Pression vs pluie, couleur vent](./figures/hexbin_explorations/hexbin_pressure_rain_color_wind.png)
### Humidité vs illuminance — couleur = température (moyenne)
Deux régimes se détachent : nuits très humides et fraîches (illuminance proche de zéro), journées sèches et plus chaudes. La température moyenne colorée rend visibles les transitions plus fraîches sous ciel couvert.
![Humidité vs illuminance, couleur température](./figures/hexbin_explorations/hexbin_lux_humidity_color_temp.png)
### Élévation solaire vs illuminance — couleur = pluie (max)
Le nuage suit la diagonale « géométrie du soleil → lumière attendue », avec des cases assombries (illuminance faible malgré un soleil haut) où la pluie maximale ressort : on visualise directement l'impact d'un ciel très chargé sur l'apport lumineux.
![Élévation solaire vs illuminance, couleur pluie](./figures/hexbin_explorations/hexbin_sunelev_lux_color_rain.png)
## Conclusion
Ces hexbins confirment les mécanismes physiques attendus : le rayonnement solaire chauffe et assèche, lhumidité suit la capacité de lair en vapeur, la pluie survient surtout sous pression plus basse et lumière écrasée par les nuages. Mais notre jeu sarrête en novembre : il manque la saison froide, donc les régimes hivernaux (neige, pluies froides, journées très courtes, plafond dhumidité quasi permanent) restent invisibles. Toute généralisation doit tenir compte de cette lacune saisonnière ; il faudrait compléter la série ou intégrer des données externes (nébulosité, contexte synoptique) pour confirmer ces motifs en hiver.

View File

@ -9,8 +9,7 @@ if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from meteo.dataset import load_raw_csv
from meteo.variables import VARIABLES_BY_KEY
from meteo.plots import plot_hexbin_with_third_variable
from meteo.plots import generate_hexbin_scenarios
from meteo.correlation_presets import DEFAULT_HEXBIN_SCENARIOS
@ -30,37 +29,19 @@ def main() -> None:
print(f" Colonnes : {list(df.columns)}")
print()
for scenario in DEFAULT_HEXBIN_SCENARIOS:
var_x = VARIABLES_BY_KEY[scenario.key_x]
var_y = VARIABLES_BY_KEY[scenario.key_y]
var_color = VARIABLES_BY_KEY[scenario.key_color]
results = generate_hexbin_scenarios(
df=df,
scenarios=DEFAULT_HEXBIN_SCENARIOS,
base_output_dir=OUTPUT_DIR,
cmap="magma",
)
filename = scenario.filename
output_path = OUTPUT_DIR / filename
reduce_func = scenario.get_reduce_func()
reduce_label = scenario.get_reduce_label()
gridsize = scenario.gridsize
mincnt = scenario.mincnt
description = scenario.description
print(f"→ Hexbin {var_y.key} vs {var_x.key} (couleur = {var_color.key})")
print(f" {description}")
plot_hexbin_with_third_variable(
df=df,
var_x=var_x,
var_y=var_y,
var_color=var_color,
output_path=output_path,
gridsize=gridsize,
mincnt=mincnt,
reduce_func=reduce_func,
reduce_func_label=reduce_label,
cmap="magma",
)
print(f" ✔ Graphique enregistré : {output_path}")
for result in results:
scenario = result.scenario
print(f"→ Hexbin {result.var_y.key} vs {result.var_x.key} (couleur = {result.var_color.key})")
print(f" {scenario.description}")
print(f" Points valides : {result.point_count}")
print(f" ✔ Graphique enregistré : {result.output_path}")
print()
print("✔ Tous les graphiques hexbin ont été générés.")

View File

@ -111,6 +111,19 @@ DEFAULT_HEXBIN_SCENARIOS: Sequence[HexbinScenario] = (
gridsize=50,
mincnt=8,
),
HexbinScenario(
key_x="sun_elevation",
key_y="temperature",
key_color="humidity",
filename="hexbin_sunelev_temp_color_humidity.png",
description=(
"Suivre le cycle diurne : la température grimpe avec l'élévation solaire "
"et l'humidité médiane chute nettement une fois le soleil levé."
),
reduce="median",
gridsize=60,
mincnt=8,
),
HexbinScenario(
key_x="pressure",
key_y="rain_rate",
@ -137,6 +150,20 @@ DEFAULT_HEXBIN_SCENARIOS: Sequence[HexbinScenario] = (
gridsize=55,
mincnt=6,
),
HexbinScenario(
key_x="sun_elevation",
key_y="illuminance",
key_color="rain_rate",
filename="hexbin_sunelev_lux_color_rain.png",
description=(
"Comparer l'ensoleillement théorique (élévation solaire) et la luminance mesurée, "
"et repérer les épisodes où la pluie ou un ciel très couvert écrasent la lumière "
"malgré un soleil haut."
),
reduce="max",
gridsize=48,
mincnt=3,
),
)

View File

@ -24,6 +24,12 @@ from .relationships import (
plot_pairwise_relationship_grid,
plot_scatter_pair,
)
from .hexbin import (
HexbinPlotResult,
generate_hexbin_for_scenario,
generate_hexbin_scenarios,
prepare_hexbin_dataframe,
)
from .basic_series import (
PlotChoice,
PlotStyle,
@ -66,6 +72,10 @@ __all__ = [
"plot_hexbin_with_third_variable",
"plot_pairwise_relationship_grid",
"plot_scatter_pair",
"HexbinPlotResult",
"generate_hexbin_for_scenario",
"generate_hexbin_scenarios",
"prepare_hexbin_dataframe",
"PlotChoice",
"PlotStyle",
"plot_basic_series",

127
meteo/plots/hexbin.py Normal file
View File

@ -0,0 +1,127 @@
"""Fonctions utilitaires pour générer des hexbins à partir de scénarios."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Sequence
import pandas as pd
from meteo.correlation_presets import HexbinScenario
from meteo.variables import VARIABLES_BY_KEY, Variable
from .relationships import plot_hexbin_with_third_variable
__all__ = [
"HexbinPlotResult",
"prepare_hexbin_dataframe",
"generate_hexbin_for_scenario",
"generate_hexbin_scenarios",
]
@dataclass(frozen=True)
class HexbinPlotResult:
"""Résultat d'un hexbin généré à partir d'un scénario prédéfini."""
scenario: HexbinScenario
var_x: Variable
var_y: Variable
var_color: Variable
output_path: Path
point_count: int
def _get_variables_for_scenario(scenario: HexbinScenario) -> tuple[Variable, Variable, Variable]:
try:
return (
VARIABLES_BY_KEY[scenario.key_x],
VARIABLES_BY_KEY[scenario.key_y],
VARIABLES_BY_KEY[scenario.key_color],
)
except KeyError as exc:
raise KeyError(f"Variable inconnue dans le scénario hexbin : {exc}") from exc
def prepare_hexbin_dataframe(
df: pd.DataFrame,
var_x: Variable,
var_y: Variable,
var_color: Variable,
) -> pd.DataFrame:
"""Sélectionne et nettoie les colonnes utiles à un hexbin."""
columns = [var_x.column, var_y.column, var_color.column]
missing = [col for col in columns if col not in df.columns]
if missing:
raise KeyError(f"Colonnes absentes dans le DataFrame : {missing}")
return df[columns].dropna()
def generate_hexbin_for_scenario(
df: pd.DataFrame,
scenario: HexbinScenario,
*,
base_output_dir: str | Path,
cmap: str = "viridis",
) -> HexbinPlotResult:
"""Construit un hexbin complet à partir d'un scénario et retourne ses métadonnées."""
var_x, var_y, var_color = _get_variables_for_scenario(scenario)
cleaned_df = prepare_hexbin_dataframe(df, var_x, var_y, var_color)
output_dir = Path(base_output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / scenario.filename
reduce_func = scenario.get_reduce_func()
reduce_label = scenario.get_reduce_label()
plot_hexbin_with_third_variable(
df=cleaned_df,
var_x=var_x,
var_y=var_y,
var_color=var_color,
output_path=output_path,
gridsize=scenario.gridsize,
mincnt=scenario.mincnt,
reduce_func=reduce_func,
reduce_func_label=reduce_label,
cmap=cmap,
)
return HexbinPlotResult(
scenario=scenario,
var_x=var_x,
var_y=var_y,
var_color=var_color,
output_path=output_path.resolve(),
point_count=len(cleaned_df),
)
def generate_hexbin_scenarios(
df: pd.DataFrame,
scenarios: Sequence[HexbinScenario],
*,
base_output_dir: str | Path,
cmap: str = "viridis",
) -> list[HexbinPlotResult]:
"""Génère en série plusieurs hexbins prédéfinis et retourne les résultats."""
if not scenarios:
raise ValueError("Aucun scénario hexbin fourni.")
results: list[HexbinPlotResult] = []
for scenario in scenarios:
result = generate_hexbin_for_scenario(
df=df,
scenario=scenario,
base_output_dir=base_output_dir,
cmap=cmap,
)
results.append(result)
return results