152 lines
4.9 KiB
Python
152 lines
4.9 KiB
Python
"""Profils horaires/saisonniers liés à l'irradiance et aux cycles diurnes."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from typing import Sequence
|
||
|
||
import matplotlib.dates as mdates
|
||
import matplotlib.pyplot as plt
|
||
import numpy as np
|
||
import pandas as pd
|
||
|
||
from .base import export_plot_dataset
|
||
from meteo.analysis import DiurnalCycleStats
|
||
from meteo.variables import Variable
|
||
|
||
__all__ = ['plot_diurnal_cycle', 'plot_seasonal_hourly_profiles', 'plot_daylight_hours']
|
||
|
||
|
||
def plot_diurnal_cycle(
|
||
stats: DiurnalCycleStats,
|
||
variables: Sequence[Variable],
|
||
output_path: str | Path,
|
||
) -> Path:
|
||
"""
|
||
Trace les cycles diurnes moyens (moyenne/médiane + quantiles).
|
||
"""
|
||
output_path = Path(output_path)
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
export_plot_dataset(
|
||
{
|
||
"mean": stats.mean,
|
||
"median": stats.median,
|
||
"quantile_low": stats.quantile_low,
|
||
"quantile_high": stats.quantile_high,
|
||
},
|
||
output_path,
|
||
)
|
||
|
||
hours = stats.mean.index.to_numpy(dtype=float)
|
||
n_vars = len(variables)
|
||
fig, axes = plt.subplots(n_vars, 1, figsize=(10, 3 * n_vars), sharex=True)
|
||
if n_vars == 1:
|
||
axes = [axes]
|
||
|
||
for ax, var in zip(axes, variables):
|
||
col = var.column
|
||
ax.plot(hours, stats.mean[col], label="Moyenne", color="tab:blue")
|
||
ax.plot(hours, stats.median[col], label="Médiane", color="tab:orange", linestyle="--")
|
||
if stats.quantile_low is not None and stats.quantile_high is not None:
|
||
ax.fill_between(
|
||
hours,
|
||
stats.quantile_low[col],
|
||
stats.quantile_high[col],
|
||
color="tab:blue",
|
||
alpha=0.15,
|
||
label=(
|
||
f"Quantiles {int(stats.quantile_low_level * 100)}–{int(stats.quantile_high_level * 100)}%"
|
||
if stats.quantile_low_level is not None and stats.quantile_high_level is not None
|
||
else "Quantiles"
|
||
),
|
||
)
|
||
ylabel = f"{var.label} ({var.unit})" if var.unit else var.label
|
||
ax.set_ylabel(ylabel)
|
||
ax.grid(True, linestyle=":", alpha=0.5)
|
||
|
||
axes[-1].set_xlabel("Heure locale")
|
||
axes[0].legend(loc="upper right")
|
||
axes[-1].set_xticks(range(0, 24, 2))
|
||
axes[-1].set_xlim(0, 23)
|
||
fig.suptitle("Cycle diurne moyen")
|
||
fig.tight_layout(rect=[0, 0, 1, 0.97])
|
||
fig.savefig(output_path, dpi=150)
|
||
plt.close(fig)
|
||
return output_path.resolve()
|
||
|
||
def plot_seasonal_hourly_profiles(
|
||
profile_df: pd.DataFrame,
|
||
output_path: str | Path,
|
||
*,
|
||
title: str,
|
||
ylabel: str,
|
||
) -> Path:
|
||
"""
|
||
Courbes moyennes par heure pour chaque saison.
|
||
"""
|
||
output_path = Path(output_path)
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
if profile_df.empty or profile_df.isna().all().all():
|
||
fig, ax = plt.subplots()
|
||
ax.text(0.5, 0.5, "Pas de profil saisonnier disponible.", ha="center", va="center")
|
||
ax.set_axis_off()
|
||
fig.savefig(output_path, dpi=150, bbox_inches="tight")
|
||
plt.close(fig)
|
||
return output_path.resolve()
|
||
|
||
export_plot_dataset(profile_df, output_path)
|
||
|
||
hours = profile_df.index.to_numpy(dtype=float)
|
||
fig, ax = plt.subplots(figsize=(10, 4))
|
||
colors = plt.get_cmap("turbo")(np.linspace(0.1, 0.9, profile_df.shape[1]))
|
||
for color, season in zip(colors, profile_df.columns):
|
||
ax.plot(hours, profile_df[season], label=season.capitalize(), color=color)
|
||
|
||
ax.set_xlabel("Heure locale")
|
||
ax.set_ylabel(ylabel)
|
||
ax.set_title(title)
|
||
ax.grid(True, linestyle=":", alpha=0.5)
|
||
ax.legend()
|
||
fig.tight_layout()
|
||
fig.savefig(output_path, dpi=150)
|
||
plt.close(fig)
|
||
return output_path.resolve()
|
||
|
||
def plot_daylight_hours(
|
||
monthly_series: pd.Series,
|
||
output_path: str | Path,
|
||
*,
|
||
title: str = "Durée moyenne de luminosité (> seuil)",
|
||
) -> Path:
|
||
"""
|
||
Représente la durée moyenne quotidienne de luminosité par mois.
|
||
"""
|
||
output_path = Path(output_path)
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
if monthly_series.empty:
|
||
fig, ax = plt.subplots()
|
||
ax.text(0.5, 0.5, "Pas de données sur la luminosité.", ha="center", va="center")
|
||
ax.set_axis_off()
|
||
fig.savefig(output_path, dpi=150, bbox_inches="tight")
|
||
plt.close(fig)
|
||
return output_path.resolve()
|
||
|
||
export_plot_dataset(monthly_series, output_path)
|
||
|
||
months = monthly_series.index
|
||
fig, ax = plt.subplots(figsize=(10, 4))
|
||
ax.bar(months, monthly_series.values, color="goldenrod", alpha=0.8)
|
||
ax.set_ylabel("Heures de luminosité par jour")
|
||
ax.set_xlabel("Mois")
|
||
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
|
||
ax.xaxis.set_major_formatter(mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
|
||
ax.set_title(title)
|
||
ax.grid(True, axis="y", linestyle=":", alpha=0.5)
|
||
fig.tight_layout()
|
||
fig.savefig(output_path, dpi=150)
|
||
plt.close(fig)
|
||
return output_path.resolve()
|