From 29ba8f9641dca5dc226e82101c894e95bc38f505 Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Thu, 7 Mar 2019 15:15:05 +0100
Subject: [PATCH] [SCM] add studies visualizer. refactor visualization part.

---
 .../abstract_extended_study.py                |  1 -
 .../meteo_france_SCM_study/abstract_study.py  | 14 +++++++
 .../meteo_france_SCM_study/safran/safran.py   |  8 ++--
 .../visualization/__init__.py                 |  0
 .../studies_visualization/__init__.py         |  0
 .../main_studies_visualizer.py                | 24 ++++++++++++
 .../studies_visualization/studies.py          | 28 +++++++++++++
 .../studies_visualizer.py                     | 39 +++++++++++++++++++
 .../study_visualization/__init__.py           |  0
 .../main_study_visualizer.py}                 |  3 +-
 .../study_visualization/study_visualizer.py}  |  0
 .../visualization/utils.py                    | 29 ++++++++++++++
 .../test_SCM_study.py                         |  6 +--
 13 files changed, 143 insertions(+), 9 deletions(-)
 create mode 100644 experiment/meteo_france_SCM_study/visualization/__init__.py
 create mode 100644 experiment/meteo_france_SCM_study/visualization/studies_visualization/__init__.py
 create mode 100644 experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py
 create mode 100644 experiment/meteo_france_SCM_study/visualization/studies_visualization/studies.py
 create mode 100644 experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py
 create mode 100644 experiment/meteo_france_SCM_study/visualization/study_visualization/__init__.py
 rename experiment/meteo_france_SCM_study/{main_visualize.py => visualization/study_visualization/main_study_visualizer.py} (96%)
 rename experiment/meteo_france_SCM_study/{safran/safran_visualizer.py => visualization/study_visualization/study_visualizer.py} (100%)
 create mode 100644 experiment/meteo_france_SCM_study/visualization/utils.py

diff --git a/experiment/meteo_france_SCM_study/abstract_extended_study.py b/experiment/meteo_france_SCM_study/abstract_extended_study.py
index 2da5a1e8..fad23841 100644
--- a/experiment/meteo_france_SCM_study/abstract_extended_study.py
+++ b/experiment/meteo_france_SCM_study/abstract_extended_study.py
@@ -57,6 +57,5 @@ class AbstractExtendedStudy(AbstractStudy):
                 massifs_ids_belong_to_region = self.region_name_to_massif_ids[region_name]
                 aggregated_time_serie = aggregation_function(old_time_serie[:, massifs_ids_belong_to_region], axis=1)
                 new_time_serie[:, i] = aggregated_time_serie
-
             year_to_extended_time_serie[year] = new_time_serie
         return year_to_extended_time_serie
diff --git a/experiment/meteo_france_SCM_study/abstract_study.py b/experiment/meteo_france_SCM_study/abstract_study.py
index 22d18fce..c1f362f5 100644
--- a/experiment/meteo_france_SCM_study/abstract_study.py
+++ b/experiment/meteo_france_SCM_study/abstract_study.py
@@ -44,6 +44,10 @@ class AbstractStudy(object):
     def observations_annual_maxima(self) -> AnnualMaxima:
         return AnnualMaxima(df_maxima_gev=pd.DataFrame(self.year_to_annual_maxima, index=self.safran_massif_names))
 
+    @property
+    def df_annual_mean(self) -> pd.DataFrame:
+        return pd.DataFrame(self.year_to_annual_mean, index=self.safran_massif_names).transpose()
+
     """ Load some attributes only once """
 
     @cached_property
@@ -67,6 +71,14 @@ class AbstractStudy(object):
             year_to_annual_maxima[year] = time_serie.max(axis=0)
         return year_to_annual_maxima
 
+    @cached_property
+    def year_to_annual_mean(self) -> OrderedDict:
+        # Map each year to an array of size nb_massif
+        year_to_annual_mean = OrderedDict()
+        for year, time_serie in self._year_to_daily_time_serie.items():
+            year_to_annual_mean[year] = time_serie.mean(axis=0)
+        return year_to_annual_mean
+
     def instantiate_variable_object(self, dataset) -> AbstractVariable:
         return self.variable_class(dataset, self.altitude)
 
@@ -86,6 +98,8 @@ class AbstractStudy(object):
     def _year_to_max_daily_time_serie(self) -> OrderedDict:
         return self._year_to_daily_time_serie
 
+
+
     ##########
 
     @property
diff --git a/experiment/meteo_france_SCM_study/safran/safran.py b/experiment/meteo_france_SCM_study/safran/safran.py
index 6588b1e5..ccc30a1f 100644
--- a/experiment/meteo_france_SCM_study/safran/safran.py
+++ b/experiment/meteo_france_SCM_study/safran/safran.py
@@ -26,6 +26,8 @@ class ExtendedSafran(AbstractExtendedStudy, Safran):
 if __name__ == '__main__':
     study = Safran()
     d = study.year_to_dataset_ordered_dict[1958]
-    print(d.variables['time'])
-    print(study.year_to_daily_time_serie[1958].shape)
-    print(len(d.variables['time']))
\ No newline at end of file
+    # print(d.variables['time'])
+    # print(study.year_to_daily_time_serie[1958].shape)
+    # print(len(d.variables['time']))
+    print(study.year_to_annual_mean)
+    print(study.df_annual_mean)
diff --git a/experiment/meteo_france_SCM_study/visualization/__init__.py b/experiment/meteo_france_SCM_study/visualization/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/experiment/meteo_france_SCM_study/visualization/studies_visualization/__init__.py b/experiment/meteo_france_SCM_study/visualization/studies_visualization/__init__.py
new file mode 100644
index 00000000..e69de29b
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
new file mode 100644
index 00000000..83d192de
--- /dev/null
+++ b/experiment/meteo_france_SCM_study/visualization/studies_visualization/main_studies_visualizer.py
@@ -0,0 +1,24 @@
+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 Safran, ExtendedSafran
+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.study_visualization.study_visualizer import StudyVisualizer
+from collections import OrderedDict
+
+SCM_STUDIES = [Safran, CrocusSwe, CrocusDepth]
+SCM_EXTENDED_STUDIES = [ExtendedSafran, 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:2]:
+        extended_studies = Studies(study_type)
+        studies_visualizer = StudiesVisualizer(extended_studies)
+        studies_visualizer.mean_as_a_function_of_altitude(region_only=True)
+
+
+if __name__ == '__main__':
+    normal_visualization()
diff --git a/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies.py b/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies.py
new file mode 100644
index 00000000..784f1bc0
--- /dev/null
+++ b/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies.py
@@ -0,0 +1,28 @@
+from collections import OrderedDict
+from typing import Dict
+
+from experiment.meteo_france_SCM_study.abstract_study import AbstractStudy
+
+
+class Studies(object):
+    """Object that will handle studies of the same study type (it could be Safran for instance)
+    at several altitudes"""
+
+    def __init__(self, study_type, altitude_list=None) -> None:
+        # Load altitude_list attribute
+        if altitude_list is None:
+            altitude_list = AbstractStudy.ALTITUDES
+        else:
+            assert isinstance(altitude_list, list)
+            assert len(altitude_list) > 0
+            assert all([altitudes in AbstractStudy.ALTITUDES for altitudes in altitude_list])
+            altitude_list = sorted(altitude_list)
+        self.altitude_list = altitude_list
+        # Load altitude_to_study attribute
+        self.altitude_to_study = OrderedDict()  # type: Dict[int, AbstractStudy]
+        for altitude in self.altitude_list:
+            self.altitude_to_study[altitude] = study_type(altitude=altitude)
+
+    @property
+    def first_study(self):
+        return self.altitude_to_study[self.altitude_list[0]]
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
new file mode 100644
index 00000000..398d5af7
--- /dev/null
+++ b/experiment/meteo_france_SCM_study/visualization/studies_visualization/studies_visualizer.py
@@ -0,0 +1,39 @@
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+
+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.utils import plot_df
+
+
+class StudiesVisualizer(object):
+
+    def __init__(self, studies: Studies) -> None:
+        self.studies = studies
+
+    @property
+    def first_study(self):
+        return self.studies.first_study
+
+    def mean_as_a_function_of_altitude(self, region_only=False):
+        # Load the massif names to display
+        if region_only:
+            assert isinstance(self.first_study, AbstractExtendedStudy)
+            massif_names = self.first_study.region_names
+        else:
+            massif_names = self.first_study.safran_massif_names
+        # Load the dictionary that maps each massif_name to its corresponding time series
+        mean_series = []
+        for study in self.studies.altitude_to_study.values():
+            mean_serie = study.df_annual_mean.loc[:, massif_names].mean(axis=0)
+            mean_series.append(mean_serie)
+        df_mean = pd.concat(mean_series, axis=1) # type: pd.DataFrame
+        df_mean.columns = self.studies.altitude_list
+        plot_df(df_mean)
+
+
+
+
+
diff --git a/experiment/meteo_france_SCM_study/visualization/study_visualization/__init__.py b/experiment/meteo_france_SCM_study/visualization/study_visualization/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/experiment/meteo_france_SCM_study/main_visualize.py b/experiment/meteo_france_SCM_study/visualization/study_visualization/main_study_visualizer.py
similarity index 96%
rename from experiment/meteo_france_SCM_study/main_visualize.py
rename to experiment/meteo_france_SCM_study/visualization/study_visualization/main_study_visualizer.py
index 1719c325..0b6f8296 100644
--- a/experiment/meteo_france_SCM_study/main_visualize.py
+++ b/experiment/meteo_france_SCM_study/visualization/study_visualization/main_study_visualizer.py
@@ -2,9 +2,8 @@ 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 Safran, ExtendedSafran
-from itertools import product
 
-from experiment.meteo_france_SCM_study.safran.safran_visualizer import StudyVisualizer
+from experiment.meteo_france_SCM_study.visualization.study_visualization.study_visualizer import StudyVisualizer
 from collections import OrderedDict
 
 SCM_STUDIES = [Safran, CrocusSwe, CrocusDepth]
diff --git a/experiment/meteo_france_SCM_study/safran/safran_visualizer.py b/experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py
similarity index 100%
rename from experiment/meteo_france_SCM_study/safran/safran_visualizer.py
rename to experiment/meteo_france_SCM_study/visualization/study_visualization/study_visualizer.py
diff --git a/experiment/meteo_france_SCM_study/visualization/utils.py b/experiment/meteo_france_SCM_study/visualization/utils.py
new file mode 100644
index 00000000..1a0e4532
--- /dev/null
+++ b/experiment/meteo_france_SCM_study/visualization/utils.py
@@ -0,0 +1,29 @@
+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+
+def plot_df(df: pd.DataFrame, ax=None, ylabel='Example plot', show=True, xlabel='Altitude (m)'):
+    if ax is None:
+        fig, ax = plt.subplots(1, 1)
+    for _, row in df.iterrows():
+        ax.set_xlabel(xlabel)
+        ax.set_ylabel(ylabel)
+        ax.plot(np.array(row.index), row.values, '-+', label=row.name)
+        ax.set_title("")
+        ax.legend()
+
+    if show:
+        plt.show()
+
+
+def example_plot_df():
+    df = pd.DataFrame.from_dict({
+        '1800': [0, 2],
+        '2400': [1, 3],
+    }, columns=['North', 'South'], orient='index').transpose()
+    plot_df(df)
+
+
+if __name__ == '__main__':
+    example_plot_df()
diff --git a/test/test_experiment/test_meteo_france_SCM_study/test_SCM_study.py b/test/test_experiment/test_meteo_france_SCM_study/test_SCM_study.py
index 01c0f512..811c519d 100644
--- a/test/test_experiment/test_meteo_france_SCM_study/test_SCM_study.py
+++ b/test/test_experiment/test_meteo_france_SCM_study/test_SCM_study.py
@@ -3,10 +3,10 @@ import unittest
 
 import pandas as pd
 
-from experiment.meteo_france_SCM_study.crocus.crocus import ExtendedCrocusSwe, Crocus
-from experiment.meteo_france_SCM_study.main_visualize import study_iterator
+from experiment.meteo_france_SCM_study.crocus.crocus import ExtendedCrocusSwe
+from experiment.meteo_france_SCM_study.visualization.study_visualization.main_study_visualizer import study_iterator
 from experiment.meteo_france_SCM_study.safran.safran import Safran, ExtendedSafran
-from experiment.meteo_france_SCM_study.safran.safran_visualizer import StudyVisualizer
+from experiment.meteo_france_SCM_study.visualization.study_visualization.study_visualizer import StudyVisualizer
 from test.test_utils import load_scm_studies
 
 
-- 
GitLab