smashbox.init.multimodel_statistics

Created on Tue Nov 4 13:23:42 2025

@author: maxime

  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3"""
  4Created on Tue Nov  4 13:23:42 2025
  5
  6@author: maxime
  7"""
  8
  9from smashbox.stats import mystats
 10import numpy as np
 11
 12
 13class multimodel_statistics:
 14    """
 15    Class which handle computation of statistics for multi-model containers. Statistics for each model container must be calculated first. Mean, median, variance, min, max and the distribution of every scores of each model containers are computed.
 16    """
 17
 18    def __init__(self, parent_class):
 19        self._parent_class = parent_class
 20        """_parent_class attribute stores the parent_class src.init.smashbox.SmashBox() 
 21        to be able to access to the result of any model"""
 22
 23        self.multimodel_misfit_stats = _copy_stats_attr(mystats.misfit_results())
 24        """Attribute misfit_stats_results stores the results of the multimodel statistics for the misfit."""
 25
 26        self.multimodel_quantile_stats = None
 27        """Attribute quantile_stats_results store the results of the multimodel statistics for the spatial quantile"""
 28
 29        self.multimodel_spatial_stats = _copy_stats_attr(mystats.spatial_stats_results())
 30        """Atrribute spatial_stats_results store the results of the multimodel statistics for the spatial results"""
 31
 32        self.multimodel_outlets_stats_sim = _copy_stats_attr(
 33            mystats.outlets_stats_results()
 34        )
 35        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  simulted outlets stats"""
 36
 37        self.multimodel_outlets_stats_obs = _copy_stats_attr(
 38            mystats.outlets_stats_results()
 39        )
 40        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  observed outlets stats"""
 41
 42    def _get_model_list(self):
 43
 44        model_list = list()
 45
 46        for key, attr in self._parent_class.__dict__.items():
 47
 48            if hasattr(attr, "mysmashmodel"):
 49
 50                current_model = getattr(self._parent_class, key)
 51
 52                if (
 53                    current_model._myparam.param.bbox
 54                    == self._parent_class.myparam.param.bbox
 55                ):
 56                    model_list.append(key)
 57
 58        return model_list
 59
 60    def compute_multimodel_statistics(self, model_list=None):
 61        """
 62        Compute the multimodel statistics: mean, median, variance, min, max and the distribution. These statistics
 63        are computed over all models containers which contains statistics and match the bounding box in object myparam.
 64
 65        :param model_list: list of the model container, defaults to None
 66        :type model_list: list, optional
 67
 68        """
 69
 70        if model_list is None:
 71            model_list = self._get_model_list()
 72
 73        self.compute_multimodel_statistics_misfit()
 74        self.compute_multimodel_statistics_outlets()
 75        self.compute_multimodel_statistics_outlets(obs=False)
 76        self.compute_multimodel_statistics_outlets(obs=True)
 77        self.compute_multimodel_statistics_spatial()
 78        self.compute_multimodel_statistics_quantile()
 79
 80    def compute_multimodel_statistics_misfit(self, model_list=None):
 81        """
 82        Compute the multimodel statistics for the misfit criteria.
 83        """
 84        if model_list is None:
 85            model_list = self._get_model_list()
 86
 87        dict_results = {}
 88        for model in model_list:
 89            current_model = getattr(self._parent_class, model)
 90
 91            misfit_list = current_model.mystats.misfit_stats.results.__dict__
 92
 93            for misfit, results in misfit_list.items():
 94
 95                if results is None:
 96
 97                    results = (
 98                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
 99                    )
100                    print(f"</> Warning misfit {misfit} is None for model {model}.")
101
102                if misfit in dict_results.keys():
103                    dict_results[misfit] = np.stack(
104                        (dict_results[misfit], np.array(results))
105                    )
106                else:
107                    dict_results.update({misfit: np.array(results)})
108
109        for key, values in dict_results.items():
110            stats = getattr(self.multimodel_misfit_stats, key)
111            stats._fill_stats_attributes(values)
112
113    def compute_multimodel_statistics_outlets(self, model_list=None, obs=False):
114        """
115        Compute the multimodel statistics for the outlets criteria.
116        """
117        if model_list is None:
118            model_list = self._get_model_list()
119
120        dict_results = {}
121        for model in model_list:
122            current_model = getattr(self._parent_class, model)
123
124            if obs is False:
125                misfit_list = current_model.mystats.outlets_stats.results_sim.__dict__
126            else:
127                misfit_list = current_model.mystats.outlets_stats.results_obs.__dict__
128
129            for misfit, results in misfit_list.items():
130
131                if results is None:
132
133                    results = (
134                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
135                    )
136                    print(f"</> Warning misfit {misfit} is None for model {model}.")
137
138                if misfit in dict_results.keys():
139                    dict_results[misfit] = np.stack(
140                        (dict_results[misfit], np.array(results))
141                    )
142                else:
143                    dict_results.update({misfit: np.array(results)})
144
145        for key, values in dict_results.items():
146            if obs is False:
147                stats = getattr(self.multimodel_outlets_stats_sim, key)
148            else:
149                stats = getattr(self.multimodel_outlets_stats_obs, key)
150            stats._fill_stats_attributes(values)
151
152    def compute_multimodel_statistics_spatial(self, model_list=None):
153        """
154        Compute the multimodel statistics for the spatial statisctics.
155        """
156        if model_list is None:
157            model_list = self._get_model_list()
158
159        dict_results = {}
160        for model in model_list:
161            current_model = getattr(self._parent_class, model)
162
163            stats_list = current_model.mystats.spatial_stats.results.__dict__
164
165            for stats, results in stats_list.items():
166
167                if results is None:
168
169                    results = (
170                        np.zeros(
171                            shape=(
172                                (
173                                    current_model.mymesh.mesh["nrow"],
174                                    current_model.mymesh.mesh["ncol"],
175                                )
176                            )
177                        )
178                        * np.nan
179                    )
180                    print(f"</> Warning stats {stats} is None for model {model}.")
181
182                if stats in dict_results.keys():
183                    dict_results[stats] = np.stack(
184                        (dict_results[stats], np.array(results))
185                    )
186                else:
187                    dict_results.update({stats: np.array(results)})
188
189        for key, values in dict_results.items():
190            stats = getattr(self.multimodel_spatial_stats, key)
191            stats._fill_stats_attributes(values)
192
193    def compute_multimodel_statistics_quantile(self, model_list=None):
194        """
195        Compute the multimodel statistics for the quantile statistic.
196        """
197        if model_list is None:
198            model_list = self._get_model_list()
199
200        quantile_matrix_dict_results = {}
201        spatial_dict_results = {}
202        vector_dict_results = {}
203
204        self.multimodel_quantile_stats = None
205
206        for model in model_list:
207
208            current_model = getattr(self._parent_class, model)
209
210            if self.multimodel_quantile_stats is None:
211                self.multimodel_quantile_stats = _copy_quantile_attr(
212                    current_model.mystats.quantile_stats
213                )
214
215            all_quantile_results = current_model.mystats.quantile_stats.__dict__
216
217            quantile_matrix_results = dict(
218                filter(
219                    lambda key: not key[0].startswith("Quantile_"),
220                    all_quantile_results.items(),
221                )
222            )
223
224            quantile_xxh_results = dict(
225                filter(
226                    lambda key: key[0].startswith("Quantile_"),
227                    all_quantile_results.items(),
228                )
229            )
230
231            for key, quantile_matrix in quantile_matrix_results.items():
232
233                # if not key in quantile_matrix_dict_results.keys():
234                #     quantile_matrix_dict_results.update({key: {}})
235
236                if quantile_matrix is None:
237
238                    quantile_matrix = np.array([np.nan])
239                    print(f"</> Warning quantile attr {key} is None for model {model}.")
240
241                if key in quantile_matrix_dict_results.keys():
242                    quantile_matrix_dict_results[key] = np.concat(
243                        (
244                            quantile_matrix_dict_results[key],
245                            np.array(quantile_matrix)[np.newaxis, :],
246                        ),
247                        axis=0,
248                    )
249                else:
250                    quantile_matrix_dict_results.update(
251                        {key: np.array(quantile_matrix)[np.newaxis, :]}
252                    )
253
254            for key, quantile_xxh in quantile_xxh_results.items():
255
256                if not key in spatial_dict_results.keys():
257                    spatial_dict_results.update({key: {}})
258
259                if not key in vector_dict_results.keys():
260                    vector_dict_results.update({key: {}})
261
262                spatial_attr_list = [
263                    "Q_th",
264                    "maxima",
265                    "Umin",
266                    "Umax",
267                    "fit_shape",
268                    "fit_scale",
269                    "fit_loc",
270                ]
271
272                for attr in spatial_attr_list:
273
274                    results = getattr(quantile_xxh, attr)
275
276                    if results is None:
277
278                        results = (
279                            np.zeros(
280                                shape=(
281                                    (
282                                        current_model.mymesh.mesh["nrow"],
283                                        current_model.mymesh.mesh["ncol"],
284                                    )
285                                )
286                            )
287                            * np.nan
288                        )
289                        print(
290                            f"</> Warning quantile attr {attr} is None for model {model}."
291                        )
292
293                    if attr in spatial_dict_results[key].keys():
294                        spatial_dict_results[key][attr] = np.concat(
295                            (
296                                spatial_dict_results[key][attr],
297                                np.array(results)[np.newaxis, :],
298                            ),
299                            axis=0,
300                        )
301                    else:
302                        spatial_dict_results[key].update(
303                            {attr: np.array(results)[np.newaxis, :]}
304                        )
305
306                vector_attr = ["T", "T_emp", "nb_chunks", "chunk_size", "fit", "duration"]
307
308                for attr in vector_attr:
309
310                    results = getattr(quantile_xxh_results[key], attr)
311
312                    if results is None:
313
314                        results = np.array([np.nan])
315                        print(f"</> Warning attr {attr} is None for model {model}.")
316
317                    if isinstance(results, str | int | float):
318                        results = [results]
319
320                    if attr in vector_dict_results[key].keys():
321                        vector_dict_results[key][attr] = np.concat(
322                            (
323                                vector_dict_results[key][attr],
324                                np.array(results)[np.newaxis, :],
325                            ),
326                            axis=0,
327                        )
328                    else:
329                        vector_dict_results[key].update(
330                            {attr: np.array(results)[np.newaxis, :]}
331                        )
332
333        for key, values in quantile_matrix_dict_results.items():
334
335            if np.any(values != np.nan):
336                stats = getattr(self.multimodel_quantile_stats, key)
337                stats._fill_stats_attributes(values)
338
339        for key, quantile in quantile_xxh_results.items():
340
341            quantileXXh = getattr(self.multimodel_quantile_stats, key)
342
343            for var_name, values in spatial_dict_results[key].items():
344                stats = getattr(quantileXXh, var_name)
345                stats._fill_stats_attributes(values)
346
347            for var_name, values in vector_dict_results[key].items():
348                # stats = getattr(quantileXXh, var_name)
349                # stats = values
350                setattr(quantileXXh, var_name, values)
351
352
353class _copy_stats_attr:
354
355    def __init__(self, example_class):
356
357        for key in example_class.__dict__.keys():
358            setattr(self, key, _statistics())
359
360
361class _copy_quantile_attr:
362
363    def __init__(self, quantile_class):
364
365        for key in quantile_class.__dict__.keys():
366            if key.startswith("Quantile_"):
367                setattr(self, key, _copy_stats_attr(getattr(quantile_class, key)))
368            else:
369                setattr(self, key, _statistics())
370
371
372class _statistics:
373    """
374    Class statistics for every statistics used for the multimodel statistics: mean, median, variance, min, max and data (the distribution)
375    """
376
377    def __init__(self):
378
379        self.mean = None
380
381        self.median = None
382
383        self.variance = None
384
385        self.min = None
386
387        self.max = None
388
389        self.data = None
390
391    def _fill_stats_attributes(self, distrib):
392        self.mean = np.mean(distrib, axis=0)
393        self.median = np.median(distrib, axis=0)
394        self.variance = np.var(distrib, axis=0)
395        self.min = np.min(distrib, axis=0)
396        self.max = np.max(distrib, axis=0)
397        self.data = distrib
398
399    # def _fill_spatial_stats_attributes(self, matrix):
400    #     self.mean = np.mean(matrix, axis=0)
401    #     self.median = np.median(matrix, axis=0)
402    #     self.variance = np.var(matrix, axis=0)
403    #     self.min = np.min(matrix, axis=0)
404    #     self.max = np.max(matrix, axis=0)
405    #     self.data = matrix
class multimodel_statistics:
 14class multimodel_statistics:
 15    """
 16    Class which handle computation of statistics for multi-model containers. Statistics for each model container must be calculated first. Mean, median, variance, min, max and the distribution of every scores of each model containers are computed.
 17    """
 18
 19    def __init__(self, parent_class):
 20        self._parent_class = parent_class
 21        """_parent_class attribute stores the parent_class src.init.smashbox.SmashBox() 
 22        to be able to access to the result of any model"""
 23
 24        self.multimodel_misfit_stats = _copy_stats_attr(mystats.misfit_results())
 25        """Attribute misfit_stats_results stores the results of the multimodel statistics for the misfit."""
 26
 27        self.multimodel_quantile_stats = None
 28        """Attribute quantile_stats_results store the results of the multimodel statistics for the spatial quantile"""
 29
 30        self.multimodel_spatial_stats = _copy_stats_attr(mystats.spatial_stats_results())
 31        """Atrribute spatial_stats_results store the results of the multimodel statistics for the spatial results"""
 32
 33        self.multimodel_outlets_stats_sim = _copy_stats_attr(
 34            mystats.outlets_stats_results()
 35        )
 36        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  simulted outlets stats"""
 37
 38        self.multimodel_outlets_stats_obs = _copy_stats_attr(
 39            mystats.outlets_stats_results()
 40        )
 41        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  observed outlets stats"""
 42
 43    def _get_model_list(self):
 44
 45        model_list = list()
 46
 47        for key, attr in self._parent_class.__dict__.items():
 48
 49            if hasattr(attr, "mysmashmodel"):
 50
 51                current_model = getattr(self._parent_class, key)
 52
 53                if (
 54                    current_model._myparam.param.bbox
 55                    == self._parent_class.myparam.param.bbox
 56                ):
 57                    model_list.append(key)
 58
 59        return model_list
 60
 61    def compute_multimodel_statistics(self, model_list=None):
 62        """
 63        Compute the multimodel statistics: mean, median, variance, min, max and the distribution. These statistics
 64        are computed over all models containers which contains statistics and match the bounding box in object myparam.
 65
 66        :param model_list: list of the model container, defaults to None
 67        :type model_list: list, optional
 68
 69        """
 70
 71        if model_list is None:
 72            model_list = self._get_model_list()
 73
 74        self.compute_multimodel_statistics_misfit()
 75        self.compute_multimodel_statistics_outlets()
 76        self.compute_multimodel_statistics_outlets(obs=False)
 77        self.compute_multimodel_statistics_outlets(obs=True)
 78        self.compute_multimodel_statistics_spatial()
 79        self.compute_multimodel_statistics_quantile()
 80
 81    def compute_multimodel_statistics_misfit(self, model_list=None):
 82        """
 83        Compute the multimodel statistics for the misfit criteria.
 84        """
 85        if model_list is None:
 86            model_list = self._get_model_list()
 87
 88        dict_results = {}
 89        for model in model_list:
 90            current_model = getattr(self._parent_class, model)
 91
 92            misfit_list = current_model.mystats.misfit_stats.results.__dict__
 93
 94            for misfit, results in misfit_list.items():
 95
 96                if results is None:
 97
 98                    results = (
 99                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
100                    )
101                    print(f"</> Warning misfit {misfit} is None for model {model}.")
102
103                if misfit in dict_results.keys():
104                    dict_results[misfit] = np.stack(
105                        (dict_results[misfit], np.array(results))
106                    )
107                else:
108                    dict_results.update({misfit: np.array(results)})
109
110        for key, values in dict_results.items():
111            stats = getattr(self.multimodel_misfit_stats, key)
112            stats._fill_stats_attributes(values)
113
114    def compute_multimodel_statistics_outlets(self, model_list=None, obs=False):
115        """
116        Compute the multimodel statistics for the outlets criteria.
117        """
118        if model_list is None:
119            model_list = self._get_model_list()
120
121        dict_results = {}
122        for model in model_list:
123            current_model = getattr(self._parent_class, model)
124
125            if obs is False:
126                misfit_list = current_model.mystats.outlets_stats.results_sim.__dict__
127            else:
128                misfit_list = current_model.mystats.outlets_stats.results_obs.__dict__
129
130            for misfit, results in misfit_list.items():
131
132                if results is None:
133
134                    results = (
135                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
136                    )
137                    print(f"</> Warning misfit {misfit} is None for model {model}.")
138
139                if misfit in dict_results.keys():
140                    dict_results[misfit] = np.stack(
141                        (dict_results[misfit], np.array(results))
142                    )
143                else:
144                    dict_results.update({misfit: np.array(results)})
145
146        for key, values in dict_results.items():
147            if obs is False:
148                stats = getattr(self.multimodel_outlets_stats_sim, key)
149            else:
150                stats = getattr(self.multimodel_outlets_stats_obs, key)
151            stats._fill_stats_attributes(values)
152
153    def compute_multimodel_statistics_spatial(self, model_list=None):
154        """
155        Compute the multimodel statistics for the spatial statisctics.
156        """
157        if model_list is None:
158            model_list = self._get_model_list()
159
160        dict_results = {}
161        for model in model_list:
162            current_model = getattr(self._parent_class, model)
163
164            stats_list = current_model.mystats.spatial_stats.results.__dict__
165
166            for stats, results in stats_list.items():
167
168                if results is None:
169
170                    results = (
171                        np.zeros(
172                            shape=(
173                                (
174                                    current_model.mymesh.mesh["nrow"],
175                                    current_model.mymesh.mesh["ncol"],
176                                )
177                            )
178                        )
179                        * np.nan
180                    )
181                    print(f"</> Warning stats {stats} is None for model {model}.")
182
183                if stats in dict_results.keys():
184                    dict_results[stats] = np.stack(
185                        (dict_results[stats], np.array(results))
186                    )
187                else:
188                    dict_results.update({stats: np.array(results)})
189
190        for key, values in dict_results.items():
191            stats = getattr(self.multimodel_spatial_stats, key)
192            stats._fill_stats_attributes(values)
193
194    def compute_multimodel_statistics_quantile(self, model_list=None):
195        """
196        Compute the multimodel statistics for the quantile statistic.
197        """
198        if model_list is None:
199            model_list = self._get_model_list()
200
201        quantile_matrix_dict_results = {}
202        spatial_dict_results = {}
203        vector_dict_results = {}
204
205        self.multimodel_quantile_stats = None
206
207        for model in model_list:
208
209            current_model = getattr(self._parent_class, model)
210
211            if self.multimodel_quantile_stats is None:
212                self.multimodel_quantile_stats = _copy_quantile_attr(
213                    current_model.mystats.quantile_stats
214                )
215
216            all_quantile_results = current_model.mystats.quantile_stats.__dict__
217
218            quantile_matrix_results = dict(
219                filter(
220                    lambda key: not key[0].startswith("Quantile_"),
221                    all_quantile_results.items(),
222                )
223            )
224
225            quantile_xxh_results = dict(
226                filter(
227                    lambda key: key[0].startswith("Quantile_"),
228                    all_quantile_results.items(),
229                )
230            )
231
232            for key, quantile_matrix in quantile_matrix_results.items():
233
234                # if not key in quantile_matrix_dict_results.keys():
235                #     quantile_matrix_dict_results.update({key: {}})
236
237                if quantile_matrix is None:
238
239                    quantile_matrix = np.array([np.nan])
240                    print(f"</> Warning quantile attr {key} is None for model {model}.")
241
242                if key in quantile_matrix_dict_results.keys():
243                    quantile_matrix_dict_results[key] = np.concat(
244                        (
245                            quantile_matrix_dict_results[key],
246                            np.array(quantile_matrix)[np.newaxis, :],
247                        ),
248                        axis=0,
249                    )
250                else:
251                    quantile_matrix_dict_results.update(
252                        {key: np.array(quantile_matrix)[np.newaxis, :]}
253                    )
254
255            for key, quantile_xxh in quantile_xxh_results.items():
256
257                if not key in spatial_dict_results.keys():
258                    spatial_dict_results.update({key: {}})
259
260                if not key in vector_dict_results.keys():
261                    vector_dict_results.update({key: {}})
262
263                spatial_attr_list = [
264                    "Q_th",
265                    "maxima",
266                    "Umin",
267                    "Umax",
268                    "fit_shape",
269                    "fit_scale",
270                    "fit_loc",
271                ]
272
273                for attr in spatial_attr_list:
274
275                    results = getattr(quantile_xxh, attr)
276
277                    if results is None:
278
279                        results = (
280                            np.zeros(
281                                shape=(
282                                    (
283                                        current_model.mymesh.mesh["nrow"],
284                                        current_model.mymesh.mesh["ncol"],
285                                    )
286                                )
287                            )
288                            * np.nan
289                        )
290                        print(
291                            f"</> Warning quantile attr {attr} is None for model {model}."
292                        )
293
294                    if attr in spatial_dict_results[key].keys():
295                        spatial_dict_results[key][attr] = np.concat(
296                            (
297                                spatial_dict_results[key][attr],
298                                np.array(results)[np.newaxis, :],
299                            ),
300                            axis=0,
301                        )
302                    else:
303                        spatial_dict_results[key].update(
304                            {attr: np.array(results)[np.newaxis, :]}
305                        )
306
307                vector_attr = ["T", "T_emp", "nb_chunks", "chunk_size", "fit", "duration"]
308
309                for attr in vector_attr:
310
311                    results = getattr(quantile_xxh_results[key], attr)
312
313                    if results is None:
314
315                        results = np.array([np.nan])
316                        print(f"</> Warning attr {attr} is None for model {model}.")
317
318                    if isinstance(results, str | int | float):
319                        results = [results]
320
321                    if attr in vector_dict_results[key].keys():
322                        vector_dict_results[key][attr] = np.concat(
323                            (
324                                vector_dict_results[key][attr],
325                                np.array(results)[np.newaxis, :],
326                            ),
327                            axis=0,
328                        )
329                    else:
330                        vector_dict_results[key].update(
331                            {attr: np.array(results)[np.newaxis, :]}
332                        )
333
334        for key, values in quantile_matrix_dict_results.items():
335
336            if np.any(values != np.nan):
337                stats = getattr(self.multimodel_quantile_stats, key)
338                stats._fill_stats_attributes(values)
339
340        for key, quantile in quantile_xxh_results.items():
341
342            quantileXXh = getattr(self.multimodel_quantile_stats, key)
343
344            for var_name, values in spatial_dict_results[key].items():
345                stats = getattr(quantileXXh, var_name)
346                stats._fill_stats_attributes(values)
347
348            for var_name, values in vector_dict_results[key].items():
349                # stats = getattr(quantileXXh, var_name)
350                # stats = values
351                setattr(quantileXXh, var_name, values)

Class which handle computation of statistics for multi-model containers. Statistics for each model container must be calculated first. Mean, median, variance, min, max and the distribution of every scores of each model containers are computed.

multimodel_statistics(parent_class)
19    def __init__(self, parent_class):
20        self._parent_class = parent_class
21        """_parent_class attribute stores the parent_class src.init.smashbox.SmashBox() 
22        to be able to access to the result of any model"""
23
24        self.multimodel_misfit_stats = _copy_stats_attr(mystats.misfit_results())
25        """Attribute misfit_stats_results stores the results of the multimodel statistics for the misfit."""
26
27        self.multimodel_quantile_stats = None
28        """Attribute quantile_stats_results store the results of the multimodel statistics for the spatial quantile"""
29
30        self.multimodel_spatial_stats = _copy_stats_attr(mystats.spatial_stats_results())
31        """Atrribute spatial_stats_results store the results of the multimodel statistics for the spatial results"""
32
33        self.multimodel_outlets_stats_sim = _copy_stats_attr(
34            mystats.outlets_stats_results()
35        )
36        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  simulted outlets stats"""
37
38        self.multimodel_outlets_stats_obs = _copy_stats_attr(
39            mystats.outlets_stats_results()
40        )
41        """Atrribute outlets_stats store the resutls of the multimodel statistics for the  observed outlets stats"""
multimodel_misfit_stats

Attribute misfit_stats_results stores the results of the multimodel statistics for the misfit.

multimodel_quantile_stats

Attribute quantile_stats_results store the results of the multimodel statistics for the spatial quantile

multimodel_spatial_stats

Atrribute spatial_stats_results store the results of the multimodel statistics for the spatial results

multimodel_outlets_stats_sim

Atrribute outlets_stats store the resutls of the multimodel statistics for the simulted outlets stats

multimodel_outlets_stats_obs

Atrribute outlets_stats store the resutls of the multimodel statistics for the observed outlets stats

def compute_multimodel_statistics(self, model_list=None):
61    def compute_multimodel_statistics(self, model_list=None):
62        """
63        Compute the multimodel statistics: mean, median, variance, min, max and the distribution. These statistics
64        are computed over all models containers which contains statistics and match the bounding box in object myparam.
65
66        :param model_list: list of the model container, defaults to None
67        :type model_list: list, optional
68
69        """
70
71        if model_list is None:
72            model_list = self._get_model_list()
73
74        self.compute_multimodel_statistics_misfit()
75        self.compute_multimodel_statistics_outlets()
76        self.compute_multimodel_statistics_outlets(obs=False)
77        self.compute_multimodel_statistics_outlets(obs=True)
78        self.compute_multimodel_statistics_spatial()
79        self.compute_multimodel_statistics_quantile()

Compute the multimodel statistics: mean, median, variance, min, max and the distribution. These statistics are computed over all models containers which contains statistics and match the bounding box in object myparam.

Parameters
  • model_list: list of the model container, defaults to None
def compute_multimodel_statistics_misfit(self, model_list=None):
 81    def compute_multimodel_statistics_misfit(self, model_list=None):
 82        """
 83        Compute the multimodel statistics for the misfit criteria.
 84        """
 85        if model_list is None:
 86            model_list = self._get_model_list()
 87
 88        dict_results = {}
 89        for model in model_list:
 90            current_model = getattr(self._parent_class, model)
 91
 92            misfit_list = current_model.mystats.misfit_stats.results.__dict__
 93
 94            for misfit, results in misfit_list.items():
 95
 96                if results is None:
 97
 98                    results = (
 99                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
100                    )
101                    print(f"</> Warning misfit {misfit} is None for model {model}.")
102
103                if misfit in dict_results.keys():
104                    dict_results[misfit] = np.stack(
105                        (dict_results[misfit], np.array(results))
106                    )
107                else:
108                    dict_results.update({misfit: np.array(results)})
109
110        for key, values in dict_results.items():
111            stats = getattr(self.multimodel_misfit_stats, key)
112            stats._fill_stats_attributes(values)

Compute the multimodel statistics for the misfit criteria.

def compute_multimodel_statistics_outlets(self, model_list=None, obs=False):
114    def compute_multimodel_statistics_outlets(self, model_list=None, obs=False):
115        """
116        Compute the multimodel statistics for the outlets criteria.
117        """
118        if model_list is None:
119            model_list = self._get_model_list()
120
121        dict_results = {}
122        for model in model_list:
123            current_model = getattr(self._parent_class, model)
124
125            if obs is False:
126                misfit_list = current_model.mystats.outlets_stats.results_sim.__dict__
127            else:
128                misfit_list = current_model.mystats.outlets_stats.results_obs.__dict__
129
130            for misfit, results in misfit_list.items():
131
132                if results is None:
133
134                    results = (
135                        np.zeros(shape=(len(current_model.mymesh.mesh["code"]))) * np.nan
136                    )
137                    print(f"</> Warning misfit {misfit} is None for model {model}.")
138
139                if misfit in dict_results.keys():
140                    dict_results[misfit] = np.stack(
141                        (dict_results[misfit], np.array(results))
142                    )
143                else:
144                    dict_results.update({misfit: np.array(results)})
145
146        for key, values in dict_results.items():
147            if obs is False:
148                stats = getattr(self.multimodel_outlets_stats_sim, key)
149            else:
150                stats = getattr(self.multimodel_outlets_stats_obs, key)
151            stats._fill_stats_attributes(values)

Compute the multimodel statistics for the outlets criteria.

def compute_multimodel_statistics_spatial(self, model_list=None):
153    def compute_multimodel_statistics_spatial(self, model_list=None):
154        """
155        Compute the multimodel statistics for the spatial statisctics.
156        """
157        if model_list is None:
158            model_list = self._get_model_list()
159
160        dict_results = {}
161        for model in model_list:
162            current_model = getattr(self._parent_class, model)
163
164            stats_list = current_model.mystats.spatial_stats.results.__dict__
165
166            for stats, results in stats_list.items():
167
168                if results is None:
169
170                    results = (
171                        np.zeros(
172                            shape=(
173                                (
174                                    current_model.mymesh.mesh["nrow"],
175                                    current_model.mymesh.mesh["ncol"],
176                                )
177                            )
178                        )
179                        * np.nan
180                    )
181                    print(f"</> Warning stats {stats} is None for model {model}.")
182
183                if stats in dict_results.keys():
184                    dict_results[stats] = np.stack(
185                        (dict_results[stats], np.array(results))
186                    )
187                else:
188                    dict_results.update({stats: np.array(results)})
189
190        for key, values in dict_results.items():
191            stats = getattr(self.multimodel_spatial_stats, key)
192            stats._fill_stats_attributes(values)

Compute the multimodel statistics for the spatial statisctics.

def compute_multimodel_statistics_quantile(self, model_list=None):
194    def compute_multimodel_statistics_quantile(self, model_list=None):
195        """
196        Compute the multimodel statistics for the quantile statistic.
197        """
198        if model_list is None:
199            model_list = self._get_model_list()
200
201        quantile_matrix_dict_results = {}
202        spatial_dict_results = {}
203        vector_dict_results = {}
204
205        self.multimodel_quantile_stats = None
206
207        for model in model_list:
208
209            current_model = getattr(self._parent_class, model)
210
211            if self.multimodel_quantile_stats is None:
212                self.multimodel_quantile_stats = _copy_quantile_attr(
213                    current_model.mystats.quantile_stats
214                )
215
216            all_quantile_results = current_model.mystats.quantile_stats.__dict__
217
218            quantile_matrix_results = dict(
219                filter(
220                    lambda key: not key[0].startswith("Quantile_"),
221                    all_quantile_results.items(),
222                )
223            )
224
225            quantile_xxh_results = dict(
226                filter(
227                    lambda key: key[0].startswith("Quantile_"),
228                    all_quantile_results.items(),
229                )
230            )
231
232            for key, quantile_matrix in quantile_matrix_results.items():
233
234                # if not key in quantile_matrix_dict_results.keys():
235                #     quantile_matrix_dict_results.update({key: {}})
236
237                if quantile_matrix is None:
238
239                    quantile_matrix = np.array([np.nan])
240                    print(f"</> Warning quantile attr {key} is None for model {model}.")
241
242                if key in quantile_matrix_dict_results.keys():
243                    quantile_matrix_dict_results[key] = np.concat(
244                        (
245                            quantile_matrix_dict_results[key],
246                            np.array(quantile_matrix)[np.newaxis, :],
247                        ),
248                        axis=0,
249                    )
250                else:
251                    quantile_matrix_dict_results.update(
252                        {key: np.array(quantile_matrix)[np.newaxis, :]}
253                    )
254
255            for key, quantile_xxh in quantile_xxh_results.items():
256
257                if not key in spatial_dict_results.keys():
258                    spatial_dict_results.update({key: {}})
259
260                if not key in vector_dict_results.keys():
261                    vector_dict_results.update({key: {}})
262
263                spatial_attr_list = [
264                    "Q_th",
265                    "maxima",
266                    "Umin",
267                    "Umax",
268                    "fit_shape",
269                    "fit_scale",
270                    "fit_loc",
271                ]
272
273                for attr in spatial_attr_list:
274
275                    results = getattr(quantile_xxh, attr)
276
277                    if results is None:
278
279                        results = (
280                            np.zeros(
281                                shape=(
282                                    (
283                                        current_model.mymesh.mesh["nrow"],
284                                        current_model.mymesh.mesh["ncol"],
285                                    )
286                                )
287                            )
288                            * np.nan
289                        )
290                        print(
291                            f"</> Warning quantile attr {attr} is None for model {model}."
292                        )
293
294                    if attr in spatial_dict_results[key].keys():
295                        spatial_dict_results[key][attr] = np.concat(
296                            (
297                                spatial_dict_results[key][attr],
298                                np.array(results)[np.newaxis, :],
299                            ),
300                            axis=0,
301                        )
302                    else:
303                        spatial_dict_results[key].update(
304                            {attr: np.array(results)[np.newaxis, :]}
305                        )
306
307                vector_attr = ["T", "T_emp", "nb_chunks", "chunk_size", "fit", "duration"]
308
309                for attr in vector_attr:
310
311                    results = getattr(quantile_xxh_results[key], attr)
312
313                    if results is None:
314
315                        results = np.array([np.nan])
316                        print(f"</> Warning attr {attr} is None for model {model}.")
317
318                    if isinstance(results, str | int | float):
319                        results = [results]
320
321                    if attr in vector_dict_results[key].keys():
322                        vector_dict_results[key][attr] = np.concat(
323                            (
324                                vector_dict_results[key][attr],
325                                np.array(results)[np.newaxis, :],
326                            ),
327                            axis=0,
328                        )
329                    else:
330                        vector_dict_results[key].update(
331                            {attr: np.array(results)[np.newaxis, :]}
332                        )
333
334        for key, values in quantile_matrix_dict_results.items():
335
336            if np.any(values != np.nan):
337                stats = getattr(self.multimodel_quantile_stats, key)
338                stats._fill_stats_attributes(values)
339
340        for key, quantile in quantile_xxh_results.items():
341
342            quantileXXh = getattr(self.multimodel_quantile_stats, key)
343
344            for var_name, values in spatial_dict_results[key].items():
345                stats = getattr(quantileXXh, var_name)
346                stats._fill_stats_attributes(values)
347
348            for var_name, values in vector_dict_results[key].items():
349                # stats = getattr(quantileXXh, var_name)
350                # stats = values
351                setattr(quantileXXh, var_name, values)

Compute the multimodel statistics for the quantile statistic.