diff --git a/experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py b/experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py index 861cf404a73730de86007663a833035a662915dc..689ac9c9b51b1af9b33909d43f61d9f85086b9a7 100644 --- a/experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py +++ b/experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py @@ -1,25 +1,40 @@ +from experiment.meteo_france_SCM_study.abstract_score import MannKendall, WeigthedScore, MeanScore, MedianScore from experiment.meteo_france_SCM_study.abstract_study import AbstractStudy from experiment.meteo_france_SCM_study.crocus.crocus import CrocusDepth, CrocusSwe, ExtendedCrocusDepth, \ ExtendedCrocusSwe from experiment.meteo_france_SCM_study.safran.safran import SafranSnowfall, ExtendedSafranSnowfall, \ ExtendedSafranTotalPrecip from experiment.meteo_france_SCM_study.visualization.studies_visualization.studies import Studies -from experiment.meteo_france_SCM_study.visualization.studies_visualization.studies_visualizer import StudiesVisualizer +from experiment.meteo_france_SCM_study.visualization.studies_visualization.studies_visualizer import StudiesVisualizer, \ + AltitudeVisualizer +from experiment.meteo_france_SCM_study.visualization.study_visualization.main_study_visualizer import ALL_ALTITUDES, \ + study_iterator_global from experiment.meteo_france_SCM_study.visualization.study_visualization.study_visualizer import StudyVisualizer from collections import OrderedDict -SCM_STUDIES = [SafranSnowfall, CrocusSwe, CrocusDepth] -SCM_EXTENDED_STUDIES = [ExtendedSafranTotalPrecip, ExtendedSafranSnowfall, ExtendedCrocusSwe, ExtendedCrocusDepth] -SCM_STUDY_TO_EXTENDED_STUDY = OrderedDict(zip(SCM_STUDIES, SCM_EXTENDED_STUDIES)) - def normal_visualization(): - for study_type in SCM_EXTENDED_STUDIES[:1]: + for study_type in [ExtendedSafranTotalPrecip]: extended_studies = Studies(study_type) studies_visualizer = StudiesVisualizer(extended_studies) studies_visualizer.mean_as_a_function_of_altitude(region_only=True) +def altitude_trends(): + save_to_file = False + only_first_one = False + # altitudes that have 20 massifs at least + altitudes = ALL_ALTITUDES[3:-6] + # altitudes = ALL_ALTITUDES[:2] + visualizers = [StudyVisualizer(study, save_to_file=save_to_file, temporal_non_stationarity=True, verbose=True, + score_class=MedianScore) + for study in study_iterator_global(study_classes=[SafranSnowfall], only_first_one=only_first_one, + altitudes=altitudes)] + altitude_to_visualizer = OrderedDict(zip(altitudes, visualizers)) + visualizer = AltitudeVisualizer(altitude_to_visualizer) + visualizer.negative_trend_percentages_evolution() + + if __name__ == '__main__': - normal_visualization() + altitude_trends() diff --git a/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py b/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py index e10979f8c0aaa7b484c819652f6c2f3bd5aa1cc2..2403e3924739ebf551f24e75981ad3973a9afdb9 100644 --- a/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py +++ b/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py @@ -1,11 +1,17 @@ +from collections import OrderedDict, Counter +from typing import Dict + import numpy as np import pandas as pd import matplotlib.pyplot as plt +from matplotlib.lines import Line2D from experiment.meteo_france_SCM_study.abstract_extended_study import AbstractExtendedStudy from experiment.meteo_france_SCM_study.visualization.studies_visualization.studies import \ Studies +from experiment.meteo_france_SCM_study.visualization.study_visualization.study_visualizer import StudyVisualizer from experiment.meteo_france_SCM_study.visualization.utils import plot_df +from utils import cached_property, get_display_name_from_object_type class StudiesVisualizer(object): @@ -29,11 +35,86 @@ class StudiesVisualizer(object): for study in self.studies.altitude_to_study.values(): mean_serie = study.df_annual_total.loc[:, massif_names].mean(axis=0) mean_series.append(mean_serie) - df_mean = pd.concat(mean_series, axis=1) # type: pd.DataFrame + df_mean = pd.concat(mean_series, axis=1) # type: pd.DataFrame df_mean.columns = self.studies.altitude_list plot_df(df_mean) +class AltitudeVisualizer(object): + + def __init__(self, altitude_to_study_visualizer: Dict[int, StudyVisualizer]): + assert isinstance(altitude_to_study_visualizer, OrderedDict) + self.altitude_to_study_visualizer = altitude_to_study_visualizer + + @property + def altitudes(self): + return list(self.altitude_to_study_visualizer.keys()) + + @cached_property + def all_percentages(self): + return [v.percentages_of_negative_trends()[0] for v in self.altitude_to_study_visualizer.values()] + + @property + def any_study_visualizer(self) -> StudyVisualizer: + return list(self.altitude_to_study_visualizer.values())[0] + + def get_item_fct(self, year): + idx = self.any_study_visualizer.starting_years.index(year) + f = lambda s: s[idx] + return f + + @cached_property + def starting_year(self): + return self.any_study_visualizer.starting_years[0] + + def get_top_potential_years(self, reverse=False): + top_n = 5 + top_top = 3 + # keep the top_n for each altitude + all_years = [[year for year, _ in sorted(enumerate(p), key=lambda s:s[1], reverse=reverse)[-top_n:]] for p in self.all_percentages] + from itertools import chain + all_years = list(chain(*all_years)) + years = [y for y, _ in sorted(Counter(all_years).items(), key=lambda s:s[1])[-top_top:]] + years = [y + self.starting_year for y in years] + return years + + def negative_trend_percentages_evolution(self): + curve_name__metric_and_color = [ + ('max', np.max, 'r'), + ('mean', np.mean, 'b'), + ('median', np.median, 'c'), + ('min', np.min, 'g'), + ] + # Add some years + # spotted_years = [1963, 1976] + # years_to_display = spotted_years + str_markers = ['o'] + [m for m in Line2D.markers if isinstance(m, str)][3:] + # for year, marker in zip(years_to_display, str_markers): + # new = (str(year), self.get_item_fct(year), 'y', marker + ':') + # curve_name__metric_and_color.append(new) + for year, marker in zip(self.get_top_potential_years(), str_markers): + new = (str(year), self.get_item_fct(year), 'm', marker + ':') + curve_name__metric_and_color.append(new) + for year, marker in zip(self.get_top_potential_years(reverse=True), str_markers): + new = (str(year), self.get_item_fct(year), 'y', marker + ':') + curve_name__metric_and_color.append(new) + fig, ax = plt.subplots(1, 1, figsize=self.any_study_visualizer.figsize) + for curve_name, metric, color, *marker in curve_name__metric_and_color[:]: + marker, curve_name = (marker[0], curve_name + ' starting year') if marker \ + else ('-', curve_name + ' over the starting years') + values = [metric(p) for p in self.all_percentages] + ax.plot(self.altitudes, values, color + marker, label=curve_name) + ax.legend() + ax.set_xticks(self.altitudes) + ax.set_yticks(list(range(0,101, 10))) + ax.grid() + ax.axhline(y=50, color='k') + ax.set_ylabel('% of negative trends') + ax.set_xlabel('altitude') + scoer_class_name = get_display_name_from_object_type(self.any_study_visualizer.score_class) + title = 'Distribution of negative trend for the {}'.format(scoer_class_name) + ax.set_title(title) + plt.show() diff --git a/experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py b/experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py index 23ad3a76837f72049e0b52a5a1213bd879b2726d..6c88b1ba8f4eb009f8b36e4d0501ca64679baaf1 100644 --- a/experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py +++ b/experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py @@ -402,7 +402,7 @@ class StudyVisualizer(object): def visualize_score_wrt_starting_year(self, ax, massif_name): if massif_name is None: - percentage, title = self.percentage_of_negative_trends() + percentage, title = self.percentages_of_negative_trends() scores = percentage ax.set_ylabel('% of negative trends') # Add two lines of interest @@ -420,24 +420,27 @@ class StudyVisualizer(object): ax.set_title(title) ax.xaxis.set_ticks(self.starting_years[2::20]) - def percentage_of_negative_trends(self): + def percentages_of_negative_trends(self): + print('start computing percentages negative trends') # scores = np.median([np.array(v) < 0 for v in self.massif_name_to_scores.values()], axis=0) + # Take the mean with respect to the massifs + # We obtain an array whose length equal the length of starting years scores = np.mean([np.array(v) < 0 for v in self.massif_name_to_scores.values()], axis=0) - percentage = 100 * scores + percentages = 100 * scores # First argmin, first argmax argmin, argmax = np.argmin(scores), np.argmax(scores) # Last argmin, last argmax # argmin, argmax = len(scores) - 1 - np.argmin(scores[::-1]), len(scores) - 1 - np.argmax(scores[::-1]) top_starting_year_for_positive_trend = self.starting_years[argmin] top_starting_year_for_negative_trend = self.starting_years[argmax] - top_percentage_positive_trend = round(100 - percentage[argmin], 0) - top_percentage_negative_trend = round(percentage[argmax], 0) + top_percentage_positive_trend = round(100 - percentages[argmin], 0) + top_percentage_negative_trend = round(percentages[argmax], 0) title = "Global trend; > 0: {}% in {}; < 0: {}% in {}".format(top_percentage_positive_trend, top_starting_year_for_positive_trend, top_percentage_negative_trend, top_starting_year_for_negative_trend) - return percentage, title + return percentages, title def visualize_all_mean_and_max_graphs(self): specified_massif_ids = [self.study.study_massif_names.index(massif_name)