diff --git a/mlair/helpers/statistics.py b/mlair/helpers/statistics.py
index 5c424aba0d4656441a5f66745ad6d09874bb4e2b..57143c9aed0e730c81adbed33f7ba62fea39b298 100644
--- a/mlair/helpers/statistics.py
+++ b/mlair/helpers/statistics.py
@@ -569,9 +569,10 @@ def create_n_bootstrap_realizations(data: xr.DataArray, dim_name_time: str, dim_
     """
     res_dims = [dim_name_boots]
     dims = list(data.dims)
-    coords = {dim_name_boots: range(n_boots), dim_name_model: data.coords[dim_name_model] }
+    other_dims = [v for v in dims if v in set(dims).difference([dim_name_time])]
+    coords = {dim_name_boots: range(n_boots), **{dim_name: data.coords[dim_name] for dim_name in other_dims}}
     if len(dims) > 1:
-        res_dims = res_dims + dims[1:]
+        res_dims = res_dims + other_dims
     res = xr.DataArray(np.nan, dims=res_dims, coords=coords)
     for boot in range(n_boots):
         res[boot] = (calculate_average(
diff --git a/mlair/plotting/postprocessing_plotting.py b/mlair/plotting/postprocessing_plotting.py
index 0cdfada5f201fa0cd7b7eb69c6bc135d53138f86..bd2012c3fe9f53e7e07bfb4bfc4cde096c2dc891 100644
--- a/mlair/plotting/postprocessing_plotting.py
+++ b/mlair/plotting/postprocessing_plotting.py
@@ -6,7 +6,8 @@ import logging
 import math
 import os
 import warnings
-from typing import Dict, List, Tuple
+from typing import Dict, List, Tuple, Union
+import itertools
 
 import matplotlib
 import matplotlib.pyplot as plt
@@ -538,9 +539,10 @@ class PlotCompetitiveSkillScore(AbstractPlotClass):  # pragma: no cover
 
     def _plot(self, single_model_comparison=False):
         """Plot skill scores of the comparisons."""
-        size = max([len(np.unique(self._data.comparison)), 6])
-        fig, ax = plt.subplots(figsize=(size, size * 0.8))
         data = self._filter_comparisons(self._data) if single_model_comparison is True else self._data
+        max_label_size = len(max(np.unique(data.comparison).tolist(), key=len))
+        size = max([len(np.unique(data.comparison)), 6])
+        fig, ax = plt.subplots(figsize=(size, 5 * max(0.8, max_label_size/20)))
         order = self._create_pseudo_order(data)
         sns.boxplot(x="comparison", y="data", hue="ahead", data=data, whis=1.5, ax=ax, palette="Blues_d",
                     showmeans=True, meanprops={"markersize": 3, "markeredgecolor": "k"}, flierprops={"marker": "."},
@@ -554,8 +556,10 @@ class PlotCompetitiveSkillScore(AbstractPlotClass):  # pragma: no cover
 
     def _plot_vertical(self, single_model_comparison=False):
         """Plot skill scores of the comparisons, but vertically aligned."""
-        fig, ax = plt.subplots()
         data = self._filter_comparisons(self._data) if single_model_comparison is True else self._data
+        max_label_size = len(max(np.unique(data.comparison).tolist(), key=len))
+        size = max([len(np.unique(data.comparison)), 6])
+        fig, ax = plt.subplots(figsize=(5 * max(0.8, max_label_size/20), size))
         order = self._create_pseudo_order(data)
         sns.boxplot(y="comparison", x="data", hue="ahead", data=data, whis=1.5, ax=ax, palette="Blues_d",
                     showmeans=True, meanprops={"markersize": 3, "markeredgecolor": "k"}, flierprops={"marker": "."},
@@ -1090,34 +1094,36 @@ class PlotSampleUncertaintyFromBootstrap(AbstractPlotClass):  # pragma: no cover
 
     def __init__(self, data: xr.DataArray, plot_folder: str = ".", model_type_dim: str = "type",
                  error_measure: str = "mse", error_unit: str = None, dim_name_boots: str = 'boots',
-                 block_length: str = None, model_name: str = "NN", model_indicator: str = "nn"):
+                 block_length: str = None, model_name: str = "NN", model_indicator: str = "nn",
+                 ahead_dim: str = "ahead", sampling: Union[str, Tuple[str]] = ""):
         super().__init__(plot_folder, "sample_uncertainty_from_bootstrap")
-        default_name = self.plot_name
+        self.default_plot_name = self.plot_name
         self.model_type_dim = model_type_dim
+        self.ahead_dim = ahead_dim
         self.error_measure = error_measure
         self.dim_name_boots = dim_name_boots
         self.error_unit = error_unit
         self.block_length = block_length
         self.model_name = model_name
+        self.sampling = {"daily": "d", "hourly": "H"}.get(sampling[1] if isinstance(sampling, tuple) else sampling, "")
         data = self.rename_model_indicator(data, model_name, model_indicator)
         self.prepare_data(data)
 
-        variants = [("_vertical", "v", False), ("_vertical_u_test", "v", True),
-                    ("_horizontal", "h", False), ("_horizontal_u_test", "h", True)]
+        # create all combinations to plot (h/v, utest/notest, single/multi)
+        variants = list(itertools.product(*[["v", "h"], [True, False], ["single", "multi"]]))
 
-        for name_tag, orientation, utest in variants:
-            self.plot_name = default_name + name_tag
-            self._plot(orientation=orientation, apply_u_test=utest)
+        # plot raw metric (mse)
+        for orientation, utest, agg_type in variants:
+            self._plot(orientation=orientation, apply_u_test=utest, agg_type=agg_type)
 
+        # plot root of metric (rmse)
         self._apply_root()
-        variants = [(tag+"_sqrt", ori, ut) for tag, ori, ut in variants]
-
-        for name_tag, orientation, utest in variants:
-            self.plot_name = default_name + name_tag
-            self._plot(orientation=orientation, apply_u_test=utest)
+        for orientation, utest, agg_type in variants:
+            self._plot(orientation=orientation, apply_u_test=utest, agg_type=agg_type, tag="_sqrt")
 
         self._data_table = None
         self._n_boots = None
+        self._factor = None
 
     @property
     def get_asteriks_from_mann_whitney_u_result(self):
@@ -1131,20 +1137,29 @@ class PlotSampleUncertaintyFromBootstrap(AbstractPlotClass):  # pragma: no cover
         return data
 
     def prepare_data(self, data: xr.DataArray):
-        data_table = data.to_pandas()
-        self._data_table = data_table[data_table.mean().sort_values().index]
-        self._n_boots = self._data_table.shape[0]
+        data_table = data.to_dataframe(self.model_type_dim).unstack()
+        factor = len(data.coords[self.ahead_dim]) if self.ahead_dim in data.dims else 1
+        self._data_table = data_table[data_table.mean().sort_values().index].droplevel(0, axis=1)
+        self._n_boots = int(self._data_table.shape[0] / factor)
+        self._factor = factor
 
     def _apply_root(self):
         self._data_table = np.sqrt(self._data_table)
         self.error_measure = f"root {self.error_measure}"
         self.error_unit = self.error_unit.replace("$^2$", "")
 
-    def _plot(self, orientation: str = "v", apply_u_test: bool = False):
+    def _plot(self, orientation: str = "v", apply_u_test: bool = False, agg_type="single", tag=""):
+        self.plot_name = self.default_plot_name + {"v": "_vertical", "h": "_horizontal"}[orientation] + \
+                         {True: "_u_test", False: ""}[apply_u_test] + "_" + agg_type + tag
+        if apply_u_test is True and agg_type == "multi":
+            return  # not implemented
         data_table = self._data_table
+        if self.ahead_dim not in data_table.index.names and agg_type == "multi":
+            return  # nothing to do
         n_boots = self._n_boots
         size = len(np.unique(data_table.columns))
-        asteriks = self.get_asteriks_from_mann_whitney_u_result
+        asteriks = self.get_asteriks_from_mann_whitney_u_result if apply_u_test is True else None
+        color_palette = sns.color_palette("Blues_d", self._factor).as_hex()
         if orientation == "v":
             figsize, width = (size, 5), 0.4
         elif orientation == "h":
@@ -1152,11 +1167,24 @@ class PlotSampleUncertaintyFromBootstrap(AbstractPlotClass):  # pragma: no cover
         else:
             raise ValueError(f"orientation must be `v' or `h' but is: {orientation}")
         fig, ax = plt.subplots(figsize=figsize)
-        sns.boxplot(data=data_table, ax=ax, whis=1.5, color="white",
-                    showmeans=True, meanprops={"markersize": 6, "markeredgecolor": "k"},
-                    flierprops={"marker": "o", "markerfacecolor": "black", "markeredgecolor": "none", "markersize": 3},
-                    boxprops={'facecolor': 'none', 'edgecolor': 'k'},
-                    width=width, orient=orientation)
+        if agg_type == "single":
+            if self.ahead_dim in data_table.index.names:
+                data_table = data_table.groupby(level=0).mean()
+            sns.boxplot(data=data_table, ax=ax, whis=1.5, color="white",
+                        showmeans=True, meanprops={"markersize": 6, "markeredgecolor": "k"},
+                        flierprops={"marker": "o", "markerfacecolor": "black", "markeredgecolor": "none", "markersize": 3},
+                        boxprops={'facecolor': 'none', 'edgecolor': 'k'}, width=width, orient=orientation)
+        else:
+            xy = {"x": self.model_type_dim, "y": 0} if orientation == "v" else {"x": 0, "y": self.model_type_dim}
+            sns.boxplot(data=data_table.stack(self.model_type_dim).reset_index(), ax=ax, whis=1.5, palette=color_palette,
+                        showmeans=True, meanprops={"markersize": 6, "markeredgecolor": "k", "markerfacecolor": "white"},
+                        flierprops={"marker": "o", "markerfacecolor": "black", "markeredgecolor": "none", "markersize": 3},
+                        boxprops={'edgecolor': 'k'}, width=.9, orient=orientation, **xy, hue=self.ahead_dim)
+
+            _labels = [str(i) + self.sampling for i in data_table.index.levels[1].values]
+            handles, _ = ax.get_legend_handles_labels()
+            ax.legend(handles, _labels)
+
         if orientation == "v":
             if apply_u_test:
                 ax = self.set_significance_bars(asteriks, ax, data_table, orientation)
diff --git a/mlair/run_modules/post_processing.py b/mlair/run_modules/post_processing.py
index 8b69034fa5d3b69b39ba74aa8a6bbab873c06cc8..8f6bf05d29b2534e4918d72aa59f91aace0ec982 100644
--- a/mlair/run_modules/post_processing.py
+++ b/mlair/run_modules/post_processing.py
@@ -111,7 +111,7 @@ class PostProcessing(RunEnvironment):
 
         # sample uncertainty
         if self.data_store.get("do_uncertainty_estimate", "postprocessing"):
-            self.estimate_sample_uncertainty()
+            self.estimate_sample_uncertainty(separate_ahead=True)
 
         # feature importance bootstraps
         if self.data_store.get("evaluate_feature_importance", "postprocessing"):
@@ -170,12 +170,27 @@ class PostProcessing(RunEnvironment):
         # store statistics
         if percentiles is None:
             percentiles = [.05, .1, .25, .5, .75, .9, .95]
-        df_descr = self.uncertainty_estimate.to_pandas().describe(percentiles=percentiles).astype("float32")
-        column_format = tables.create_column_format_for_tex(df_descr)
-        file_name = os.path.join(report_path, "uncertainty_estimate_statistics.%s")
-        tables.save_to_tex(report_path, file_name % "tex", column_format=column_format, df=df_descr)
-        tables.save_to_md(report_path, file_name % "md", df=df_descr)
-        df_descr.to_csv(file_name % "csv", sep=";")
+
+        for ahead_steps in ["single", "multi"]:
+            if ahead_steps == "single":
+                try:
+                    df_descr = self.uncertainty_estimate.to_pandas().describe(percentiles=percentiles).astype("float32")
+                except ValueError:
+                    df_descr = self.uncertainty_estimate.mean(self.ahead_dim).to_pandas().describe(percentiles=percentiles).astype("float32")
+            else:
+                if self.ahead_dim not in self.uncertainty_estimate.dims:
+                    continue
+                df_descr = self.uncertainty_estimate.to_dataframe(self.model_type_dim).unstack().groupby(level=self.ahead_dim).describe(
+                    percentiles=percentiles).astype("float32")
+                df_descr = df_descr.stack(-1)
+                df_descr = df_descr.reorder_levels(df_descr.index.names[::-1])
+                df_sorter = ["count", "mean", "std", "min", *[f"{round(p * 100)}%" for p in percentiles], "max"]
+                df_descr = df_descr.loc[df_sorter]
+            column_format = tables.create_column_format_for_tex(df_descr)
+            file_name = os.path.join(report_path, f"uncertainty_estimate_statistics_{ahead_steps}.%s")
+            tables.save_to_tex(report_path, file_name % "tex", column_format=column_format, df=df_descr)
+            tables.save_to_md(report_path, file_name % "md", df=df_descr)
+            df_descr.to_csv(file_name % "csv", sep=";")
 
     def calculate_block_mse(self, evaluate_competitors=True, separate_ahead=False, block_length="1m"):
         """
@@ -566,6 +581,18 @@ class PostProcessing(RunEnvironment):
             logging.error(f"Could not create plot PlotTimeSeries due to the following error:\n{sys.exc_info()[0]}\n"
                           f"{sys.exc_info()[1]}\n{sys.exc_info()[2]}\n{traceback.format_exc()}")
 
+        try:
+            if "PlotSampleUncertaintyFromBootstrap" in plot_list and self.uncertainty_estimate is not None:
+                block_length = self.data_store.get_default("block_length", default="1m", scope="uncertainty_estimate")
+                PlotSampleUncertaintyFromBootstrap(
+                    data=self.uncertainty_estimate, plot_folder=self.plot_path, model_type_dim=self.model_type_dim,
+                    dim_name_boots=self.uncertainty_estimate_boot_dim, error_measure="mean squared error",
+                    error_unit=r"ppb$^2$", block_length=block_length, model_name=self.model_display_name,
+                    model_indicator=self.forecast_indicator, sampling=self._sampling)
+        except Exception as e:
+            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 "PlotStationMap" in plot_list:
                 if self.data_store.get("hostname")[:2] in self.data_store.get("hpc_hosts") or self.data_store.get(
@@ -602,15 +629,6 @@ class PostProcessing(RunEnvironment):
             logging.error(f"Could not create plot PlotAvailabilityHistogram due to the following error: {e}"
                           f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}")
 
-        try:
-            if "PlotPeriodogram" in plot_list:
-                PlotPeriodogram(self.train_data, plot_folder=self.plot_path, time_dim=time_dim,
-                                variables_dim=target_dim, sampling=self._sampling,
-                                use_multiprocessing=use_multiprocessing)
-        except Exception as e:
-            logging.error(f"Could not create plot PlotPeriodogram due to the following error: {e}"
-                          f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}")
-
         try:
             if "PlotDataHistogram" in plot_list:
                 upsampling = self.data_store.get_default("upsampling", scope="train", default=False)
@@ -622,15 +640,12 @@ class PostProcessing(RunEnvironment):
                           f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}")
 
         try:
-            if "PlotSampleUncertaintyFromBootstrap" in plot_list and self.uncertainty_estimate is not None:
-                block_length = self.data_store.get_default("block_length", default="1m", scope="uncertainty_estimate")
-                PlotSampleUncertaintyFromBootstrap(
-                    data=self.uncertainty_estimate, plot_folder=self.plot_path, model_type_dim=self.model_type_dim,
-                    dim_name_boots=self.uncertainty_estimate_boot_dim, error_measure="mean squared error",
-                    error_unit=r"ppb$^2$", block_length=block_length, model_name=self.model_display_name,
-                    model_indicator=self.forecast_indicator)
+            if "PlotPeriodogram" in plot_list:
+                PlotPeriodogram(self.train_data, plot_folder=self.plot_path, time_dim=time_dim,
+                                variables_dim=target_dim, sampling=self._sampling,
+                                use_multiprocessing=use_multiprocessing)
         except Exception as e:
-            logging.error(f"Could not create plot PlotSampleUncertaintyFromBootstrap due to the following error: {e}"
+            logging.error(f"Could not create plot PlotPeriodogram due to the following error: {e}"
                           f"\n{sys.exc_info()[0]}\n{sys.exc_info()[1]}\n{sys.exc_info()[2]}")
         
     @TimeTrackingWrapper