From ab6e9563b63be36ea79cbe2b77523107205f5aef Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 30 Aug 2023 16:09:26 -0400 Subject: [PATCH 01/25] Generate weights: added stationary and non-stationary weights in attribute_weights --- tests/test_ensembles.py | 252 +++++++++++++++++++++++++++++++++++++++- xscen/ensembles.py | 78 ++++++++++++- 2 files changed, 325 insertions(+), 5 deletions(-) diff --git a/tests/test_ensembles.py b/tests/test_ensembles.py index 88821deb..20f9e56c 100644 --- a/tests/test_ensembles.py +++ b/tests/test_ensembles.py @@ -504,12 +504,30 @@ def test_changing_horizon(self): ) @pytest.mark.parametrize( - "standardize, skipna", [(True, True), (True, False), (False, True)] + "standardize, skipna, attribute_weights", + [ + (True, True, None), + (True, False, None), + (False, True, None), + ( + True, + True, + {"institution": {"CCCma": 2, "CSIRO-QCCCE": 3, "ECMWF": 0, "GFDL": 5}}, + ), + ], ) - def test_standardize(self, standardize, skipna): - out = xs.generate_weights(self.ens, standardize=standardize, skipna=skipna) + def test_standardize(self, standardize, skipna, attribute_weights): + out = xs.generate_weights( + self.ens, + standardize=standardize, + skipna=skipna, + attribute_weights=attribute_weights, + ) if standardize: - np.testing.assert_allclose(out.sum(), 1 if skipna else 4) + if not attribute_weights: + np.testing.assert_allclose(out.sum(), 1 if skipna else 4) + else: + np.testing.assert_allclose(out.sum(), 1) else: np.testing.assert_allclose(out.sum(), 10) @@ -605,3 +623,229 @@ def test_errors(self): match="The 'cat:member' attribute is inconsistent across datasets.", ): xs.generate_weights(ens2) + + @staticmethod + def answer_attribute_weight(can45, can85, clim, csi45, csi85, ec85, gfd45): + ans = np.concatenate( + ( + np.array( + np.split( + np.array( + [ + 0.08333, + 0.25, + 0.25, + 0.25, + 0.25, + ] + ), + 5, + ) + ) + * can45, # CanESM2 family, RCP4.5 + np.array( + np.split( + np.array( + [ + 0.08333, + 0.25, + 0.25, + 0.25, + 0.25, + ] + ), + 5, + ) + ) + * can85, # CanESM2 family, RCP8.5 + np.array(np.split(np.repeat(np.array(0.08333), 10, 0), 10)) + * clim, # ClimEx + np.array(np.split(np.repeat(0.2, 5, 0), 5)) + * csi45, # CSIRO-Mk6, RCP4.5 + np.array([1], ndmin=2) * csi85, # CSIRO2, RCP8.5 + np.array(np.split(np.array([1, 1]), 2)) * ec85, # EC-EARTH RCMs, RCP8.5 + np.array(np.split(np.array([0.5, 0.5]), 2)) + * gfd45, # GFDL-ESM2G family, RCP4.5 + np.array(np.split(np.array([1, 1]), 2)) + * gfd45, # GFDL-ESM2M family, RCP4.5 + ) + ) + return np.squeeze(ans) + + @pytest.mark.parametrize( + "coefs, weights", + [ + ( + { + "can45": 2, + "can85": 2, + "clim": 2, + "csi45": 3, + "csi85": 3, + "ec85": 0, + "gfd45": 5, + }, + {"institution": {"CCCma": 2, "CSIRO-QCCCE": 3, "ECMWF": 0, "GFDL": 5}}, + ), + ( + { + "can45": 2, + "can85": 2, + "clim": 2, + "csi45": 1, + "csi85": 1, + "ec85": 1, + "gfd45": 1, + }, + {"institution": {"CCCma": 2, "others": 1}}, + ), + ( + { + "can45": 2, + "can85": 1, + "clim": 1, + "csi45": 2, + "csi85": 1, + "ec85": 1, + "gfd45": 2, + }, + {"experiment": {"rcp45": 2, "rcp85": 1}}, + ), + ( + { + "can45": [0, 0, 1, 1], + "can85": [0, 1, 0, 1], + "clim": [0, 1, 0, 1], + "csi45": [0, 0, 1, 1], + "csi85": [0, 1, 0, 1], + "ec85": [0, 1, 0, 1], + "gfd45": [0, 0, 1, 1], + }, + { + "experiment": xr.DataArray( + data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), + dims=["horizon", "experiment"], + coords=dict( + horizon=["1981-2010", "2041-2070", "+2C", "+4C"], + experiment=["rcp45", "rcp85"], + ), + attrs=dict( + description="Experiment weight through horizons", + units="", + ), + ) + }, + ), + ( + { + "can45": [0, 0, 1, 1], + "can85": [0, 1, 0, 1], + "clim": [0, 1, 0, 1], + "csi45": [0, 0, 1, 1], + "csi85": [0, 1, 0, 1], + "ec85": [0, 1, 0, 1], + "gfd45": [0, 0, 1, 1], + }, + { + "experiment": xr.DataArray( + data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), + dims=["horizon", "experiment"], + coords=dict( + horizon=["1981-2010", "2041-2070", "+2C", "+4C"], + experiment=["rcp45", "others"], + ), + attrs=dict( + description="Experiment weight through horizons", + units="", + ), + ) + }, + ), + ], + ) + def test_attribute_weight(self, coefs, weights): + ans = self.answer_attribute_weight(**coefs) + if len(ans.shape) > 1: + ans = ans.transpose() + out = xs.generate_weights(self.ens, attribute_weights=weights) + np.testing.assert_array_almost_equal(out, ans, decimal=4) + + def test_attribute_weight_error(self): + # Required attributes + with pytest.raises( + ValueError, + match="Attribute_weights should be dict or xr.DataArray.", + ): + xs.generate_weights(self.ens, attribute_weights={"experiment": [1, 2, 3]}) + with pytest.raises( + ValueError, + match="The test attribute is missing from some simulations.", + ): + xs.generate_weights( + self.ens, attribute_weights={"test": {"CCCma": 2, "others": 1}} + ) + with pytest.raises( + ValueError, + match="The institution CSIRO-QCCCE or others are not in the attribute_weights dict.", + ): + xs.generate_weights( + self.ens, attribute_weights={"institution": {"CCCma": 2, "GFDL": 1}} + ) + with pytest.raises( + ValueError, match="experiment is not in the xr.DataArray coords." + ): + xs.generate_weights( + self.ens, + attribute_weights={ + "experiment": xr.DataArray( + data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), + dims=["horizon", "test"], + coords=dict( + horizon=["1981-2010", "2041-2070", "+2C", "+4C"], + test=["rcp45", "rcp85"], + ), + ) + }, + ) + with pytest.raises( + ValueError, + match="The experiment DataArray has more than one coord dimension to apply weights", + ): + xs.generate_weights( + self.ens, + attribute_weights={ + "experiment": xr.DataArray( + data=np.array( + [ + [[0, 0], [0, 1]], + [[0, 1], [2, 1]], + [[1, 0], [1, 2]], + [[1, 1], [2, 2]], + ] + ), + dims=["horizon", "experiment", "test"], + coords=dict( + horizon=["1981-2010", "2041-2070", "+2C", "+4C"], + experiment=["rcp45", "rcp85"], + test=[0, 1], + ), + ) + }, + ) + with pytest.raises( + ValueError, + match="The experiment rcp85 or others are not in the attribute_weights datarray coords.", + ): + xs.generate_weights( + self.ens, + attribute_weights={ + "experiment": xr.DataArray( + data=np.array([[0], [0], [1], [1]]), + dims=["horizon", "experiment"], + coords=dict( + horizon=["1981-2010", "2041-2070", "+2C", "+4C"], + experiment=["rcp45"], + ), + ) + }, + ) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 53d722d7..9bc0b423 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -143,6 +143,7 @@ def generate_weights( *, independence_level: str = "model", experiment_weights: bool = False, + attribute_weights: dict = None, skipna: bool = True, v_for_skipna: str = None, standardize: bool = False, @@ -162,6 +163,14 @@ def generate_weights( 'institution': Weights using the method '1 institution - 1 Vote' experiment_weights : bool If True, each experiment will be given a total weight of 1. This option requires the 'cat:experiment' attribute to be present in all datasets. + attribute_weights : dict + Nested dictionaries of weights to apply to each dataset. + Keys are the attributes names for which weights are given, + values are either dictionaries containing attribute value and individual weight + or a xr.DataArray with the same non-stationary coord as the datasets (ex: time, horizon) and attribute coord (ex: experiment). + If others is used, all options not named will be given same weight value for the attribute. + ex: {'model': {'MPI-ESM-1-2-HAM': 0.25, 'MPI-ESM1-2-HR': 0.5}, + 'experiment': {'ssp585': xr.DataArray, 'ssp126': xr.DataArray}, 'institution': {'CCma': 0.5, 'others': 1} skipna : bool If True, weights will be computed from attributes only. If False, weights will be computed from the number of non-missing values. skipna=False requires either a 'time' or 'horizon' dimension in the datasets. @@ -231,7 +240,9 @@ def generate_weights( "source": None, "member": None, } + info = {key: dict(defdict, **get_cat_attrs(datasets[key])) for key in keys} + # More easily manage GCMs and RCMs for k in info: if info[k]["driving_model"] is None or len(info[k]["driving_model"]) == 0: @@ -441,7 +452,72 @@ def generate_weights( # Drop the experiment coordinate weights = weights.drop_vars("experiment") + # Attribute_weights + if attribute_weights: + stationary_weights = {} + non_stationary_weights = {} + for att, v_att in attribute_weights.items(): + # Verification + if att not in info[k] or any( + (info[k][att] is None or len(info[k][att]) == 0) for k in info + ): + raise ValueError( + f"The {att} attribute is missing from some simulations." + ) + # Split dict and xr.Dataarray weights + if isinstance(v_att, xr.DataArray): + non_stationary_weights[att] = v_att + elif isinstance(v_att, dict): + stationary_weights[att] = v_att + else: + raise ValueError("Attribute_weights should be dict or xr.DataArray.") + # Stationary weights (dicts) + if stationary_weights: + for att, w_dict in stationary_weights.items(): + for k, v in info.items(): + if v[att] not in w_dict and "others" not in w_dict: + raise ValueError( + f"The {att} {v[att]} or others are not in the attribute_weights dict." + ) + elif v[att] not in w_dict and "others" in w_dict: + w = w_dict["others"] + elif v[att] in w_dict: + w = w_dict[v[att]] + weights.loc[{"realization": k}] = weights.sel(realization=k) * w + # Non-staionary weights (xr.DataArray) + if non_stationary_weights: + for att, da in non_stationary_weights.items(): + # verification + if att not in da.coords: + raise ValueError(f"{att} is not in the xr.DataArray coords.") + ls_coord = list(da.coords) + ls_coord.remove(att) + if len(ls_coord) > 1: + raise ValueError( + f"The {att} DataArray has more than one coord dimension to apply weights." + ) + else: + coord = ls_coord[0] + # coord which will be used for broadcasting + if coord not in weights.coords: + weights = weights.expand_dims({coord: da[coord].values}) + ls_da = [] + for k, v in info.items(): + if v[att] not in da[att] and "others" not in da[att]: + raise ValueError( + f"The {att} {v[att]} or others are not in the attribute_weights datarray coords." + ) + elif v[att] not in da[att] and "others" in da[att]: + ls_da.append(da.sel(**{att: "others"}).drop_vars(att)) + else: + ls_da.append(da.sel(**{att: v[att]}).drop_vars(att)) + nw = xr.concat(ls_da, weights.realization) + weights = weights * nw + if standardize: - weights = weights / weights.sum(dim="realization") + if attribute_weights: + weights = weights / weights.sum(dim=weights.dims) + else: + weights = weights / weights.sum(dim="realization") return weights From 48b13e9a8690571ea0221716eb92f48aea4d22f0 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 31 Aug 2023 09:32:57 -0400 Subject: [PATCH 02/25] Added pull request info to authors.rst, history.rst --- AUTHORS.rst | 1 + HISTORY.rst | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index be4540bd..c481db41 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -22,3 +22,4 @@ Contributors * Sarah Gammon `@SarahG-579462 `_ * Yannick Rousseau * Marco Braun `@vindelico `_ +* Sarah-Claude Bourdeau-Goulet `@sarahclaude `_ diff --git a/HISTORY.rst b/HISTORY.rst index f5537145..38c2d603 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,13 @@ History ======= +v0.7.2 (Unreleased) +------------------- + +New features and enhancements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* New argument attribute_weights to ``generate_weights`` to allow for custom weights. + v0.7.1 (2023-08-23) ------------------- * Update dependencies by removing ``pygeos``, pinning ``shapely>=2`` and ``intake-esm>=2023.07.07`` as well as other small fixes to the environment files. (:pull:`243`). From 7f379d1f3fc8ed544388efd1be020e275af7636d Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 31 Aug 2023 09:46:53 -0400 Subject: [PATCH 03/25] Zenodo.json --- .zenodo.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index 9ad96650..9af036da 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -39,6 +39,11 @@ "name": "Braun, Marco", "affiliation": "Ouranos", "orcid": "0000-0001-5061-3217" + }, + { + "name": "Bourdeau-Goulet, Sarah-Claude", + "affiliation": "Ouranos", + "orcid": "0000-0002-6125-2557" } ], "keywords": [ From da100694f949a43e8e3f93117159b12e2c64efeb Mon Sep 17 00:00:00 2001 From: RondeauG Date: Fri, 15 Sep 2023 09:28:41 -0400 Subject: [PATCH 04/25] typo --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index a555f55c..a699bf75 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,7 @@ History ======= -v0.8.0 (Unreleased) +v0.8.0 (unreleased) ------------------- Contributors to this version: Gabriel Rondeau-Genesse (:user:`RondeauG`). From e9d3ddcecfda176da2fda7b62367b5c12a4929f1 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Tue, 19 Sep 2023 16:27:44 -0400 Subject: [PATCH 05/25] Added PR# --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index a699bf75..eca7e980 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -13,7 +13,7 @@ Announcements New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Added the ability to search for simulations that reach a given warming level. (:pull:`251`). -* New argument attribute_weights to ``generate_weights`` to allow for custom weights. +* New argument attribute_weights to ``generate_weights`` to allow for custom weights. (:pull:`252`). Breaking changes ^^^^^^^^^^^^^^^^ From b09cec2d1802a65d88d27a526ccd8bf6c83f1d29 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 20 Sep 2023 12:51:52 -0400 Subject: [PATCH 06/25] added error and warnings --- xscen/ensembles.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 9bc0b423..b99c4e19 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -169,7 +169,7 @@ def generate_weights( values are either dictionaries containing attribute value and individual weight or a xr.DataArray with the same non-stationary coord as the datasets (ex: time, horizon) and attribute coord (ex: experiment). If others is used, all options not named will be given same weight value for the attribute. - ex: {'model': {'MPI-ESM-1-2-HAM': 0.25, 'MPI-ESM1-2-HR': 0.5}, + ex: {'source': {'MPI-ESM-1-2-HAM': 0.25, 'MPI-ESM1-2-HR': 0.5}, 'experiment': {'ssp585': xr.DataArray, 'ssp126': xr.DataArray}, 'institution': {'CCma': 0.5, 'others': 1} skipna : bool If True, weights will be computed from attributes only. If False, weights will be computed from the number of non-missing values. @@ -454,9 +454,31 @@ def generate_weights( # Attribute_weights if attribute_weights: + # mismatch GCM/RCM attributes in same datasets not yet implemented + if not all([info[k]["driving_model"] is None for k in info.keys()]) or not all( + [info[k]["driving_model"] is not None for k in info.keys()] + ): + raise NotImplementedError( + "Management of RCM and GCM in same datasets dictionary not " + "yet implemented with attribute_weights." + ) + stationary_weights = {} non_stationary_weights = {} for att, v_att in attribute_weights.items(): + # Add warning when mismatch between independance_level/experiment_weight and attribute_weights + if att != independence_level or ( + att == "experiment" and not experiment_weights + ): + if att != "experiment": + warnings.warn( + f"The {att} weights do not match the {independence_level} independance level" + ) + else: + warnings.warn( + "Key experiment given in attribute_weights without argument experiment_weights=True" + ) + # Verification if att not in info[k] or any( (info[k][att] is None or len(info[k][att]) == 0) for k in info From 5ba6e8c74a1e15578a8f8199a0d0d0e342546596 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 20 Sep 2023 14:18:57 -0400 Subject: [PATCH 07/25] bug fix --- xscen/ensembles.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index b99c4e19..3bd9ab6c 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -3,7 +3,7 @@ import logging import warnings from copy import deepcopy -from itertools import chain +from itertools import chain, groupby from pathlib import Path from typing import Any, Union @@ -455,8 +455,9 @@ def generate_weights( # Attribute_weights if attribute_weights: # mismatch GCM/RCM attributes in same datasets not yet implemented - if not all([info[k]["driving_model"] is None for k in info.keys()]) or not all( - [info[k]["driving_model"] is not None for k in info.keys()] + if ( + len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) + != 1 ): raise NotImplementedError( "Management of RCM and GCM in same datasets dictionary not " From 6be25693ed8c5387466fd95898bf76613c5bafee Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 21 Sep 2023 14:01:53 -0400 Subject: [PATCH 08/25] tests warnings and errors --- tests/test_ensembles.py | 75 ++++++++++++++++++++++++++++++++++------- xscen/ensembles.py | 23 +++++++------ 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/tests/test_ensembles.py b/tests/test_ensembles.py index 20f9e56c..50267030 100644 --- a/tests/test_ensembles.py +++ b/tests/test_ensembles.py @@ -207,7 +207,16 @@ def make_ensemble(): ] = ds2 return {o: out[o] for o in sorted(out)} + @staticmethod + def make_ensemble_rcm(ens): + ens_rcm = ens.copy() + for k in ens.keys(): + if "cat:driving_model" not in ens_rcm[k].attrs.keys(): + ens_rcm.pop(k) + return ens_rcm + ens = make_ensemble.__func__() + ens_rcm = make_ensemble_rcm(ens) @staticmethod def make_answer(independence_level, exp_weights, skipna): @@ -517,8 +526,12 @@ def test_changing_horizon(self): ], ) def test_standardize(self, standardize, skipna, attribute_weights): + if attribute_weights: + datasets = self.ens_rcm + else: + datasets = self.ens out = xs.generate_weights( - self.ens, + datasets, standardize=standardize, skipna=skipna, attribute_weights=attribute_weights, @@ -764,11 +777,20 @@ def answer_attribute_weight(can45, can85, clim, csi45, csi85, ec85, gfd45): ], ) def test_attribute_weight(self, coefs, weights): + # only test for RCMs + + # generate ans ans = self.answer_attribute_weight(**coefs) - if len(ans.shape) > 1: - ans = ans.transpose() - out = xs.generate_weights(self.ens, attribute_weights=weights) - np.testing.assert_array_almost_equal(out, ans, decimal=4) + ind = [ + index + for index, value in enumerate(list(self.ens.keys())) + if value in list(self.ens_rcm.keys()) + ] + ans_rcms = ans[ind] + if len(ans_rcms.shape) > 1: + ans_rcms = ans_rcms.transpose() + out = xs.generate_weights(self.ens_rcm, attribute_weights=weights) + np.testing.assert_array_almost_equal(out, ans_rcms, decimal=4) def test_attribute_weight_error(self): # Required attributes @@ -776,26 +798,28 @@ def test_attribute_weight_error(self): ValueError, match="Attribute_weights should be dict or xr.DataArray.", ): - xs.generate_weights(self.ens, attribute_weights={"experiment": [1, 2, 3]}) + xs.generate_weights( + self.ens_rcm, attribute_weights={"experiment": [1, 2, 3]} + ) with pytest.raises( ValueError, match="The test attribute is missing from some simulations.", ): xs.generate_weights( - self.ens, attribute_weights={"test": {"CCCma": 2, "others": 1}} + self.ens_rcm, attribute_weights={"test": {"CCCma": 2, "others": 1}} ) with pytest.raises( ValueError, - match="The institution CSIRO-QCCCE or others are not in the attribute_weights dict.", + match="The institution ECMWF or others are not in the attribute_weights dict.", ): xs.generate_weights( - self.ens, attribute_weights={"institution": {"CCCma": 2, "GFDL": 1}} + self.ens_rcm, attribute_weights={"institution": {"CCCma": 2, "GFDL": 1}} ) with pytest.raises( ValueError, match="experiment is not in the xr.DataArray coords." ): xs.generate_weights( - self.ens, + self.ens_rcm, attribute_weights={ "experiment": xr.DataArray( data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), @@ -812,7 +836,7 @@ def test_attribute_weight_error(self): match="The experiment DataArray has more than one coord dimension to apply weights", ): xs.generate_weights( - self.ens, + self.ens_rcm, attribute_weights={ "experiment": xr.DataArray( data=np.array( @@ -837,7 +861,7 @@ def test_attribute_weight_error(self): match="The experiment rcp85 or others are not in the attribute_weights datarray coords.", ): xs.generate_weights( - self.ens, + self.ens_rcm, attribute_weights={ "experiment": xr.DataArray( data=np.array([[0], [0], [1], [1]]), @@ -849,3 +873,30 @@ def test_attribute_weight_error(self): ) }, ) + # mismatch RCMs & GCMs + with pytest.raises( + NotImplementedError, + match="Management of RCM and GCM in same datasets dictionary not " + "yet implemented with attribute_weights.", + ): + xs.generate_weights( + self.ens, + attribute_weights={"institution": {"CCCma": 2, "others": 1}}, + ) + # warning mismatch attribute_weights & independance_level + with pytest.warns( + UserWarning, + match="The institution weights do not match the model independance_level", + ): + xs.generate_weights( + self.ens_rcm, + attribute_weights={"institution": {"CCCma": 2, "others": 1}}, + ) + with pytest.warns( + UserWarning, + match="Key experiment given in attribute_weights without argument experiment_weights=True", + ): + xs.generate_weights( + self.ens_rcm, + attribute_weights={"experiment": {"rcp45": 2, "rcp85": 1}}, + ) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 3bd9ab6c..4943aa96 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -243,6 +243,17 @@ def generate_weights( info = {key: dict(defdict, **get_cat_attrs(datasets[key])) for key in keys} + # Check if RCM and GCMs in datasets with attribute_weights + if ( + attribute_weights + and len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) + > 1 + ): + raise NotImplementedError( + "Management of RCM and GCM in same datasets dictionary not " + "yet implemented with attribute_weights." + ) + # More easily manage GCMs and RCMs for k in info: if info[k]["driving_model"] is None or len(info[k]["driving_model"]) == 0: @@ -454,16 +465,6 @@ def generate_weights( # Attribute_weights if attribute_weights: - # mismatch GCM/RCM attributes in same datasets not yet implemented - if ( - len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) - != 1 - ): - raise NotImplementedError( - "Management of RCM and GCM in same datasets dictionary not " - "yet implemented with attribute_weights." - ) - stationary_weights = {} non_stationary_weights = {} for att, v_att in attribute_weights.items(): @@ -473,7 +474,7 @@ def generate_weights( ): if att != "experiment": warnings.warn( - f"The {att} weights do not match the {independence_level} independance level" + f"The {att} weights do not match the {independence_level} independance_level" ) else: warnings.warn( From 153053ebf8e19f504278006c06e41c80eca73e48 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Mon, 25 Sep 2023 09:53:52 -0400 Subject: [PATCH 09/25] replace experiment_weights to balance_experiments + depreciation warning --- tests/test_ensembles.py | 8 ++++---- xscen/ensembles.py | 32 ++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/tests/test_ensembles.py b/tests/test_ensembles.py index 50267030..e97279ac 100644 --- a/tests/test_ensembles.py +++ b/tests/test_ensembles.py @@ -478,7 +478,7 @@ def test_generate_weights(self, independence_level, exp_weights, skipna): out = xs.generate_weights( self.ens, independence_level=independence_level, - experiment_weights=exp_weights, + balance_experiments=exp_weights, skipna=skipna, ) @@ -612,7 +612,7 @@ def test_errors(self): with pytest.raises( ValueError, match="The 'cat:experiment' attribute is missing" ): - xs.generate_weights(ens2, experiment_weights=True) + xs.generate_weights(ens2, balance_experiments=True) ens2 = deepcopy(self.ens) ens2["CCCma-CanESM2-rcp45-r1i1p1-CanESM2"].attrs["cat:institution"] = None with pytest.raises( @@ -628,7 +628,7 @@ def test_errors(self): UserWarning, match="The 'cat:experiment' attribute is missing from all datasets", ): - xs.generate_weights(ens2, experiment_weights=False) + xs.generate_weights(ens2, balance_experiments=False) ens2 = deepcopy(self.ens) ens2["CCCma-CanESM2-rcp45-r1i1p1-CanESM2"].attrs["cat:member"] = None with pytest.warns( @@ -894,7 +894,7 @@ def test_attribute_weight_error(self): ) with pytest.warns( UserWarning, - match="Key experiment given in attribute_weights without argument experiment_weights=True", + match="Key experiment given in attribute_weights without argument balance_experiments=True", ): xs.generate_weights( self.ens_rcm, diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 4943aa96..e51e0286 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -142,11 +142,12 @@ def generate_weights( datasets: Union[dict, list], *, independence_level: str = "model", - experiment_weights: bool = False, + balance_experiments: bool = False, attribute_weights: dict = None, skipna: bool = True, v_for_skipna: str = None, standardize: bool = False, + experiment_weights: bool = False, ) -> xr.DataArray: """Use realization attributes to automatically generate weights along the 'realization' dimension. @@ -161,7 +162,7 @@ def generate_weights( 'model': Weights using the method '1 model - 1 Vote', where every unique combination of 'source' and 'driving_model' is considered a model. 'GCM': Weights using the method '1 GCM - 1 Vote' 'institution': Weights using the method '1 institution - 1 Vote' - experiment_weights : bool + balance_experiments : bool If True, each experiment will be given a total weight of 1. This option requires the 'cat:experiment' attribute to be present in all datasets. attribute_weights : dict Nested dictionaries of weights to apply to each dataset. @@ -178,6 +179,8 @@ def generate_weights( Variable to use for skipna=False. If None, the first variable in the first dataset is used. standardize : bool If True, the weights are standardized to sum to 1 (per timestep/horizon, if skipna=False). + experiment_weights : bool + Deprecated. Use balance_experiments instead. Notes ----- @@ -194,6 +197,13 @@ def generate_weights( xr.DataArray Weights along the 'realization' dimension, or 2D weights along the 'realization' and 'time/horizon' dimensions if skipna=False. """ + if experiment_weights is True: + warnings.warn( + "`experiment_weights` has been renamed and will be removed in a future release. Use `balance_experiments` instead.", + category=FutureWarning, + ) + balance_experiments = True + if isinstance(datasets, list): datasets = {i: datasets[i] for i in range(len(datasets))} @@ -267,11 +277,11 @@ def generate_weights( raise ValueError( "The 'cat:source' or 'cat:driving_model' attribute is missing from some simulations." ) - if experiment_weights and any( + if balance_experiments and any( (info[k]["experiment"] is None or len(info[k]["experiment"]) == 0) for k in info ): raise ValueError( - "The 'cat:experiment' attribute is missing from some simulations. 'experiment_weights' cannot be True." + "The 'cat:experiment' attribute is missing from some simulations. 'balance_experiments' cannot be True." ) if independence_level == "institution" and any( (info[k]["institution"] is None or len(info[k]["institution"]) == 0) @@ -371,13 +381,13 @@ def generate_weights( if independence_level == "model": realization_struct = ( ["source", "driving_model", "experiment"] - if experiment_weights + if balance_experiments else ["source", "driving_model"] ) else: realization_struct = ( ["driving_model", "experiment"] - if experiment_weights + if balance_experiments else ["driving_model"] ) realizations = { @@ -411,7 +421,9 @@ def generate_weights( # Number of driving models run by a given institution if independence_level == "institution": institution_struct = ( - ["institution", "experiment"] if experiment_weights else ["institution"] + ["institution", "experiment"] + if balance_experiments + else ["institution"] ) institution = { info[k]["driving_model"] @@ -447,7 +459,7 @@ def generate_weights( w = 1 / n_models / n_realizations / n_institutions weights[i] = xr.where(np.isfinite(w), w, 0) - if experiment_weights: + if balance_experiments: # Divide the weight equally between the experiments experiments = [info[k]["experiment"] for k in info.keys()] weights = weights.assign_coords( @@ -470,7 +482,7 @@ def generate_weights( for att, v_att in attribute_weights.items(): # Add warning when mismatch between independance_level/experiment_weight and attribute_weights if att != independence_level or ( - att == "experiment" and not experiment_weights + att == "experiment" and not balance_experiments ): if att != "experiment": warnings.warn( @@ -478,7 +490,7 @@ def generate_weights( ) else: warnings.warn( - "Key experiment given in attribute_weights without argument experiment_weights=True" + "Key experiment given in attribute_weights without argument balance_experiments=True" ) # Verification From 1d266c9f2dcb9a09f4cf3377b54cd512d038dea1 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:22:16 -0400 Subject: [PATCH 10/25] Update tests/test_ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- tests/test_ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ensembles.py b/tests/test_ensembles.py index e97279ac..80523eaf 100644 --- a/tests/test_ensembles.py +++ b/tests/test_ensembles.py @@ -216,7 +216,7 @@ def make_ensemble_rcm(ens): return ens_rcm ens = make_ensemble.__func__() - ens_rcm = make_ensemble_rcm(ens) + ens_rcm = make_ensemble_rcm.__func__(ens) @staticmethod def make_answer(independence_level, exp_weights, skipna): From e3d68ce0f1f151e54c83407822d5cf570067c61f Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:23:17 -0400 Subject: [PATCH 11/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index e51e0286..98b56dd6 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -163,7 +163,8 @@ def generate_weights( 'GCM': Weights using the method '1 GCM - 1 Vote' 'institution': Weights using the method '1 institution - 1 Vote' balance_experiments : bool - If True, each experiment will be given a total weight of 1. This option requires the 'cat:experiment' attribute to be present in all datasets. + If True, each experiment will be given a total weight of 1 (prior to subsequent weighting made through `attribute_weights`). + This option requires the 'cat:experiment' attribute to be present in all datasets. attribute_weights : dict Nested dictionaries of weights to apply to each dataset. Keys are the attributes names for which weights are given, From 7a3b07e7da44f79a2d9538495dcf65a10205e6bb Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:24:46 -0400 Subject: [PATCH 12/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 98b56dd6..41fdd6ba 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -166,13 +166,13 @@ def generate_weights( If True, each experiment will be given a total weight of 1 (prior to subsequent weighting made through `attribute_weights`). This option requires the 'cat:experiment' attribute to be present in all datasets. attribute_weights : dict - Nested dictionaries of weights to apply to each dataset. - Keys are the attributes names for which weights are given, - values are either dictionaries containing attribute value and individual weight - or a xr.DataArray with the same non-stationary coord as the datasets (ex: time, horizon) and attribute coord (ex: experiment). - If others is used, all options not named will be given same weight value for the attribute. - ex: {'source': {'MPI-ESM-1-2-HAM': 0.25, 'MPI-ESM1-2-HR': 0.5}, - 'experiment': {'ssp585': xr.DataArray, 'ssp126': xr.DataArray}, 'institution': {'CCma': 0.5, 'others': 1} + Nested dictionaries of weights to apply to each dataset. These weights are applied after the independence weighting. + The first level of keys are the attributes for which weights are being given. + The second level of keys are unique entries for the attribute, with the value being either an individual weight + or a xr.DataArray. If a DataArray is used, its dimensions must be the same non-stationary coordinate as the datasets (ex: time, horizon) and the attribute being weighted (ex: experiment). + A `others` key can be used to give the same weight to all entries not specifically named in the dictionnary. + Example #1: {'source': {'MPI-ESM-1-2-HAM': 0.25, 'MPI-ESM1-2-HR': 0.5}}, + Example #2: {'experiment': {'ssp585': xr.DataArray, 'ssp126': xr.DataArray}, 'institution': {'CCCma': 0.5, 'others': 1}} skipna : bool If True, weights will be computed from attributes only. If False, weights will be computed from the number of non-missing values. skipna=False requires either a 'time' or 'horizon' dimension in the datasets. From 476ee69c3a557618d390a0f5240e76fd4fd01927 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:25:25 -0400 Subject: [PATCH 13/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 41fdd6ba..acd1cfa0 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -254,7 +254,7 @@ def generate_weights( info = {key: dict(defdict, **get_cat_attrs(datasets[key])) for key in keys} - # Check if RCM and GCMs in datasets with attribute_weights + # Check if there are both RCMs and GCMs in datasets, with attribute_weights set to weight them. if ( attribute_weights and len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) From e0b7e5440fdd40775ac683a8e742703c4ac64052 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:26:24 -0400 Subject: [PATCH 14/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index acd1cfa0..e781f125 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -261,8 +261,7 @@ def generate_weights( > 1 ): raise NotImplementedError( - "Management of RCM and GCM in same datasets dictionary not " - "yet implemented with attribute_weights." + "Weighting `source` and/or `driving_model` through `attribute_weights` is not yet implemented when given a mix of GCMs and RCMs." ) # More easily manage GCMs and RCMs From 489ca99e70ad915595371ee57301853250d7da72 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:26:56 -0400 Subject: [PATCH 15/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index e781f125..315d471b 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -480,7 +480,7 @@ def generate_weights( stationary_weights = {} non_stationary_weights = {} for att, v_att in attribute_weights.items(): - # Add warning when mismatch between independance_level/experiment_weight and attribute_weights + # Add warning when a mismatch between independance_level/experiment_weight and attribute_weights is detected if att != independence_level or ( att == "experiment" and not balance_experiments ): From 066150b8b617e168104ca34b5d65bcbcd6c9e940 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:28:04 -0400 Subject: [PATCH 16/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 315d471b..d86cdbef 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -520,7 +520,7 @@ def generate_weights( elif v[att] in w_dict: w = w_dict[v[att]] weights.loc[{"realization": k}] = weights.sel(realization=k) * w - # Non-staionary weights (xr.DataArray) + # Non-stationary weights (xr.DataArray) if non_stationary_weights: for att, da in non_stationary_weights.items(): # verification From 9ddec48fca8d1bdd2266a62cb954262b8a6e275b Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Mon, 2 Oct 2023 08:03:23 -0400 Subject: [PATCH 17/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index d86cdbef..daeef6c0 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -486,7 +486,7 @@ def generate_weights( ): if att != "experiment": warnings.warn( - f"The {att} weights do not match the {independence_level} independance_level" + f"The {att} weights do not match the {independence_level} independence_level" ) else: warnings.warn( From d33bde64e71c9ac6a60016abe26898c91c85920e Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Mon, 2 Oct 2023 08:03:35 -0400 Subject: [PATCH 18/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index daeef6c0..84107140 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -500,7 +500,7 @@ def generate_weights( raise ValueError( f"The {att} attribute is missing from some simulations." ) - # Split dict and xr.Dataarray weights + # Split dict and xr.DataArray weights if isinstance(v_att, xr.DataArray): non_stationary_weights[att] = v_att elif isinstance(v_att, dict): From 4da37e8685b151b42ee2d4b91aae09eb22a0f0fe Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:14:26 -0400 Subject: [PATCH 19/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 84107140..48370f29 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -256,7 +256,7 @@ def generate_weights( # Check if there are both RCMs and GCMs in datasets, with attribute_weights set to weight them. if ( - attribute_weights + any(a in ["source", "driving_model"] for a in list(attribute_weights.keys())) and len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) > 1 ): From eb4bef949ca37dc75db0d537b9bcb7c2a5bedbea Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Tue, 10 Oct 2023 15:12:43 -0400 Subject: [PATCH 20/25] add description, remove standardize on horizon/time --- tests/test_ensembles.py | 8 +++----- xscen/ensembles.py | 35 +++++++++++++++++------------------ 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/tests/test_ensembles.py b/tests/test_ensembles.py index 80523eaf..e8005762 100644 --- a/tests/test_ensembles.py +++ b/tests/test_ensembles.py @@ -521,7 +521,7 @@ def test_changing_horizon(self): ( True, True, - {"institution": {"CCCma": 2, "CSIRO-QCCCE": 3, "ECMWF": 0, "GFDL": 5}}, + {"institution": {"CCCma": 2, "ECMWF": 0, "GFDL": 5}}, ), ], ) @@ -537,10 +537,8 @@ def test_standardize(self, standardize, skipna, attribute_weights): attribute_weights=attribute_weights, ) if standardize: - if not attribute_weights: - np.testing.assert_allclose(out.sum(), 1 if skipna else 4) - else: - np.testing.assert_allclose(out.sum(), 1) + np.testing.assert_allclose(out.sum(), 1 if skipna else 4) + else: np.testing.assert_allclose(out.sum(), 10) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 48370f29..87105b2d 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -255,7 +255,7 @@ def generate_weights( info = {key: dict(defdict, **get_cat_attrs(datasets[key])) for key in keys} # Check if there are both RCMs and GCMs in datasets, with attribute_weights set to weight them. - if ( + if attribute_weights and ( any(a in ["source", "driving_model"] for a in list(attribute_weights.keys())) and len(list(groupby([info[k]["driving_model"] is None for k in info.keys()]))) > 1 @@ -481,17 +481,18 @@ def generate_weights( non_stationary_weights = {} for att, v_att in attribute_weights.items(): # Add warning when a mismatch between independance_level/experiment_weight and attribute_weights is detected - if att != independence_level or ( - att == "experiment" and not balance_experiments - ): - if att != "experiment": - warnings.warn( - f"The {att} weights do not match the {independence_level} independence_level" - ) - else: - warnings.warn( - "Key experiment given in attribute_weights without argument balance_experiments=True" - ) + if att == "experiment" and not balance_experiments: + warnings.warn( + "Key experiment given in attribute_weights without argument balance_experiments=True" + ) + + if ( + att in ["driving_model", "source"] + and independence_level not in ["model", "GCM"] + ) or (att == "institution" and independence_level != "institution"): + warnings.warn( + f"The {att} weights do not match the {independence_level} independence_level" + ) # Verification if att not in info[k] or any( @@ -523,9 +524,10 @@ def generate_weights( # Non-stationary weights (xr.DataArray) if non_stationary_weights: for att, da in non_stationary_weights.items(): - # verification + # check if the attribute is in the xr.DataArray coords if att not in da.coords: raise ValueError(f"{att} is not in the xr.DataArray coords.") + # find the coordinate (coord) to broadcast the weights (not equal to the attribute), ex: time / horizon ls_coord = list(da.coords) ls_coord.remove(att) if len(ls_coord) > 1: @@ -534,7 +536,7 @@ def generate_weights( ) else: coord = ls_coord[0] - # coord which will be used for broadcasting + # broadcast coord to the weights DataArray if coord not in weights.coords: weights = weights.expand_dims({coord: da[coord].values}) ls_da = [] @@ -551,9 +553,6 @@ def generate_weights( weights = weights * nw if standardize: - if attribute_weights: - weights = weights / weights.sum(dim=weights.dims) - else: - weights = weights / weights.sum(dim="realization") + weights = weights / weights.sum(dim="realization") return weights From dd7a3eeb0ceacd42e6f72c455d12e31f7c45a765 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Tue, 10 Oct 2023 15:28:12 -0400 Subject: [PATCH 21/25] add breaking changes --- HISTORY.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 4f139af7..e89aece8 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -18,11 +18,11 @@ New features and enhancements * Added documentation for `require_all_on` in `search_data_catalogs`. (:pull:`263`). * ``xs.save_to_table`` and ``xs.io.to_table`` to transform datasets and arrays to DataFrames, but with support for multi-columns, multi-sheets and localized table of content generation. * Better ``xs.extract.resample`` : support for weighted resampling operations when starting with frequencies coarser than daily and missing timesteps/values handling. (:issue:`80`, :issue:`93`, :pull:`265`). -* New argument attribute_weights to ``generate_weights`` to allow for custom weights. (:pull:`252`). +* New argument ``attribute_weights`` to ``generate_weights`` to allow for custom weights. (:pull:`252`). Breaking changes ^^^^^^^^^^^^^^^^ -* N/A +* ``experiment_weights`` argument in ``generate_weights`` was renamed to ``balance_experiments``. (:pull:`252`). Bug fixes ^^^^^^^^^ From 4e9892fdf735d9ba4c11e82a7fcf89ef73b404d8 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:48:40 -0400 Subject: [PATCH 22/25] Update HISTORY.rst Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index e89aece8..d4be1543 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,7 +4,7 @@ History v0.8.0 (unreleased) ------------------- -Contributors to this version: Gabriel Rondeau-Genesse (:user:`RondeauG`). +Contributors to this version: Gabriel Rondeau-Genesse (:user:`RondeauG`), Pascal Bourgault (:user:`aulemahal`), Juliette Lavoie (:user:`juliettelavoie`), Sarah-Claude Bourdeau-Goulet (:user:`sarahclaude`). Announcements ^^^^^^^^^^^^^ From cdd90e63bfa8f9e5b5eab42deaf46f62581dedd5 Mon Sep 17 00:00:00 2001 From: Sarah-Claude Bourdeau <83776457+sarahclaude@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:49:11 -0400 Subject: [PATCH 23/25] Update xscen/ensembles.py Co-authored-by: RondeauG <38501935+RondeauG@users.noreply.github.com> --- xscen/ensembles.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index 87105b2d..ad55f861 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -486,10 +486,7 @@ def generate_weights( "Key experiment given in attribute_weights without argument balance_experiments=True" ) - if ( - att in ["driving_model", "source"] - and independence_level not in ["model", "GCM"] - ) or (att == "institution" and independence_level != "institution"): + if ((att == "source" and independence_level != "model") or (att == "driving_model" and independence_level != "GCM") or (att == "institution" and independence_level != "institution"): warnings.warn( f"The {att} weights do not match the {independence_level} independence_level" ) From 228c4e01eb880aef69989167dbd7193919a6ac6b Mon Sep 17 00:00:00 2001 From: RondeauG <38501935+RondeauG@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:07:07 -0400 Subject: [PATCH 24/25] Update xscen/ensembles.py --- xscen/ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index ad55f861..9ceef86d 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -486,7 +486,7 @@ def generate_weights( "Key experiment given in attribute_weights without argument balance_experiments=True" ) - if ((att == "source" and independence_level != "model") or (att == "driving_model" and independence_level != "GCM") or (att == "institution" and independence_level != "institution"): + if ((att == "source" and independence_level != "model") or (att == "driving_model" and independence_level != "GCM") or (att == "institution" and independence_level != "institution")): warnings.warn( f"The {att} weights do not match the {independence_level} independence_level" ) From 89785ba7f5f35479067faa1f4df3feab16e0ebbc Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 11 Oct 2023 11:07:18 -0400 Subject: [PATCH 25/25] fixed typo --- xscen/ensembles.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xscen/ensembles.py b/xscen/ensembles.py index ad55f861..d545b415 100644 --- a/xscen/ensembles.py +++ b/xscen/ensembles.py @@ -486,7 +486,11 @@ def generate_weights( "Key experiment given in attribute_weights without argument balance_experiments=True" ) - if ((att == "source" and independence_level != "model") or (att == "driving_model" and independence_level != "GCM") or (att == "institution" and independence_level != "institution"): + if ( + (att == "source" and independence_level != "model") + or (att == "driving_model" and independence_level != "GCM") + or (att == "institution" and independence_level != "institution") + ): warnings.warn( f"The {att} weights do not match the {independence_level} independence_level" )