obsarray 1.0.2__tar.gz → 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {obsarray-1.0.2/obsarray.egg-info → obsarray-1.0.3}/PKG-INFO +1 -1
  2. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/__init__.py +1 -0
  3. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/_version.py +3 -3
  4. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/flag_accessor.py +6 -6
  5. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/dataset_util.py +1 -1
  6. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/test/test_unc_accessor.py +86 -24
  7. obsarray-1.0.3/obsarray/test/test_utils.py +169 -0
  8. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/unc_accessor.py +99 -4
  9. obsarray-1.0.3/obsarray/utils.py +66 -0
  10. {obsarray-1.0.2 → obsarray-1.0.3/obsarray.egg-info}/PKG-INFO +1 -1
  11. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray.egg-info/SOURCES.txt +2 -1
  12. {obsarray-1.0.2 → obsarray-1.0.3}/versioneer.py +183 -126
  13. obsarray-1.0.2/obsarray/utils.py +0 -31
  14. {obsarray-1.0.2 → obsarray-1.0.3}/LICENSE +0 -0
  15. {obsarray-1.0.2 → obsarray-1.0.3}/MANIFEST.in +0 -0
  16. {obsarray-1.0.2 → obsarray-1.0.3}/README.md +0 -0
  17. {obsarray-1.0.2 → obsarray-1.0.3}/docs/conf.py +0 -0
  18. {obsarray-1.0.2 → obsarray-1.0.3}/notebook/test_register_err_corr_form.py +0 -0
  19. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/err_corr.py +0 -0
  20. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/__init__.py +0 -0
  21. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/dstemplater.py +0 -0
  22. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/dswriter.py +0 -0
  23. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/template_util.py +0 -0
  24. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/tests/__init__.py +0 -0
  25. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/tests/test_dataset_util.py +0 -0
  26. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/tests/test_dswriter.py +0 -0
  27. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/templater/tests/test_template_util.py +0 -0
  28. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/test/__init__.py +0 -0
  29. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/test/test_err_corr_forms.py +0 -0
  30. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray/test/test_flag_accessor.py +0 -0
  31. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray.egg-info/dependency_links.txt +0 -0
  32. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray.egg-info/requires.txt +0 -0
  33. {obsarray-1.0.2 → obsarray-1.0.3}/obsarray.egg-info/top_level.txt +0 -0
  34. {obsarray-1.0.2 → obsarray-1.0.3}/pyproject.toml +0 -0
  35. {obsarray-1.0.2 → obsarray-1.0.3}/setup.cfg +0 -0
  36. {obsarray-1.0.2 → obsarray-1.0.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obsarray
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Measurement data handling in Python
5
5
  Author-email: CoMet Toolkit Team <team@comet-toolkit.org>
6
6
  Project-URL: Homepage, https://github.com/comet-toolkit/obsarray
@@ -9,6 +9,7 @@ from obsarray.err_corr import err_corr_forms
9
9
  from obsarray.templater.template_util import create_ds
10
10
  from obsarray.templater.dstemplater import DSTemplater
11
11
  from obsarray.templater.dswriter import DSWriter
12
+ from obsarray.utils import append_names
12
13
 
13
14
  __version__ = get_versions()["version"]
14
15
  del get_versions
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-04-25T14:06:04+0100",
11
+ "date": "2025-06-22T11:46:36+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "b26b43a5d2e41ccb9a5e73c00e6d16a28a8515a9",
15
- "version": "1.0.2"
14
+ "full-revisionid": "48598121962220e8e42df0515693a450e732585c",
15
+ "version": "1.0.3"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -194,12 +194,12 @@ class FlagVariable:
194
194
  )
195
195
 
196
196
  if flag_meaning not in flag_meanings:
197
- self._obj[self._flag_var_name].attrs = (
198
- DatasetUtil.add_flag_meaning_to_attrs(
199
- self._obj[self._flag_var_name].attrs,
200
- flag_meaning,
201
- self._obj[self._flag_var_name].dtype,
202
- )
197
+ self._obj[
198
+ self._flag_var_name
199
+ ].attrs = DatasetUtil.add_flag_meaning_to_attrs(
200
+ self._obj[self._flag_var_name].attrs,
201
+ flag_meaning,
202
+ self._obj[self._flag_var_name].dtype,
203
203
  )
204
204
 
205
205
  self[flag_meaning][:] = flag_value
@@ -507,7 +507,7 @@ class DatasetUtil:
507
507
  ds[flag_meaning] = DatasetUtil.create_variable(
508
508
  list(da.shape), bool, dim_names=list(da.dims)
509
509
  )
510
- ds[flag_meaning] = (da & flag_mask).astype(bool)
510
+ ds[flag_meaning] = (da.astype(int) & flag_mask).astype(bool)
511
511
 
512
512
  return ds
513
513
 
@@ -34,7 +34,7 @@ def compare_err_corr_form(self, form, exp_form):
34
34
  self.assertCountEqual(form._unc_var_name, exp_form._unc_var_name)
35
35
 
36
36
 
37
- def create_ds():
37
+ def create_ds(var_suffix="", dim_suffix="", coord_dim_suffix_extra=""):
38
38
  np.random.seed(0)
39
39
  temperature = 15 + 8 * np.random.randn(2, 2, 3)
40
40
  u_r_temperature = temperature * 0.02
@@ -47,42 +47,59 @@ def create_ds():
47
47
  reference_time = pd.Timestamp("2014-09-05")
48
48
 
49
49
  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
+ },
59
68
  attrs=dict(description="Weather related data."),
60
69
  )
61
70
 
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
+ ],
64
77
  temperature * 0.05,
65
78
  {"units": "K", "pdf_shape": "gaussian"},
66
79
  )
67
80
 
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
+ ],
70
87
  temperature * 0.03,
71
88
  {
72
89
  "units": "K",
73
90
  "err_corr": [
74
91
  {
75
- "dim": "x",
92
+ "dim": "x" + dim_suffix,
76
93
  "form": "systematic",
77
94
  "params": [],
78
95
  },
79
96
  {
80
- "dim": "y",
97
+ "dim": "y" + dim_suffix,
81
98
  "form": "systematic",
82
99
  "params": [],
83
100
  },
84
101
  {
85
- "dim": "time",
102
+ "dim": "time" + dim_suffix + coord_dim_suffix_extra,
86
103
  "form": "systematic",
87
104
  "params": [],
88
105
  },
@@ -91,19 +108,26 @@ def create_ds():
91
108
  },
92
109
  )
93
110
 
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
+ ],
96
117
  temperature * 0.1,
97
118
  {
98
119
  "units": "K",
99
120
  "err_corr": [
100
121
  {
101
- "dim": ["x", "time"],
122
+ "dim": [
123
+ "x" + dim_suffix,
124
+ "time" + dim_suffix + coord_dim_suffix_extra,
125
+ ],
102
126
  "form": "err_corr_matrix",
103
- "params": ["err_corr_str_temperature"],
127
+ "params": ["err_corr_str_temperature" + var_suffix],
104
128
  },
105
129
  {
106
- "dim": "y",
130
+ "dim": "y" + dim_suffix,
107
131
  "form": "systematic",
108
132
  "params": [],
109
133
  },
@@ -112,8 +136,8 @@ def create_ds():
112
136
  },
113
137
  )
114
138
 
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],
117
141
  np.ones(
118
142
  (
119
143
  temperature.shape[0] * temperature.shape[2],
@@ -740,6 +764,44 @@ class TestUncertainty(unittest.TestCase):
740
764
  exp_ecm = xr.DataArray(np.ones((12, 12)), dims=["x.y.time", "x.y.time"])
741
765
  xr.testing.assert_equal(ecm, exp_ecm)
742
766
 
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
+
743
805
 
744
806
  if __name__ == "__main__":
745
807
  unittest.main()
@@ -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()
@@ -9,6 +9,7 @@ from obsarray.templater.template_util import create_var
9
9
  from obsarray.templater.dataset_util import DatasetUtil
10
10
  from obsarray.err_corr import err_corr_forms, BaseErrCorrForm
11
11
  from obsarray.utils import empty_err_corr_matrix
12
+ from xarray.core.types import T_Dataset
12
13
 
13
14
 
14
15
  __author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
@@ -308,12 +309,24 @@ class Uncertainty:
308
309
  ]
309
310
  ):
310
311
  if dim_err_corr[1].form in ["random", "systematic"]:
311
- err_corr_dict[dim_err_corr[0]] = dim_err_corr[1].form
312
+ if isinstance(dim_err_corr[0], List):
313
+ for dim in dim_err_corr[0]:
314
+ err_corr_dict[dim] = dim_err_corr[1].form
315
+
316
+ else:
317
+ err_corr_dict[dim_err_corr[0]] = dim_err_corr[1].form
312
318
 
313
319
  elif dim_err_corr[1].form == "err_corr_matrix":
314
- err_corr_dict[dim_err_corr[0]] = self._obj[
315
- dim_err_corr[1].params[0]
316
- ].values
320
+ if isinstance(dim_err_corr[0], List):
321
+ for dim in dim_err_corr[0]:
322
+ err_corr_dict[dim] = self._obj[
323
+ dim_err_corr[1].params[0]
324
+ ].values
325
+
326
+ else:
327
+ err_corr_dict[dim_err_corr[0]] = self._obj[
328
+ dim_err_corr[1].params[0]
329
+ ].values
317
330
 
318
331
  else:
319
332
  raise NotImplementedError(
@@ -894,6 +907,88 @@ class UncAccessor(object):
894
907
  del self._obj[unc_var]
895
908
  self._obj[obs_var].attrs["unc_comps"].remove(unc_var)
896
909
 
910
+ def rename(self, vars_dict: dict[str, str]) -> T_Dataset:
911
+ """
912
+ Returns a new dataset with renamed variables - safely handling `unc_vars` and related metadata
913
+
914
+ :param vars_dict: Dictionary whose keys are current variable names and whose values are the desired names. The desired names must not be the name of an existing dimension or Variable in the Dataset.
915
+ :returns: Dataset with renamed variables
916
+ """
917
+
918
+ # handle case that xarray.Dataset.rename renames the dimension associated with a renamed coordinate dimension
919
+ coord_dim_dict = {
920
+ str(dim): vars_dict[dim]
921
+ for dim in self._obj.dims
922
+ if (dim in self._obj.coords) and (dim in vars_dict.keys())
923
+ }
924
+ ds = self.rename_dims(coord_dim_dict)
925
+
926
+ # update metadata where unc_var err corr param to be renamed
927
+ unc_var_paths = []
928
+ for obs_var in ds.unc.obs_vars:
929
+ for unc_var in ds.unc[obs_var]:
930
+ unc_var_paths.append((obs_var, unc_var._unc_var_name))
931
+
932
+ for unc_var_path in unc_var_paths:
933
+ unc_var_i = unc_var_path[1]
934
+
935
+ for attr in ds[unc_var_i].attrs.keys():
936
+ if (attr[:9] == "err_corr_") and (attr[-7:] == "_params"):
937
+ for i, param in enumerate(ds[unc_var_i].attrs[attr]):
938
+ if param in vars_dict.keys():
939
+ ds[unc_var_i].attrs[attr][i] = vars_dict[param]
940
+
941
+ # safely update unc_vars
942
+ for unc_var_path in unc_var_paths:
943
+ obs_var_i = unc_var_path[0]
944
+ unc_var_i = unc_var_path[1]
945
+ if unc_var_i in vars_dict:
946
+ ds = ds.unc[obs_var_i][unc_var_i].rename(vars_dict[unc_var_i])
947
+
948
+ # update remaining variable names
949
+ non_unc_var_names = list(
950
+ filter(lambda x: x not in self.unc_vars.keys(), self._obj.variables.keys())
951
+ )
952
+ var_dict_no_unc = {
953
+ n: vars_dict[n] for n in non_unc_var_names if n in vars_dict.keys()
954
+ }
955
+ ds = ds.rename(var_dict_no_unc)
956
+
957
+ return ds
958
+
959
+ def rename_dims(self, dims_dict: dict[str, str]) -> T_Dataset:
960
+ """
961
+ Returns a new dataset with renamed dimensions - safely handling `unc_vars` related metadata
962
+
963
+ :param dims_dict: Dictionary whose keys are current dimension names and whose values are the desired names. The desired names must not be the name of an existing dimension or Variable in the Dataset.
964
+ :returns: Dataset with renamed dimensions
965
+ """
966
+
967
+ # update dimension names
968
+ obj = self._obj.rename_dims(dims_dict)
969
+
970
+ # update uncertainty metadata related to variable names
971
+ unc_var_names = []
972
+ for obs_var in obj.unc.obs_vars:
973
+ for unc_var in obj.unc[obs_var]:
974
+ unc_var_names.append(unc_var._unc_var_name)
975
+
976
+ for unc_var_name in unc_var_names:
977
+ for attr in obj[unc_var_name].attrs.keys():
978
+ if (attr[:9] == "err_corr_") and (attr[-4:] == "_dim"):
979
+ if isinstance(obj[unc_var_name].attrs[attr], str):
980
+ if obj[unc_var_name].attrs[attr] in dims_dict:
981
+ obj[unc_var_name].attrs[attr] = dims_dict[
982
+ obj[unc_var_name].attrs[attr]
983
+ ]
984
+
985
+ if isinstance(obj[unc_var_name].attrs[attr], list):
986
+ for i, attr_i in enumerate(obj[unc_var_name].attrs[attr]):
987
+ if attr_i in dims_dict:
988
+ obj[unc_var_name].attrs[attr][i] = dims_dict[attr_i]
989
+
990
+ return obj
991
+
897
992
 
898
993
  if __name__ == "__main__":
899
994
  pass
@@ -0,0 +1,66 @@
1
+ """utils - utilities for obsarray"""
2
+
3
+ import numpy as np
4
+ import xarray as xr
5
+ from xarray.core.types import T_Dataset
6
+
7
+ __author__ = "Sam Hunt <sam.hunt@npl.co.uk>"
8
+ __all__ = ["empty_err_corr_matrix", "append_names"]
9
+
10
+
11
+ def empty_err_corr_matrix(obs_var: xr.DataArray):
12
+ """
13
+ Returns diagonal error correlation matrix for observation variable
14
+
15
+ :param obs_var: uncertainty variable
16
+ :return: diagonal error correlation matrix
17
+ """
18
+
19
+ dim_names = [dim for dim in obs_var.dims]
20
+ dim_lens = [len(obs_var[dim]) for dim in dim_names]
21
+
22
+ n_elems = int(np.prod(dim_lens))
23
+ erc_dim_name = ".".join(dim_names)
24
+
25
+ err_corr_matrix = xr.DataArray(np.eye(n_elems), dims=[erc_dim_name, erc_dim_name])
26
+
27
+ return err_corr_matrix
28
+
29
+
30
+ def append_names(
31
+ ds: T_Dataset,
32
+ suffix: str,
33
+ skip_vars: bool = False,
34
+ skip_dims: bool = False,
35
+ skip_attrs: bool = False,
36
+ ) -> T_Dataset:
37
+ """
38
+ Appends a suffix to the names of dataset variables, dimensions, and attributes - safely handling `unc_vars` and associated metadata
39
+
40
+ :param ds: xarray dataset
41
+ :param suffix: suffix to append to dataset variable, dimension, and attribute names
42
+ :param skip_vars: (default: `False`) switch to skip applying suffix to variable names
43
+ :param skip_dims: (default: `False`) switch to skip applying suffix to dimension names
44
+ :param skip_attrs: (default: `False`) switch to skip applying suffix to variable names
45
+ :returns: ds with suffix appended to names of variables, dimensions, attributes
46
+ """
47
+
48
+ # update variable names
49
+ if not skip_vars:
50
+ var_rename = {var_name: var_name + suffix for var_name in ds.variables.keys()}
51
+ ds = ds.unc.rename(var_rename)
52
+
53
+ # update dimension names
54
+ if not skip_dims:
55
+ dim_rename = {dim_name: dim_name + suffix for dim_name in ds.dims.keys()}
56
+ ds = ds.unc.rename_dims(dim_rename)
57
+
58
+ # update attribute names
59
+ if not skip_attrs:
60
+ ds.attrs = {key + suffix: value for key, value in ds.attrs.items()}
61
+
62
+ return ds
63
+
64
+
65
+ if __name__ == "__main__":
66
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obsarray
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Measurement data handling in Python
5
5
  Author-email: CoMet Toolkit Team <team@comet-toolkit.org>
6
6
  Project-URL: Homepage, https://github.com/comet-toolkit/obsarray
@@ -29,4 +29,5 @@ obsarray/templater/tests/test_template_util.py
29
29
  obsarray/test/__init__.py
30
30
  obsarray/test/test_err_corr_forms.py
31
31
  obsarray/test/test_flag_accessor.py
32
- obsarray/test/test_unc_accessor.py
32
+ obsarray/test/test_unc_accessor.py
33
+ obsarray/test/test_utils.py