#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2021 R. Marty
# (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""
Images - Peak Flow comparison
"""
import matplotlib.pyplot as mplt
[docs]
def plot_regime(data=None, fig_filename=None, boxplot=False, fill=False):
"""
Tracer l'analyse des pointes de crue amont et aval
Parameters
----------
data : dict
Dictionnaire des régimes hydrologiques
fig_filename : str
Nom du fichier image
boxplot : bool
Tracer les quantiles sous forme de boxplot (True)
ou d'enveloppes quantiliques (False).
Par défaut: False.
fill : bool
Remplir les boxplots/enveloppes quantiliques.
Par défaut: False
Returns
-------
fig_filename : str
Nom du fichier image
See Also
--------
pyspc.core.serie.Serie.regime
pyspc.core.series.Series.regime
"""
# =========================================================================
# --- INFORMATION REGIME
# =========================================================================
loc = list({c[0] for c in data.columns})[0]
var = list({c[1] for c in data.columns})[0]
ics = find_confidence_intervals(data.columns)
# =========================================================================
# --- TRACER FIGURE INITIALE
# =========================================================================
fig = mplt.figure(dpi=300, figsize=(11.69, 8.27))
ax = fig.add_axes((0.05, 0.08, 0.90, 0.85))
# =========================================================================
# --- VERSION boxplot
# =========================================================================
if boxplot:
bxpstats = _data2bxpstats(data, loc, var)
props = ax.bxp(bxpstats, showmeans=True, meanline=True,
patch_artist=True)
if fill:
for patch in props['boxes']:
patch.set_facecolor('lightskyblue')
patch.set_edgecolor('tab:blue')
patch.set_alpha(0.8)
else:
for patch in props['boxes']:
patch.set_facecolor('white')
patch.set_edgecolor('tab:blue')
for patch in props['medians']:
patch.set_color('black')
for patch in props['means']:
patch.set_color('blue')
for patch in props['whiskers']:
patch.set_color('tab:grey')
for patch in props['caps']:
patch.set_color('tab:grey')
# =========================================================================
# --- VERSION classique
# =========================================================================
else:
if fill:
for ic in ics:
ax.fill_between(
data.index,
data[(loc, var, ic[1])], data[(loc, var, ic[2])],
color='tab:cyan', alpha=1 - ic[0]/100.,
label=f'ic{ic[0]}')
else:
for ic in ics:
ax.plot(data.index, data[(loc, var, ic[1])],
color='tab:cyan', linestyle='-', linewidth=2,
marker='', label=ic[1])
ax.plot(data.index, data[(loc, var, ic[2])],
color='tab:cyan', linestyle='-', linewidth=2,
marker='', label=ic[2])
ax.scatter(data.index, data[(loc, var, 'min')], s=10,
color='white', edgecolor='black', marker='o',
label='min')
ax.scatter(data.index, data[(loc, var, 'max')], s=10,
color='white', edgecolor='black', marker='o',
label='max')
ax.plot(data.index, data[(loc, var, 'q50')],
color='tab:blue', linestyle='-', linewidth=2, marker='',
label='med')
ax.plot(data.index, data[(loc, var, 'mean')],
color='tab:orange', linestyle='-', linewidth=2, marker='',
label='moy')
fig.legend(bbox_to_anchor=(0.5, 0.02), loc=10, ncol=8, fontsize=12)
# =========================================================================
# --- TRACER FIGURE FINALE
# =========================================================================
fig.suptitle(f"Régme hydrologique - {loc} - {var}",
fontsize=12, fontweight='bold')
if fig_filename is None:
return fig
mplt.savefig(fig_filename, dpi=300)
mplt.close(fig)
return fig_filename
[docs]
def _data2bxpstats(data, loc, var):
"""
Construire la liste des boxplots à partir du régime
Parameters
----------
data : dict
Dictionnaire des régimes hydrologiques
loc : str
Lieu
var : str
Grandeur
Returns
-------
bxpstats : list
Dictionnaires contenant les informations de chaque boxplot
- med: Median (scalar).
- q1, q3: First & third quartiles (scalars).
- whislo, whishi: Lower & upper whisker positions (scalars).
- mean: Mean (scalar). Needed if showmeans=True.
- fliers: Data beyond the whiskers (array-like).
Needed if showfliers=True.
- label: Name of the dataset (str).
If available, this will be used a tick label for the boxplot
See Also
--------
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bxp.html
"""
bxpstats = []
for row in data.iterrows():
bx = {
'label': row[0],
'med': row[1][(loc, var, 'q50')], # Median (50th percentile)
'mean': row[1][(loc, var, 'mean')], # Mean
'fliers': [row[1][(loc, var, 'min')],
row[1][(loc, var, 'max')]] # Outliers
}
# 'whislo': 162.6, # Bottom whisker position
# 'q1' : 170.2, # First quartile (25th percentile)
# 'q3' : 180.4, # Third quartile (75th percentile)
# 'whishi': 187.8, # Top whisker position
for k1, k2 in zip(['whislo', 'q1', 'q3', 'whishi'],
['q10', 'q25', 'q75', 'q90']):
try:
bx[k1] = row[1][(loc, var, k2)]
except KeyError as ke:
raise ValueError(
f"La fréquence '{k2}' est nécessaire pour tracer le "
"régime sous forme de boxplot") from ke
bxpstats.append(bx)
return bxpstats
[docs]
def find_confidence_intervals(cols):
"""Définir les bornes d'un intervalle de confiance."""
ics = []
skip = ['amin', 'amax', 'q50', 'mean', 'min', 'max']
for c in cols:
if c[-1] in skip:
continue
fc = int(float(c[-1].replace('q', '')))
for c2 in cols:
if c2[-1] in skip:
continue
fc2 = int(float(c2[-1].replace('q', '')))
if fc + fc2 != 100:
continue
ics.append((max(fc, fc2) - min(fc, fc2),
min(c[-1], c2[-1]),
max(c[-1], c2[-1])))
ics = sorted(list(set(ics)), reverse=True)
return ics