diff --git a/mlair/plotting/postprocessing_plotting.py b/mlair/plotting/postprocessing_plotting.py index 6cf92c08a8d3d81ac57eaa0e3618c8eb8ace67c7..77bf7aef01ffcf7bae6f19f8f57966b11bf8b321 100644 --- a/mlair/plotting/postprocessing_plotting.py +++ b/mlair/plotting/postprocessing_plotting.py @@ -18,6 +18,7 @@ import seaborn as sns import xarray as xr from matplotlib.backends.backend_pdf import PdfPages from matplotlib.offsetbox import AnchoredText +import matplotlib as mpl from scipy.stats import mannwhitneyu from mlair import helpers @@ -1453,6 +1454,172 @@ class PlotSampleUncertaintyFromBootstrap(AbstractPlotClass): # pragma: no cover return ax +@TimeTrackingWrapper +class PlotScoresOnMap(AbstractPlotClass): + from mlair.plotting.data_insight_plotting import PlotStationMap + + def __init__(self, plot_folder: str, skill_score_report_path: str, model_name: str, + reference_models: Union[str, List[str]]): + + super().__init__(plot_folder, f"map_plot_skill_scores") + skill_score_report_path = "/media/felix/INTENSO/2021-12-03_1008-08_WRF_sector3_network_daily/latex_report" + reference_models = ['ols', 'NNb', 'NN1s', 'persi' ] + self.skill_score_report_path = skill_score_report_path + self.model_name = model_name + self.reference_models = reference_models + + ### internal data + self._data = None + self._ax = None + self._gl = None + + + self._plot() + ### workflow + # self.open_data() + # self._coords = self.get_coords_from_index() + + def open_data(self, file_name): + # read csv data + df = pd.read_csv(file_name) + # drop "total" column + # df = df[df.index != "total"].copy() + df = df[df[df.columns[0]] != "total"].copy() + df[["lat", "lon"]] = df[df.columns[0]].str.split("__", expand=True).replace("_", ".").replace("_", ".", + regex=True).iloc[ + :, 1:].astype(float) + + df = df.set_index(df.columns[0]) + # df = df.set_index(["lat", "lon"]) + return df.astype(float) + + def _plot_individual(self): + import cartopy.feature as cfeature + import cartopy.crs as ccrs + from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER + from mpl_toolkits.axes_grid1 import make_axes_locatable + + for competitor in self.reference_models: + file_name = os.path.join(self.skill_score_report_path, + f"error_report_skill_score_{self.model_name}_-_{competitor}.csv" + ) + + plot_path = os.path.join(os.path.abspath(self.plot_folder), + f"{self.plot_name}_{self.model_name}_-_{competitor}.pdf") + pdf_pages = matplotlib.backends.backend_pdf.PdfPages(plot_path) + df = self.open_data(file_name) + + bounds = np.linspace(-1, 1, 100) + cmap = mpl.cm.coolwarm + norm = mpl.colors.BoundaryNorm(bounds, cmap.N, extend='both') + ticks = np.arange(norm.vmin, norm.vmax + .2, .2) + for i, lead_name in enumerate(df.columns[:-2]): # last two are lat lon + fig = plt.figure() + self._ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree()) + + self._ax.add_feature(cfeature.LAND.with_scale("50m")) + # self._ax.natural_earth_shp(resolution='50m') + self._ax.add_feature(cfeature.COASTLINE.with_scale("50m"), edgecolor='black') + # self._ax.add_feature(cfeature.LAKES.with_scale("50m")) + self._ax.add_feature(cfeature.OCEAN.with_scale("50m")) + self._ax.add_feature(cfeature.RIVERS.with_scale("50m")) + self._ax.add_feature(cfeature.BORDERS.with_scale("50m"), facecolor='none', edgecolor='black') + self._ax.scatter(df.lon.values, df.lat.values, c=df[lead_name], + transform=ccrs.PlateCarree(), + norm=norm, cmap=cmap) + self._gl = self._ax.gridlines(xlocs=range(0, 21, 5), ylocs=range(44, 59, 2), draw_labels=True) + self._gl.xformatter = LONGITUDE_FORMATTER + self._gl.yformatter = LATITUDE_FORMATTER + label = f"Skill Score: {lead_name.replace('-', 'vs.').replace('(t+', ' (').replace(')', 'd)')}" + # cax = fig.add_axes([self._ax.get_position().x0, + # self._ax.get_position().y0-.02, + # self._ax.get_position().width, + # 0.02]) + # cax = fig.add_axes([0.09, 0.06, 0.84, 0.02]) + self._cbar = fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), + orientation='horizontal', ticks=ticks, + label=label, + # cax=cax + ) + # self._ax.set_aspect('auto') + # plt.tight_layout() + # pdf_pages.savefig() + # close all open figures / plots + pdf_pages.savefig() + pdf_pages.close() + plt.close('all') + + def _plot(self, ncol: int = 2): + import cartopy.feature as cfeature + import cartopy.crs as ccrs + from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER + import string + base_plot_name = self.plot_name + for competitor in self.reference_models: + file_name = os.path.join(self.skill_score_report_path, + f"error_report_skill_score_{self.model_name}_-_{competitor}.csv" + ) + + self.plot_name = f"{base_plot_name}_{self.model_name}_-_{competitor}" + df = self.open_data(file_name) + + nrow = int(np.ceil(len(df.columns[:-2])/ncol)) + bounds = np.linspace(-1, 1, 100) + cmap = mpl.cm.coolwarm + norm = mpl.colors.BoundaryNorm(bounds, cmap.N, extend='both') + ticks = np.arange(norm.vmin, norm.vmax + .2, .2) + fig, self._axes = plt.subplots(nrows=nrow, ncols=ncol, subplot_kw={'projection': ccrs.PlateCarree()}) + for i, ax in enumerate(self._axes.reshape(-1)): # last two are lat lon + + sub_name = f"({string.ascii_lowercase[i]})" + lead_name = df.columns[i] + ax.add_feature(cfeature.LAND.with_scale("50m")) + ax.add_feature(cfeature.COASTLINE.with_scale("50m"), edgecolor='black') + ax.add_feature(cfeature.OCEAN.with_scale("50m")) + ax.add_feature(cfeature.RIVERS.with_scale("50m")) + ax.add_feature(cfeature.BORDERS.with_scale("50m"), facecolor='none', edgecolor='black') + ax.scatter(df.lon.values, df.lat.values, c=df[lead_name], + marker='.', + transform=ccrs.PlateCarree(), + norm=norm, cmap=cmap) + gl = ax.gridlines(xlocs=range(0, 21, 5), ylocs=range(44, 59, 2), draw_labels=True) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.top_labels = [] + gl.right_labels = [] + ax.text(0.01, 1.09, f'{sub_name} {lead_name.split("+")[1][:-1]}d', + verticalalignment='top', horizontalalignment='left', + transform=ax.transAxes, + color='black', + ) + label = f"Skill Score: {lead_name.replace('-', 'vs.').split('(')[0]}" + + fig.subplots_adjust(bottom=0.18) + cax = fig.add_axes([0.15, 0.1, 0.7, 0.02]) + self._cbar = fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), + orientation='horizontal', + ticks=ticks, + label=label, + cax=cax + ) + + fig.subplots_adjust(wspace=.001, hspace=.2) + self._save(bbox_inches="tight") + plt.close('all') + + + @staticmethod + def get_coords_from_index(name_string: str) -> List[float]: + """ + + :param name_string: + :type name_string: + :return: List of coords [lat, lon] + :rtype: List + """ + res = [float(frac.replace("_", ".")) for frac in name_string.split(sep="__")[1:]] + return res + if __name__ == "__main__": stations = ['DEBW107', 'DEBY081', 'DEBW013', 'DEBW076', 'DEBW087'] path = "../../testrun_network/forecasts" diff --git a/mlair/run_modules/post_processing.py b/mlair/run_modules/post_processing.py index 5985db43fcd35491122de9739de9f607c0e3909e..f71e55c8d2c5182d6c3e104758e0ffe42e148263 100644 --- a/mlair/run_modules/post_processing.py +++ b/mlair/run_modules/post_processing.py @@ -24,7 +24,7 @@ from mlair.model_modules.linear_model import OrdinaryLeastSquaredModel from mlair.model_modules import AbstractModelClass from mlair.plotting.postprocessing_plotting import PlotMonthlySummary, PlotClimatologicalSkillScore, \ PlotCompetitiveSkillScore, PlotTimeSeries, PlotFeatureImportanceSkillScore, PlotConditionalQuantiles, \ - PlotSeparationOfScales, PlotSampleUncertaintyFromBootstrap, PlotSectorialSkillScore + PlotSeparationOfScales, PlotSampleUncertaintyFromBootstrap, PlotSectorialSkillScore, PlotScoresOnMap from mlair.plotting.data_insight_plotting import PlotStationMap, PlotAvailability, PlotAvailabilityHistogram, \ PlotPeriodogram, PlotDataHistogram from mlair.run_modules.run_environment import RunEnvironment @@ -691,6 +691,16 @@ class PostProcessing(RunEnvironment): logging.error(f"Could not create plot PlotSampleUncertaintyFromBootstrap due to the following error: {e}" f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}") + try: + if "PlotScoresOnMap" in plot_list: + report_path = os.path.join(self.data_store.get("experiment_path"), "latex_report") + PlotScoresOnMap(plot_folder=self.plot_path, skill_score_report_path=report_path, + model_name=self.model_display_name, + reference_models=self.competitors+["persi, ols"]) + except Exception as e: + logging.error(f"Could not create plot PlotScoresOnMap due to the following error: {e}" + f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}") + def calculate_test_score(self): """Evaluate test score of model and save locally."""