diff --git a/isimip_qa/main.py b/isimip_qa/main.py index f9cbdd1..e47ac6f 100644 --- a/isimip_qa/main.py +++ b/isimip_qa/main.py @@ -36,8 +36,6 @@ def get_parser(): parser.add_argument('-p', '--periods', dest='periods', default=None, help='Extract only specific periods (comma seperated, format: YYYY_YYYY)') - parser.add_argument('-g', '--grid', type=int, dest='grid', default=0, choices=[0, 1, 2], - help='Number of dimensions of the plot grid [default: 0, i.e. one plot]') parser.add_argument('-f', '--force', dest='force', action='store_true', default=False, help='Always run extractions') parser.add_argument('-l', '--load', dest='load', action='store_true', default=False, @@ -56,6 +54,10 @@ def get_parser(): help='Treat these placeholders as primary and plot them in color [default: all]') parser.add_argument('--gridarea', dest='gridarea', default=None, help='Use a CDO gridarea file instead of computing the gridarea when computing means') + parser.add_argument('--grid', type=int, dest='grid', default=0, choices=[0, 1, 2], + help='Number of dimensions of the plot grid [default: 0, i.e. no grid]') + parser.add_argument('--figs', type=int, dest='figs', default=0, + help='Number of placeholders which generate seperate figures [default: 0]') parser.add_argument('--ymin', type=float, dest='ymin', default=None, help='Fixed minimal y value for plots.') @@ -152,5 +154,5 @@ def main(): ): plot = plot_class(extraction_class, datasets, region, period, path=settings.PATHS[0].stem, dimensions=settings.PLACEHOLDERS, - grid=settings.GRID) + grid=settings.GRID, figs=settings.FIGS) plot.create() diff --git a/isimip_qa/mixins/plots.py b/isimip_qa/mixins/plots.py index 452cc42..4777f5b 100644 --- a/isimip_qa/mixins/plots.py +++ b/isimip_qa/mixins/plots.py @@ -1,4 +1,5 @@ import logging +import sys from itertools import chain, product from pathlib import Path @@ -18,7 +19,7 @@ def write(self, fig, path): logger.info(f'write {path}') try: - fig.savefig(path, bbox_inches='tight') + fig.savefig(path) except ValueError as e: logger.error(f'could not save {path} ({e})') plt.close() @@ -37,25 +38,32 @@ class GridPlotMixin: ] linestyles = ['solid', 'dashed', 'dashdot', 'dotted'] markers = ['.', '*', 'D', 's'] + max_dimensions = sys.maxsize - def __init__(self, *args, path=None, dimensions=None, grid=2, **kwargs): + def __init__(self, *args, path=None, dimensions=None, grid=0, figs=0, **kwargs): self.path = Path(path) if path else None self.dimensions = dimensions self.grid = grid + self.figs = figs + if self.dimensions: - self.keys = list(self.dimensions.keys()) - self.values = list(self.dimensions.values()) - self.permutations = list(product(*self.values)) + self.dimensions_keys = list(self.dimensions.keys()) + self.dimensions_values = list(self.dimensions.values()) + self.dimensions_len = len(self.dimensions_keys) + self.permutations = list(product(*self.dimensions_values)) self.styles = self.get_styles() + self.figs = max(self.figs, self.dimensions_len - self.grid - self.max_dimensions) + super().__init__(*args, **kwargs) def get_figure(self, nrows, ncols, ratio=1): - fig, axs = plt.subplots(nrows, ncols, squeeze=False, figsize=(6 * ratio * ncols, 6 * nrows)) + fig, axs = plt.subplots(nrows, ncols, squeeze=False, figsize=(6 * ratio * ncols, 6 * nrows), + constrained_layout=True) for ax in chain.from_iterable(axs): ax.tick_params(bottom=False, labelbottom=False, left=False, labelleft=False) return fig, axs - def get_path(self, ifig): + def get_figure_path(self, ifig=0): if self.path is None: return None @@ -68,18 +76,18 @@ def get_path(self, ifig): if self.dimensions: placeholders = {} - for j, key in enumerate(self.keys): - if ifig is None or j < self.grid: - primary_values = [value for value in self.values[j] if value in settings.PRIMARY] + for j, key in enumerate(self.dimensions_keys): + if (j < self.grid) or (j < self.dimensions_len - self.figs): + # for the first dimensions, which are not figure dimensions, combine the values + primary_values = [value for value in self.dimensions_values[j] if value in settings.PRIMARY] if primary_values: values_strings = primary_values - elif len(self.values[j]) < 10: - values_strings = self.values[j] + elif len(self.dimensions_values[j]) < 10: + values_strings = self.dimensions_values[j] else: values_strings = ['various'] else: - # this works because for j > self.grid, the permutations - # only repeat with a "period" of nfig + # for the last self.figs dimensions, which generate seperate figures, take seperate values values_strings = [self.permutations[ifig][j]] placeholders[key] = '+'.join(values_strings).lower() @@ -102,21 +110,19 @@ def get_path(self, ifig): return settings.PLOTS_PATH / self.path.with_name(stem).with_suffix(suffix) - def get_grid(self, figs=False): - grid = [1, 1, 1] if figs else [1, 1] + def get_grid(self): + grid = [1, 1, 1] if self.dimensions: - for j, key in enumerate(self.keys): + for j, key in enumerate(self.dimensions_keys): ndim = len(self.dimensions[key]) if j < self.grid: + # the grid dimensions generate rows and columns grid[j] = ndim - - if figs: - if j == self.grid: - grid[-1] = len(self.values[j]) - elif j > self.grid: - grid[-1] *= len(self.values[j]) + elif (j >= self.dimensions_len - self.figs): + # the last self.figs dimensions multiply the number of seperate figures + grid[-1] *= len(self.dimensions_values[j]) return reversed(grid) @@ -126,16 +132,25 @@ def get_grid_indexes(self, i): if self.dimensions: permutation = self.permutations[i] - for j, key in enumerate(self.keys): + for j, key in enumerate(self.dimensions_keys): value = permutation[j] value_index = self.dimensions[key].index(value) if j < self.grid: + # the first dimensions indicate the column and the row grid_indexes[j] = value_index - elif j == len(self.keys) - 1: - grid_indexes[-1] += value_index - else: - grid_indexes[-1] += value_index * len(self.values[j+1]) + elif self.figs > 0: + # the figure index is computed like this: + # lets assume self.figs = 3 and i3,i2,i1 are the indexes of dimensions d3,d2,d1 + # the figure index is i1 + i2 * len(d1) + i3 * len(d1) * len(d2) + if j == self.dimensions_len - 1: + # the last value_index just adds to the figure index + grid_indexes[-1] += value_index + elif j >= self.dimensions_len - self.figs: + # the other value_indexes need be multiplied by the lenghts of the dimensions to the right + for values in self.dimensions_values[j+1:]: + value_index *= len(values) + grid_indexes[-1] += value_index return reversed(grid_indexes) @@ -160,7 +175,7 @@ def get_primary(self, i): if self.dimensions and settings.PRIMARY: permutation = self.permutations[i] - for j, key in enumerate(self.keys): + for j, key in enumerate(self.dimensions_keys): if permutation[j] in settings.PRIMARY: return True @@ -185,7 +200,6 @@ def get_subplots(self): continue try: - attrs = self.get_attrs(dataset) except ExtractionNotFound: attrs = {} @@ -198,15 +212,13 @@ def get_subplots(self): var=var, label=self.get_label(index), title=self.get_title(index), - full_title=self.get_full_title(index), color=self.get_color(index), linestyle=self.get_linestyle(index), marker=self.get_marker(index), ifig=ifig, irow=irow, icol=icol, - primary=self.get_primary(index), - path=self.get_path(ifig) + primary=self.get_primary(index) ) subplots.append(subplot) @@ -219,10 +231,6 @@ def get_df(self, dataset): def get_attrs(self, dataset): raise NotImplementedError - def get_full_title(self, i): - if self.dimensions: - return ' '.join(self.permutations[i]) - def get_title(self, i): if self.dimensions: return ' '.join(self.permutations[i][:self.grid]) diff --git a/isimip_qa/plots/cdf.py b/isimip_qa/plots/cdf.py index 3abc704..9b7f795 100644 --- a/isimip_qa/plots/cdf.py +++ b/isimip_qa/plots/cdf.py @@ -23,34 +23,39 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, - linestyle=sp.linestyle, label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel(sp.attrs.get('standard_name')) - ax.set_ylabel('CDF') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, + linestyle=sp.linestyle, label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel(sp.attrs.get('standard_name')) + ax.set_ylabel('CDF') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/plots/daily.py b/isimip_qa/plots/daily.py index 022f499..d0fcc26 100644 --- a/isimip_qa/plots/daily.py +++ b/isimip_qa/plots/daily.py @@ -22,33 +22,38 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.plot(sp.df.index, sp.df[sp.var], label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.plot(sp.df.index, sp.df[sp.var], color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel('date') - ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.plot(sp.df.index, sp.df[sp.var], label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.plot(sp.df.index, sp.df[sp.var], color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel('date') + ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/plots/dayofyear.py b/isimip_qa/plots/dayofyear.py index 29c9b60..bf71d87 100644 --- a/isimip_qa/plots/dayofyear.py +++ b/isimip_qa/plots/dayofyear.py @@ -22,34 +22,39 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, - linestyle=sp.linestyle, label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel('day of the year') - ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, + linestyle=sp.linestyle, label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel('day of the year') + ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/plots/map.py b/isimip_qa/plots/map.py index 8f76db7..3c24d27 100644 --- a/isimip_qa/plots/map.py +++ b/isimip_qa/plots/map.py @@ -15,6 +15,7 @@ class MapPlot(FigurePlotMixin, GridPlotMixin, Plot): specifier = 'map' extractions = ['meanmap', 'countmap'] + max_dimensions = 0 def get_df(self, dataset): extraction = self.extraction_class(dataset, self.region, self.period) @@ -26,10 +27,11 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: # get the extension of the valid data for all datasets - lonmin, lonmax, latmin, latmax, ratio = -180, 180, -90, 90, 3.0 + lonmin, lonmax, latmin, latmax, ratio = -180, 180, -90, 90, 2.0 if self.region.specifier != 'global': for sp in subplots: sp_lon = sp.df['lon'].unique() @@ -50,46 +52,47 @@ def create(self): ratio = max((lonmax - lonmin) / (latmax - latmin), 1.0) - nfigs, nrows, ncols = self.get_grid(figs=True) - for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) fig_subplots = [sp for sp in subplots if sp.ifig == ifig] - fig, axs = self.get_figure(nrows, ncols, ratio=ratio) - plt.subplots_adjust(top=1.1) + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols, ratio=ratio) + + cbars = [] + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) - cbars = [] - for sp in fig_subplots: - ax = axs.item(sp.irow, sp.icol) + vmin = self.get_vmin(sp, subplots) + vmax = self.get_vmax(sp, subplots) - vmin = self.get_vmin(sp, subplots) - vmax = self.get_vmax(sp, subplots) + df_pivot = sp.df.pivot(index='lat', columns=['lon'], values=sp.var) + df_pivot = df_pivot.reindex(index=df_pivot.index[::-1]) - df_pivot = sp.df.pivot(index='lat', columns=['lon'], values=sp.var) - df_pivot = df_pivot.reindex(index=df_pivot.index[::-1]) + # truncate the dataframe at the extensions + df_pivot = df_pivot.truncate(before=latmin, after=latmax) + df_pivot = df_pivot.truncate(before=lonmin, after=lonmax, axis=1) - # truncate the dataframe at the extensions - df_pivot = df_pivot.truncate(before=latmin, after=latmax) - df_pivot = df_pivot.truncate(before=lonmin, after=lonmax, axis=1) + im = ax.imshow(df_pivot, interpolation='nearest', label=sp.label, + extent=[lonmin, lonmax, latmin, latmax], + vmin=vmin, vmax=vmax, cmap=settings.CMAP) - im = ax.imshow(df_pivot, interpolation='nearest', label=sp.label, - extent=[lonmin, lonmax, latmin, latmax], - vmin=vmin, vmax=vmax, cmap=settings.CMAP) + if sp.title and sp.label: + ax.set_title(f'{sp.title} {sp.label}', fontsize=10) - ax.set_title(sp.full_title, fontsize=10) - ax.set_xlabel('lon', fontsize=10) - ax.set_ylabel('lat', fontsize=10) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + ax.set_xlabel('lon', fontsize=10) + ax.set_ylabel('lat', fontsize=10) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - if ax not in cbars: - cbar = plt.colorbar(im, ax=ax) - cbar.set_label(f'{sp.var} [{sp.attrs.get("units")}]') - cbar.set_ticks([vmin, vmax]) - cbars.append(ax) + if ax not in cbars: + cbar = plt.colorbar(im, ax=ax) + cbar.set_label(f'{sp.var} [{sp.attrs.get("units")}]') + cbar.set_ticks([vmin, vmax]) + cbars.append(ax) - if self.path: - self.write(fig, sp.path) - else: - self.show() - else: - logger.info('nothing to plot') + if fig_path: + self.write(fig, fig_path) + else: + self.show() + else: + logger.info('nothing to plot') diff --git a/isimip_qa/plots/monthofyear.py b/isimip_qa/plots/monthofyear.py index 6b51f71..697be07 100644 --- a/isimip_qa/plots/monthofyear.py +++ b/isimip_qa/plots/monthofyear.py @@ -22,34 +22,39 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, - linestyle=sp.linestyle, label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel('month of the year') - ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, + linestyle=sp.linestyle, label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel('month of the year') + ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/plots/pdf.py b/isimip_qa/plots/pdf.py index 7255d68..f8bf046 100644 --- a/isimip_qa/plots/pdf.py +++ b/isimip_qa/plots/pdf.py @@ -23,34 +23,39 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, - linestyle=sp.linestyle, label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel(sp.attrs.get("standard_name")) - ax.set_ylabel('PDF') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, + linestyle=sp.linestyle, label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel(sp.attrs.get("standard_name")) + ax.set_ylabel('PDF') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/plots/yearly.py b/isimip_qa/plots/yearly.py index 829d206..9247b66 100644 --- a/isimip_qa/plots/yearly.py +++ b/isimip_qa/plots/yearly.py @@ -22,34 +22,39 @@ def get_attrs(self, dataset): def create(self): logger.info(f'plot {self.region.specifier} {self.extraction_class.specifier} {self.specifier}') + nfigs, nrows, ncols = self.get_grid() subplots = self.get_subplots() if subplots: - nrows, ncols = self.get_grid() - fig, axs = self.get_figure(nrows, ncols) - - for sp in subplots: - ax = axs.item(sp.irow, sp.icol) - - ymin = self.get_ymin(sp, subplots) - ymax = self.get_ymax(sp, subplots) - - if sp.primary: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, - linestyle=sp.linestyle, label=sp.label, zorder=10) - if sp.label: - ax.legend(loc='lower left').set_zorder(20) - else: - ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) - - ax.set_title(sp.title) - ax.set_xlabel('date') - ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') - ax.set_ylim(ymin, ymax) - ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) - - if self.path: - self.write(fig, sp.path) - else: - self.show() + for ifig in range(nfigs): + fig_path = self.get_figure_path(ifig) + fig_subplots = [sp for sp in subplots if sp.ifig == ifig] + + if fig_subplots: + fig, axs = self.get_figure(nrows, ncols) + + for sp in fig_subplots: + ax = axs.item(sp.irow, sp.icol) + + ymin = self.get_ymin(sp, subplots) + ymax = self.get_ymax(sp, subplots) + + if sp.primary: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color=sp.color, + linestyle=sp.linestyle, label=sp.label, zorder=10) + if sp.label: + ax.legend(loc='lower left').set_zorder(20) + else: + ax.step(sp.df.index, sp.df[sp.var], where='mid', color='grey', zorder=0) + + ax.set_title(sp.title) + ax.set_xlabel('date') + ax.set_ylabel(f'{sp.var} [{sp.attrs.get("units")}]') + ax.set_ylim(ymin, ymax) + ax.tick_params(bottom=True, labelbottom=True, left=True, labelleft=True) + + if fig_path: + self.write(fig, fig_path) + else: + self.show() else: logger.info('nothing to plot') diff --git a/isimip_qa/tests/test_plots.py b/isimip_qa/tests/test_plots.py index dd951e5..343c7d1 100644 --- a/isimip_qa/tests/test_plots.py +++ b/isimip_qa/tests/test_plots.py @@ -40,13 +40,15 @@ def test_plot(settings, dataset_path, extraction_class, plot_class): plot = plot_class(extraction_class, [dataset], path=dataset.path.stem) plot.create() - for sp in plot.get_subplots(): - assert sp.path.exists() + nfigs, nrows, ncols = plot.get_grid() + for ifig in range(nfigs): + fig_path = plot.get_figure_path(ifig) + assert fig_path.exists() - img = imread(sp.path, as_gray=True) + img = imread(fig_path, as_gray=True) template_path = Path('testing').joinpath('plots') \ - .joinpath(sp.path.relative_to(settings.PLOTS_PATH)) + .joinpath(fig_path.relative_to(settings.PLOTS_PATH)) template_img = imread(template_path, as_gray=True) similarity = ssim(img, template_img, data_range=template_img.max() - template_img.min()) diff --git a/testing/plots/linear_global_count_daily.png b/testing/plots/linear_global_count_daily.png index b0671ab..20d7eeb 100644 Binary files a/testing/plots/linear_global_count_daily.png and b/testing/plots/linear_global_count_daily.png differ diff --git a/testing/plots/linear_global_countmap_map.png b/testing/plots/linear_global_countmap_map.png index b4137a0..1c083b9 100644 Binary files a/testing/plots/linear_global_countmap_map.png and b/testing/plots/linear_global_countmap_map.png differ diff --git a/testing/plots/linear_global_histogram_cdf.png b/testing/plots/linear_global_histogram_cdf.png index 488663d..f782cac 100644 Binary files a/testing/plots/linear_global_histogram_cdf.png and b/testing/plots/linear_global_histogram_cdf.png differ diff --git a/testing/plots/linear_global_histogram_pdf.png b/testing/plots/linear_global_histogram_pdf.png index 0c1878c..62d52f7 100644 Binary files a/testing/plots/linear_global_histogram_pdf.png and b/testing/plots/linear_global_histogram_pdf.png differ diff --git a/testing/plots/linear_global_mean_daily.png b/testing/plots/linear_global_mean_daily.png index e1b4d2c..90aab6e 100644 Binary files a/testing/plots/linear_global_mean_daily.png and b/testing/plots/linear_global_mean_daily.png differ diff --git a/testing/plots/linear_global_mean_dayofyear.png b/testing/plots/linear_global_mean_dayofyear.png index dbe7b4d..2743493 100644 Binary files a/testing/plots/linear_global_mean_dayofyear.png and b/testing/plots/linear_global_mean_dayofyear.png differ diff --git a/testing/plots/linear_global_mean_monthofyear.png b/testing/plots/linear_global_mean_monthofyear.png index 640b492..2f42898 100644 Binary files a/testing/plots/linear_global_mean_monthofyear.png and b/testing/plots/linear_global_mean_monthofyear.png differ diff --git a/testing/plots/linear_global_mean_yearly.png b/testing/plots/linear_global_mean_yearly.png index 14dd660..4961a50 100644 Binary files a/testing/plots/linear_global_mean_yearly.png and b/testing/plots/linear_global_mean_yearly.png differ diff --git a/testing/plots/linear_global_meanmap_map.png b/testing/plots/linear_global_meanmap_map.png index 8db6ac1..9aee0b3 100644 Binary files a/testing/plots/linear_global_meanmap_map.png and b/testing/plots/linear_global_meanmap_map.png differ diff --git a/testing/plots/mask_global_count_daily.png b/testing/plots/mask_global_count_daily.png index c6b6860..5a871dd 100644 Binary files a/testing/plots/mask_global_count_daily.png and b/testing/plots/mask_global_count_daily.png differ diff --git a/testing/plots/mask_global_countmap_map.png b/testing/plots/mask_global_countmap_map.png index 841daf6..a065f67 100644 Binary files a/testing/plots/mask_global_countmap_map.png and b/testing/plots/mask_global_countmap_map.png differ diff --git a/testing/plots/mask_global_histogram_cdf.png b/testing/plots/mask_global_histogram_cdf.png index 0a4ee0c..6487146 100644 Binary files a/testing/plots/mask_global_histogram_cdf.png and b/testing/plots/mask_global_histogram_cdf.png differ diff --git a/testing/plots/mask_global_histogram_pdf.png b/testing/plots/mask_global_histogram_pdf.png index 5401f8f..7621f3e 100644 Binary files a/testing/plots/mask_global_histogram_pdf.png and b/testing/plots/mask_global_histogram_pdf.png differ diff --git a/testing/plots/mask_global_mean_daily.png b/testing/plots/mask_global_mean_daily.png index 0ea3dba..5ea6336 100644 Binary files a/testing/plots/mask_global_mean_daily.png and b/testing/plots/mask_global_mean_daily.png differ diff --git a/testing/plots/mask_global_mean_dayofyear.png b/testing/plots/mask_global_mean_dayofyear.png index 8553f09..f8706b8 100644 Binary files a/testing/plots/mask_global_mean_dayofyear.png and b/testing/plots/mask_global_mean_dayofyear.png differ diff --git a/testing/plots/mask_global_mean_monthofyear.png b/testing/plots/mask_global_mean_monthofyear.png index 4c1d9fd..cb5b812 100644 Binary files a/testing/plots/mask_global_mean_monthofyear.png and b/testing/plots/mask_global_mean_monthofyear.png differ diff --git a/testing/plots/mask_global_mean_yearly.png b/testing/plots/mask_global_mean_yearly.png index cc1b548..952e7e2 100644 Binary files a/testing/plots/mask_global_mean_yearly.png and b/testing/plots/mask_global_mean_yearly.png differ diff --git a/testing/plots/mask_global_meanmap_map.png b/testing/plots/mask_global_meanmap_map.png index f4b533a..c44e303 100644 Binary files a/testing/plots/mask_global_meanmap_map.png and b/testing/plots/mask_global_meanmap_map.png differ diff --git a/testing/plots/point_global_count_daily.png b/testing/plots/point_global_count_daily.png index b0671ab..20d7eeb 100644 Binary files a/testing/plots/point_global_count_daily.png and b/testing/plots/point_global_count_daily.png differ diff --git a/testing/plots/point_global_countmap_map.png b/testing/plots/point_global_countmap_map.png index b4137a0..1c083b9 100644 Binary files a/testing/plots/point_global_countmap_map.png and b/testing/plots/point_global_countmap_map.png differ diff --git a/testing/plots/point_global_histogram_cdf.png b/testing/plots/point_global_histogram_cdf.png index 63dbdc4..0d10937 100644 Binary files a/testing/plots/point_global_histogram_cdf.png and b/testing/plots/point_global_histogram_cdf.png differ diff --git a/testing/plots/point_global_histogram_pdf.png b/testing/plots/point_global_histogram_pdf.png index 79035a5..0a47609 100644 Binary files a/testing/plots/point_global_histogram_pdf.png and b/testing/plots/point_global_histogram_pdf.png differ diff --git a/testing/plots/point_global_mean_daily.png b/testing/plots/point_global_mean_daily.png index dfe1810..5b2e545 100644 Binary files a/testing/plots/point_global_mean_daily.png and b/testing/plots/point_global_mean_daily.png differ diff --git a/testing/plots/point_global_mean_dayofyear.png b/testing/plots/point_global_mean_dayofyear.png index 37da8b4..ceb4704 100644 Binary files a/testing/plots/point_global_mean_dayofyear.png and b/testing/plots/point_global_mean_dayofyear.png differ diff --git a/testing/plots/point_global_mean_monthofyear.png b/testing/plots/point_global_mean_monthofyear.png index 969a2e4..6618d27 100644 Binary files a/testing/plots/point_global_mean_monthofyear.png and b/testing/plots/point_global_mean_monthofyear.png differ diff --git a/testing/plots/point_global_mean_yearly.png b/testing/plots/point_global_mean_yearly.png index 1354868..0ddbe9a 100644 Binary files a/testing/plots/point_global_mean_yearly.png and b/testing/plots/point_global_mean_yearly.png differ diff --git a/testing/plots/point_global_meanmap_map.png b/testing/plots/point_global_meanmap_map.png index 1d40c95..2da7e18 100644 Binary files a/testing/plots/point_global_meanmap_map.png and b/testing/plots/point_global_meanmap_map.png differ diff --git a/testing/plots/sine_global_count_daily.png b/testing/plots/sine_global_count_daily.png index b0671ab..20d7eeb 100644 Binary files a/testing/plots/sine_global_count_daily.png and b/testing/plots/sine_global_count_daily.png differ diff --git a/testing/plots/sine_global_countmap_map.png b/testing/plots/sine_global_countmap_map.png index b4137a0..1c083b9 100644 Binary files a/testing/plots/sine_global_countmap_map.png and b/testing/plots/sine_global_countmap_map.png differ diff --git a/testing/plots/sine_global_histogram_cdf.png b/testing/plots/sine_global_histogram_cdf.png index 86852c2..3a8ea9b 100644 Binary files a/testing/plots/sine_global_histogram_cdf.png and b/testing/plots/sine_global_histogram_cdf.png differ diff --git a/testing/plots/sine_global_histogram_pdf.png b/testing/plots/sine_global_histogram_pdf.png index a18c12a..cab9582 100644 Binary files a/testing/plots/sine_global_histogram_pdf.png and b/testing/plots/sine_global_histogram_pdf.png differ diff --git a/testing/plots/sine_global_mean_daily.png b/testing/plots/sine_global_mean_daily.png index d62376f..938bcf6 100644 Binary files a/testing/plots/sine_global_mean_daily.png and b/testing/plots/sine_global_mean_daily.png differ diff --git a/testing/plots/sine_global_mean_dayofyear.png b/testing/plots/sine_global_mean_dayofyear.png index ecf3534..205604a 100644 Binary files a/testing/plots/sine_global_mean_dayofyear.png and b/testing/plots/sine_global_mean_dayofyear.png differ diff --git a/testing/plots/sine_global_mean_monthofyear.png b/testing/plots/sine_global_mean_monthofyear.png index ddfa764..f853a2c 100644 Binary files a/testing/plots/sine_global_mean_monthofyear.png and b/testing/plots/sine_global_mean_monthofyear.png differ diff --git a/testing/plots/sine_global_mean_yearly.png b/testing/plots/sine_global_mean_yearly.png index 8eed73d..f1d3c0a 100644 Binary files a/testing/plots/sine_global_mean_yearly.png and b/testing/plots/sine_global_mean_yearly.png differ diff --git a/testing/plots/sine_global_meanmap_map.png b/testing/plots/sine_global_meanmap_map.png index b4137a0..1c083b9 100644 Binary files a/testing/plots/sine_global_meanmap_map.png and b/testing/plots/sine_global_meanmap_map.png differ