Skip to content

Commit 4859812

Browse files
authored
Merge pull request #34 from comet-toolkit/33-suffix
Renaming of dimensions and variables
2 parents bf9b404 + 8ac9096 commit 4859812

File tree

7 files changed

+392
-31
lines changed

7 files changed

+392
-31
lines changed

docs/content/user/api.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Uncertainty functions
3030
unc_accessor.UncAccessor.unc_vars
3131
unc_accessor.UncAccessor.__getitem__
3232
unc_accessor.UncAccessor.keys
33+
unc_accessor.UncAccessor.rename
34+
unc_accessor.UncAccessor.rename_dims
3335
unc_accessor.VariableUncertainty
3436
unc_accessor.VariableUncertainty.__getitem__
3537
unc_accessor.VariableUncertainty.__setitem__
@@ -83,4 +85,12 @@ Flag functions
8385
flag_accessor.Flag
8486
flag_accessor.Flag.__getitem__
8587
flag_accessor.Flag.__setitem__
86-
flag_accessor.Flag.value
88+
flag_accessor.Flag.value
89+
90+
Utility functions
91+
=================
92+
93+
.. autosummary::
94+
:toctree: generated/
95+
96+
utils.append_names

docs/content/user/unc_accessor.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,16 @@ A component of uncertainty can be simply be deleted as,
186186
# Check uncertainties
187187
ds.unc["temperature"].keys()
188188
189-
Renaming Variables
190-
------------------
189+
Renaming Variables and Dimensions
190+
---------------------------------
191191

192-
The storage of uncertainty information is underpinned by variable attributes, which include referencing other variables (for example, which variables are the uncertainties associated with a particular observation variable). Because of this it is important, if renaming uncertainty variables, to use **obsarray**'s renaming functionality. This renames the uncertainty variable and safely updates attribute variable references. This is done as follows:
192+
The storage of uncertainty information is underpinned by variable attributes, which include referencing other variables/dimensions (for example, which variables are the uncertainties associated with a particular observation variable). Because of this it is important, if renaming uncertainty variables or dimensions, to use **obsarray**'s renaming functionality. This renames the uncertainty variable or dimension and safely updates attribute variable references. This is done as follows (mirroring the interface to `xarray renaming <https://docs.xarray.dev/en/latest/generated/xarray.Dataset.rename.html>`_):
193193

194194

195195
.. ipython:: python
196196
:okwarning:
197197
198198
print(ds.unc["temperature"])
199-
ds = ds.unc["temperature"]["u_ran_temperature"].rename("u_noise")
199+
ds = ds.unc.rename({"u_ran_temperature": "u_noise"})
200+
ds = ds.unc.rename_dims({"time": "t"})
200201
print(ds.unc["temperature"])

obsarray/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from obsarray.templater.template_util import create_ds
1010
from obsarray.templater.dstemplater import DSTemplater
1111
from obsarray.templater.dswriter import DSWriter
12+
from obsarray.utils import append_names
1213

1314
__version__ = get_versions()["version"]
1415
del get_versions

obsarray/test/test_unc_accessor.py

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def compare_err_corr_form(self, form, exp_form):
3434
self.assertCountEqual(form._unc_var_name, exp_form._unc_var_name)
3535

3636

37-
def create_ds():
37+
def create_ds(var_suffix="", dim_suffix="", coord_dim_suffix_extra=""):
3838
np.random.seed(0)
3939
temperature = 15 + 8 * np.random.randn(2, 2, 3)
4040
u_r_temperature = temperature * 0.02
@@ -47,42 +47,59 @@ def create_ds():
4747
reference_time = pd.Timestamp("2014-09-05")
4848

4949
ds = xr.Dataset(
50-
data_vars=dict(
51-
temperature=(["x", "y", "time"], temperature, {"units": "K"}),
52-
),
53-
coords=dict(
54-
lon=(["x", "y"], lon),
55-
lat=(["x", "y"], lat),
56-
time=time,
57-
reference_time=reference_time,
58-
),
50+
data_vars={
51+
"temperature"
52+
+ var_suffix: (
53+
[
54+
"x" + dim_suffix,
55+
"y" + dim_suffix,
56+
"time" + dim_suffix + coord_dim_suffix_extra,
57+
],
58+
temperature,
59+
{"units": "K"},
60+
),
61+
},
62+
coords={
63+
"lon" + var_suffix: (["x" + dim_suffix, "y" + dim_suffix], lon),
64+
"lat" + var_suffix: (["x" + dim_suffix, "y" + dim_suffix], lat),
65+
"time" + var_suffix: ("time" + dim_suffix + coord_dim_suffix_extra, time),
66+
"reference_time": reference_time,
67+
},
5968
attrs=dict(description="Weather related data."),
6069
)
6170

62-
ds.unc["temperature"]["u_ran_temperature"] = (
63-
["x", "y", "time"],
71+
ds.unc["temperature" + var_suffix]["u_ran_temperature" + var_suffix] = (
72+
[
73+
"x" + dim_suffix,
74+
"y" + dim_suffix,
75+
"time" + dim_suffix + coord_dim_suffix_extra,
76+
],
6477
temperature * 0.05,
6578
{"units": "K", "pdf_shape": "gaussian"},
6679
)
6780

68-
ds.unc["temperature"]["u_sys_temperature"] = (
69-
["x", "y", "time"],
81+
ds.unc["temperature" + var_suffix]["u_sys_temperature" + var_suffix] = (
82+
[
83+
"x" + dim_suffix,
84+
"y" + dim_suffix,
85+
"time" + dim_suffix + coord_dim_suffix_extra,
86+
],
7087
temperature * 0.03,
7188
{
7289
"units": "K",
7390
"err_corr": [
7491
{
75-
"dim": "x",
92+
"dim": "x" + dim_suffix,
7693
"form": "systematic",
7794
"params": [],
7895
},
7996
{
80-
"dim": "y",
97+
"dim": "y" + dim_suffix,
8198
"form": "systematic",
8299
"params": [],
83100
},
84101
{
85-
"dim": "time",
102+
"dim": "time" + dim_suffix + coord_dim_suffix_extra,
86103
"form": "systematic",
87104
"params": [],
88105
},
@@ -91,19 +108,26 @@ def create_ds():
91108
},
92109
)
93110

94-
ds.unc["temperature"]["u_str_temperature"] = (
95-
["x", "y", "time"],
111+
ds.unc["temperature" + var_suffix]["u_str_temperature" + var_suffix] = (
112+
[
113+
"x" + dim_suffix,
114+
"y" + dim_suffix,
115+
"time" + dim_suffix + coord_dim_suffix_extra,
116+
],
96117
temperature * 0.1,
97118
{
98119
"units": "K",
99120
"err_corr": [
100121
{
101-
"dim": ["x", "time"],
122+
"dim": [
123+
"x" + dim_suffix,
124+
"time" + dim_suffix + coord_dim_suffix_extra,
125+
],
102126
"form": "err_corr_matrix",
103-
"params": ["err_corr_str_temperature"],
127+
"params": ["err_corr_str_temperature" + var_suffix],
104128
},
105129
{
106-
"dim": "y",
130+
"dim": "y" + dim_suffix,
107131
"form": "systematic",
108132
"params": [],
109133
},
@@ -112,8 +136,8 @@ def create_ds():
112136
},
113137
)
114138

115-
ds["err_corr_str_temperature"] = (
116-
["x.time", "x.time"],
139+
ds["err_corr_str_temperature" + var_suffix] = (
140+
["x.time" + dim_suffix, "x.time" + dim_suffix],
117141
np.ones(
118142
(
119143
temperature.shape[0] * temperature.shape[2],
@@ -740,6 +764,44 @@ def test_err_cov_matrix(self, mock_cr2cv, mock_ecrm, mock_value):
740764
exp_ecm = xr.DataArray(np.ones((12, 12)), dims=["x.y.time", "x.y.time"])
741765
xr.testing.assert_equal(ecm, exp_ecm)
742766

767+
def test_rename_vars(self):
768+
var_suffix = "_test"
769+
input_ds = create_ds()
770+
771+
ds = input_ds.unc.rename(
772+
{
773+
"temperature": "temperature" + var_suffix,
774+
"lon": "lon" + var_suffix,
775+
"lat": "lat" + var_suffix,
776+
"time": "time" + var_suffix,
777+
"u_ran_temperature": "u_ran_temperature" + var_suffix,
778+
"u_str_temperature": "u_str_temperature" + var_suffix,
779+
"u_sys_temperature": "u_sys_temperature" + var_suffix,
780+
"err_corr_str_temperature": "err_corr_str_temperature" + var_suffix,
781+
}
782+
)
783+
784+
exp_ds = create_ds(var_suffix=var_suffix, coord_dim_suffix_extra=var_suffix)
785+
786+
xr.testing.assert_identical(ds, exp_ds)
787+
788+
def test_rename_dims(self):
789+
dim_suffix = "_test"
790+
input_ds = create_ds()
791+
792+
ds = input_ds.unc.rename_dims(
793+
{
794+
"x": "x" + dim_suffix,
795+
"y": "y" + dim_suffix,
796+
"time": "time" + dim_suffix,
797+
"x.time": "x.time_test",
798+
}
799+
)
800+
801+
exp_ds = create_ds(dim_suffix=dim_suffix)
802+
803+
xr.testing.assert_identical(ds, exp_ds)
804+
743805

744806
if __name__ == "__main__":
745807
unittest.main()

obsarray/test/test_utils.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""test_utils - tests for obsarray.utils"""
2+
3+
import unittest
4+
import numpy as np
5+
from obsarray import append_names, create_ds
6+
import xarray as xr
7+
8+
__author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
9+
__all__ = []
10+
11+
12+
def create_test_ds(suffix):
13+
# define ds variables
14+
template = {
15+
"temperature"
16+
+ suffix: {
17+
"dtype": np.float32,
18+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
19+
"attributes": {
20+
"units": "K",
21+
"unc_comps": [
22+
"u_ran_temperature" + suffix,
23+
"u_sys_temperature" + suffix,
24+
],
25+
},
26+
},
27+
"u_ran_temperature"
28+
+ suffix: {
29+
"dtype": np.float32,
30+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
31+
"attributes": {
32+
"units": "K",
33+
"err_corr": [
34+
{"dim": "x" + suffix, "form": "random", "params": [], "units": []},
35+
{"dim": "y" + suffix, "form": "random", "params": [], "units": []},
36+
{
37+
"dim": "time" + suffix,
38+
"form": "random",
39+
"params": [],
40+
"units": [],
41+
},
42+
],
43+
},
44+
},
45+
"u_sys_temperature"
46+
+ suffix: {
47+
"dtype": np.float32,
48+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
49+
"attributes": {
50+
"units": "K",
51+
"err_corr": [
52+
{
53+
"dim": "x" + suffix,
54+
"form": "systematic",
55+
"params": [],
56+
"units": [],
57+
},
58+
{
59+
"dim": "y" + suffix,
60+
"form": "systematic",
61+
"params": [],
62+
"units": [],
63+
},
64+
{
65+
"dim": "time" + suffix,
66+
"form": "systematic",
67+
"params": [],
68+
"units": [],
69+
},
70+
],
71+
},
72+
},
73+
"pressure"
74+
+ suffix: {
75+
"dtype": np.float32,
76+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
77+
"attributes": {"units": "Pa", "unc_comps": ["u_str_pressure" + suffix]},
78+
},
79+
"u_str_pressure"
80+
+ suffix: {
81+
"dtype": np.float32,
82+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
83+
"attributes": {
84+
"units": "Pa",
85+
"err_corr": [
86+
{"dim": "x" + suffix, "form": "random", "params": [], "units": []},
87+
{
88+
"dim": "y" + suffix,
89+
"form": "err_corr_matrix",
90+
"params": "err_corr_str_pressure_y",
91+
"units": [],
92+
},
93+
{
94+
"dim": "time" + suffix,
95+
"form": "systematic",
96+
"params": [],
97+
"units": [],
98+
},
99+
],
100+
},
101+
},
102+
"err_corr_str_pressure_y"
103+
+ suffix: {
104+
"dtype": np.float32,
105+
"dim": ["y" + suffix, "y" + suffix],
106+
"attributes": {"units": ""},
107+
},
108+
"n_moles"
109+
+ suffix: {
110+
"dtype": np.float32,
111+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
112+
"attributes": {"units": "", "unc_comps": ["u_ran_n_moles" + suffix]},
113+
},
114+
"u_ran_n_moles"
115+
+ suffix: {
116+
"dtype": np.float32,
117+
"dim": ["x" + suffix, "y" + suffix, "time" + suffix],
118+
"attributes": {
119+
"units": "",
120+
"err_corr": [
121+
{"dim": "x" + suffix, "form": "random", "params": [], "units": []},
122+
{"dim": "y" + suffix, "form": "random", "params": [], "units": []},
123+
{
124+
"dim": "time" + suffix,
125+
"form": "random",
126+
"params": [],
127+
"units": [],
128+
},
129+
],
130+
},
131+
},
132+
}
133+
134+
# define dim_size_dict to specify size of arrays
135+
dim_sizes = {"x" + suffix: 20, "y" + suffix: 30, "time" + suffix: 6}
136+
137+
# create dataset template
138+
ds = create_ds(template, dim_sizes)
139+
140+
# populate with example data
141+
ds["temperature" + suffix].values = 293 * np.ones((20, 30, 6))
142+
ds["u_ran_temperature" + suffix].values = 1 * np.ones((20, 30, 6))
143+
ds["u_sys_temperature" + suffix].values = 0.4 * np.ones((20, 30, 6))
144+
ds["pressure" + suffix].values = 10**5 * np.ones((20, 30, 6))
145+
ds["u_str_pressure" + suffix].values = 10 * np.ones((20, 30, 6))
146+
ds["err_corr_str_pressure_y" + suffix].values = 0.5 * np.ones(
147+
(30, 30)
148+
) + 0.5 * np.eye(30)
149+
ds["n_moles" + suffix].values = 40 * np.ones((20, 30, 6))
150+
ds["u_ran_n_moles" + suffix].values = 1 * np.ones((20, 30, 6))
151+
152+
ds.attrs["attr" + suffix] = "val"
153+
154+
return ds
155+
156+
157+
class TestAppendNames(unittest.TestCase):
158+
def test_append_names(self):
159+
160+
input_ds = create_test_ds(suffix="")
161+
ds = append_names(input_ds, "_test")
162+
163+
exp_ds = create_test_ds(suffix="_test")
164+
165+
xr.testing.assert_identical(ds, exp_ds)
166+
167+
168+
if __name__ == "__main__":
169+
unittest.main()

0 commit comments

Comments
 (0)