Corrélations multiples
This commit is contained in:
parent
1932938fd6
commit
18afeb1e8b
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 |
@ -2,12 +2,42 @@
|
|||||||
|
|
||||||
## Hexbin colorés
|
## 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
|
```shell
|
||||||
python "docs/06 - Corrélations multiples/scripts/plot_hexbin_explorations.py"
|
python "docs/07 - Corrélations multiples/scripts/plot_hexbin_explorations.py"
|
||||||
```
|
```
|
||||||
|
|
||||||

|
### Température vs humidité — couleur = pluie (max)
|
||||||
|
|
||||||

|
Plafond d'humidité quasi systématique sous 5–10 °C, et même quand la température remonte, la pluie ne survient que dans une bande 8–16 °C où l'humidité reste >90 %. L'agrégat `max` met en valeur les épisodes pluvieux rares.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### É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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Ces hexbins confirment les mécanismes physiques attendus : le rayonnement solaire chauffe et assèche, l’humidité suit la capacité de l’air en vapeur, la pluie survient surtout sous pression plus basse et lumière écrasée par les nuages. Mais notre jeu s’arrête en novembre : il manque la saison froide, donc les régimes hivernaux (neige, pluies froides, journées très courtes, plafond d’humidité 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.
|
||||||
|
|||||||
@ -9,8 +9,7 @@ if str(PROJECT_ROOT) not in sys.path:
|
|||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
from meteo.dataset import load_raw_csv
|
from meteo.dataset import load_raw_csv
|
||||||
from meteo.variables import VARIABLES_BY_KEY
|
from meteo.plots import generate_hexbin_scenarios
|
||||||
from meteo.plots import plot_hexbin_with_third_variable
|
|
||||||
from meteo.correlation_presets import DEFAULT_HEXBIN_SCENARIOS
|
from meteo.correlation_presets import DEFAULT_HEXBIN_SCENARIOS
|
||||||
|
|
||||||
|
|
||||||
@ -30,37 +29,19 @@ def main() -> None:
|
|||||||
print(f" Colonnes : {list(df.columns)}")
|
print(f" Colonnes : {list(df.columns)}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
for scenario in DEFAULT_HEXBIN_SCENARIOS:
|
results = generate_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]
|
|
||||||
|
|
||||||
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,
|
df=df,
|
||||||
var_x=var_x,
|
scenarios=DEFAULT_HEXBIN_SCENARIOS,
|
||||||
var_y=var_y,
|
base_output_dir=OUTPUT_DIR,
|
||||||
var_color=var_color,
|
|
||||||
output_path=output_path,
|
|
||||||
gridsize=gridsize,
|
|
||||||
mincnt=mincnt,
|
|
||||||
reduce_func=reduce_func,
|
|
||||||
reduce_func_label=reduce_label,
|
|
||||||
cmap="magma",
|
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()
|
||||||
|
|
||||||
print("✔ Tous les graphiques hexbin ont été générés.")
|
print("✔ Tous les graphiques hexbin ont été générés.")
|
||||||
|
|||||||
@ -111,6 +111,19 @@ DEFAULT_HEXBIN_SCENARIOS: Sequence[HexbinScenario] = (
|
|||||||
gridsize=50,
|
gridsize=50,
|
||||||
mincnt=8,
|
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(
|
HexbinScenario(
|
||||||
key_x="pressure",
|
key_x="pressure",
|
||||||
key_y="rain_rate",
|
key_y="rain_rate",
|
||||||
@ -137,6 +150,20 @@ DEFAULT_HEXBIN_SCENARIOS: Sequence[HexbinScenario] = (
|
|||||||
gridsize=55,
|
gridsize=55,
|
||||||
mincnt=6,
|
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,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,12 @@ from .relationships import (
|
|||||||
plot_pairwise_relationship_grid,
|
plot_pairwise_relationship_grid,
|
||||||
plot_scatter_pair,
|
plot_scatter_pair,
|
||||||
)
|
)
|
||||||
|
from .hexbin import (
|
||||||
|
HexbinPlotResult,
|
||||||
|
generate_hexbin_for_scenario,
|
||||||
|
generate_hexbin_scenarios,
|
||||||
|
prepare_hexbin_dataframe,
|
||||||
|
)
|
||||||
from .basic_series import (
|
from .basic_series import (
|
||||||
PlotChoice,
|
PlotChoice,
|
||||||
PlotStyle,
|
PlotStyle,
|
||||||
@ -66,6 +72,10 @@ __all__ = [
|
|||||||
"plot_hexbin_with_third_variable",
|
"plot_hexbin_with_third_variable",
|
||||||
"plot_pairwise_relationship_grid",
|
"plot_pairwise_relationship_grid",
|
||||||
"plot_scatter_pair",
|
"plot_scatter_pair",
|
||||||
|
"HexbinPlotResult",
|
||||||
|
"generate_hexbin_for_scenario",
|
||||||
|
"generate_hexbin_scenarios",
|
||||||
|
"prepare_hexbin_dataframe",
|
||||||
"PlotChoice",
|
"PlotChoice",
|
||||||
"PlotStyle",
|
"PlotStyle",
|
||||||
"plot_basic_series",
|
"plot_basic_series",
|
||||||
|
|||||||
127
meteo/plots/hexbin.py
Normal file
127
meteo/plots/hexbin.py
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user