1
1
# TODO is it possible to import pint-xarray from within xarray if pint is present?
2
- from xarray import (register_dataarray_accessor , register_dataset_accessor ,
3
- DataArray , Dataset , Variable )
2
+ from xarray import (
3
+ register_dataarray_accessor ,
4
+ register_dataset_accessor ,
5
+ DataArray ,
6
+ Dataset ,
7
+ Variable ,
8
+ )
4
9
from xarray .core .npcompat import IS_NEP18_ACTIVE
5
10
6
11
import numpy as np
11
16
12
17
13
18
if not hasattr (Quantity , "__array_function__" ):
14
- raise ImportError ("Imported version of pint does not implement "
15
- "__array_function__" )
19
+ raise ImportError (
20
+ "Imported version of pint does not implement " "__array_function__"
21
+ )
16
22
17
23
if not IS_NEP18_ACTIVE :
18
24
raise ImportError ("NUMPY_EXPERIMENTAL_ARRAY_FUNCTION is not enabled" )
@@ -34,26 +40,27 @@ def _array_attach_units(data, unit, convert_from=None):
34
40
35
41
if isinstance (data , Quantity ):
36
42
if not convert_from :
37
- raise ValueError (f"Cannot attach unit { unit } to quantity: data "
38
- f"already has units { data .units } " )
43
+ raise ValueError (
44
+ f"Cannot attach unit { unit } to quantity: data "
45
+ f"already has units { data .units } "
46
+ )
39
47
elif isinstance (convert_from , Unit ):
40
48
data = data .magnitude
41
49
elif convert_from is True : # intentionally accept exactly true
42
50
if data .check (unit ):
43
51
convert_from = data .units
44
52
data = data .magnitude
45
53
else :
46
- raise ValueError ("Cannot convert quantity from {data.units} "
47
- "to {unit}" )
54
+ raise ValueError (
55
+ "Cannot convert quantity from {data.units} " "to {unit}"
56
+ )
48
57
else :
49
58
raise ValueError ("Cannot convert from invalid unit {convert_from}" )
50
59
51
60
# to make sure we also encounter the case of "equal if converted"
52
61
if convert_from is not None :
53
62
quantity = (data * convert_from ).to (
54
- unit
55
- if isinstance (unit , Unit )
56
- else unit .dimensionless
63
+ unit if isinstance (unit , Unit ) else unit .dimensionless
57
64
)
58
65
else :
59
66
try :
@@ -67,6 +74,7 @@ def _array_attach_units(data, unit, convert_from=None):
67
74
68
75
return quantity
69
76
77
+
70
78
def _get_registry (unit_registry , registry_kwargs ):
71
79
if unit_registry is None :
72
80
if registry_kwargs is None :
@@ -76,10 +84,11 @@ def _get_registry(unit_registry, registry_kwargs):
76
84
unit_registry = pint .UnitRegistry (** registry_kwargs )
77
85
return unit_registry
78
86
87
+
79
88
def _decide_units (units , registry , attrs ):
80
89
if units is None :
81
90
# TODO option to read and decode units according to CF conventions (see MetPy)?
82
- attr_units = attrs [' units' ]
91
+ attr_units = attrs [" units" ]
83
92
units = registry .parse_expression (attr_units )
84
93
elif isinstance (units , Unit ):
85
94
# TODO do we have to check what happens if someone passes a Unit instance
@@ -90,16 +99,16 @@ def _decide_units(units, registry, attrs):
90
99
units = registry .Unit (units )
91
100
return units
92
101
102
+
93
103
def _quantify_variable (var , units ):
94
104
new_data = _array_attach_units (var .data , units , convert_from = None )
95
- new_var = Variable (dims = var .dims , data = new_data ,
96
- attrs = var .attrs )
105
+ new_var = Variable (dims = var .dims , data = new_data , attrs = var .attrs )
97
106
return new_var
98
107
108
+
99
109
def _dequantify_variable (var ):
100
- new_var = Variable (dims = var .dims , data = var .data .magnitude ,
101
- attrs = var .attrs )
102
- new_var .attrs ['units' ] = str (var .data .units )
110
+ new_var = Variable (dims = var .dims , data = var .data .magnitude , attrs = var .attrs )
111
+ new_var .attrs ["units" ] = str (var .data .units )
103
112
return new_var
104
113
105
114
@@ -152,8 +161,10 @@ def quantify(self, units=None, unit_registry=None, registry_kwargs=None):
152
161
# TODO should also quantify coordinates (once explicit indexes ready)
153
162
154
163
if isinstance (self .da .data , Quantity ):
155
- raise ValueError (f"Cannot attach unit { units } to quantity: data "
156
- f"already has units { self .da .data .units } " )
164
+ raise ValueError (
165
+ f"Cannot attach unit { units } to quantity: data "
166
+ f"already has units { self .da .data .units } "
167
+ )
157
168
158
169
registry = _get_registry (unit_registry , registry_kwargs )
159
170
@@ -162,8 +173,9 @@ def quantify(self, units=None, unit_registry=None, registry_kwargs=None):
162
173
quantity = _array_attach_units (self .da .data , units , convert_from = None )
163
174
164
175
# TODO should we (temporarily) remove the attrs here so that they don't become inconsistent?
165
- return DataArray (dims = self .da .dims , data = quantity ,
166
- coords = self .da .coords , attrs = self .da .attrs )
176
+ return DataArray (
177
+ dims = self .da .dims , data = quantity , coords = self .da .coords , attrs = self .da .attrs
178
+ )
167
179
168
180
def dequantify (self ):
169
181
"""
@@ -179,13 +191,18 @@ def dequantify(self):
179
191
"""
180
192
181
193
if not isinstance (self .da .data , Quantity ):
182
- raise ValueError ("Cannot remove units from data that does not have"
183
- " units" )
194
+ raise ValueError (
195
+ "Cannot remove units from data that does not have" " units"
196
+ )
184
197
185
198
# TODO also dequantify coords (once explicit indexes ready)
186
- da = DataArray (dims = self .da .dims , data = self .da .pint .magnitude ,
187
- coords = self .da .coords , attrs = self .da .attrs )
188
- da .attrs ['units' ] = str (self .da .data .units )
199
+ da = DataArray (
200
+ dims = self .da .dims ,
201
+ data = self .da .pint .magnitude ,
202
+ coords = self .da .coords ,
203
+ attrs = self .da .attrs ,
204
+ )
205
+ da .attrs ["units" ] = str (self .da .data .units )
189
206
return da
190
207
191
208
@property
@@ -199,8 +216,9 @@ def units(self):
199
216
@units .setter
200
217
def units (self , units ):
201
218
quantity = _array_attach_units (self .da .data , units )
202
- self .da = DataArray (dim = self .da .dims , data = quantity ,
203
- coords = self .da .coords , attrs = self .da .attrs )
219
+ self .da = DataArray (
220
+ dim = self .da .dims , data = quantity , coords = self .da .coords , attrs = self .da .attrs
221
+ )
204
222
205
223
@property
206
224
def dimensionality (self ):
@@ -217,25 +235,38 @@ def registry(self, _):
217
235
218
236
def to (self , units ):
219
237
quantity = self .da .data .to (units )
220
- return DataArray (dim = self .da .dims , data = quantity ,
221
- coords = self .da .coords , attrs = self .da .attrs ,
222
- encoding = self .da .encoding )
238
+ return DataArray (
239
+ dim = self .da .dims ,
240
+ data = quantity ,
241
+ coords = self .da .coords ,
242
+ attrs = self .da .attrs ,
243
+ encoding = self .da .encoding ,
244
+ )
223
245
224
246
def to_base_units (self ):
225
247
quantity = self .da .data .to_base_units ()
226
- return DataArray (dim = self .da .dims , data = quantity ,
227
- coords = self .da .coords , attrs = self .da .attrs ,
228
- encoding = self .da .encoding )
248
+ return DataArray (
249
+ dim = self .da .dims ,
250
+ data = quantity ,
251
+ coords = self .da .coords ,
252
+ attrs = self .da .attrs ,
253
+ encoding = self .da .encoding ,
254
+ )
229
255
230
256
# TODO integrate with the uncertainties package here...?
231
257
def plus_minus (self , value , error , relative = False ):
232
258
quantity = self .da .data .plus_minus (value , error , relative )
233
- return DataArray (dim = self .da .dims , data = quantity ,
234
- coords = self .da .coords , attrs = self .da .attrs ,
235
- encoding = self .da .encoding )
259
+ return DataArray (
260
+ dim = self .da .dims ,
261
+ data = quantity ,
262
+ coords = self .da .coords ,
263
+ attrs = self .da .attrs ,
264
+ encoding = self .da .encoding ,
265
+ )
236
266
237
- def sel (self , indexers = None , method = None , tolerance = None , drop = False ,
238
- ** indexers_kwargs ):
267
+ def sel (
268
+ self , indexers = None , method = None , tolerance = None , drop = False , ** indexers_kwargs
269
+ ):
239
270
...
240
271
241
272
@property
@@ -250,6 +281,7 @@ class PintDatasetAccessor:
250
281
251
282
Methods and attributes can be accessed through the `.pint` attribute.
252
283
"""
284
+
253
285
def __init__ (self , ds ):
254
286
self .ds = ds
255
287
@@ -284,44 +316,50 @@ def quantify(self, units=None, unit_registry=None, registry_kwargs=None):
284
316
285
317
for var in self .ds .data_vars :
286
318
if isinstance (self .ds [var ].data , Quantity ):
287
- raise ValueError (f"Cannot attach unit to quantity: data "
288
- f"variable { var } already has units "
289
- f"{ self .ds [var ].data .units } " )
319
+ raise ValueError (
320
+ f"Cannot attach unit to quantity: data "
321
+ f"variable { var } already has units "
322
+ f"{ self .ds [var ].data .units } "
323
+ )
290
324
291
325
registry = _get_registry (unit_registry , registry_kwargs )
292
326
293
327
if units is None :
294
328
units = {name : None for name in self .ds }
295
329
296
- units = {name : _decide_units (units .get (name , None ), registry , var .attrs )
297
- for name , var in self .ds .data_vars .items ()}
330
+ units = {
331
+ name : _decide_units (units .get (name , None ), registry , var .attrs )
332
+ for name , var in self .ds .data_vars .items ()
333
+ }
298
334
299
- quantified_vars = {name : _quantify_variable (var , units [name ])
300
- for name , var in self .ds .data_vars .items ()}
335
+ quantified_vars = {
336
+ name : _quantify_variable (var , units [name ])
337
+ for name , var in self .ds .data_vars .items ()
338
+ }
301
339
302
340
# TODO should also quantify coordinates (once explicit indexes ready)
303
341
# TODO should we (temporarily) remove the attrs here so that they don't become inconsistent?
304
- return Dataset (data_vars = quantified_vars , coords = self .ds .coords ,
305
- attrs = self .ds .attrs )
342
+ return Dataset (
343
+ data_vars = quantified_vars , coords = self .ds .coords , attrs = self .ds .attrs
344
+ )
306
345
307
346
def dequantify (self ):
308
- dequantified_vars = {name : da . pint . to_base_units ()
309
- for name , da in self .ds .items ()}
310
- return Dataset ( dequantified_vars , coords = self . ds . coords ,
311
- attrs = self .ds .attrs )
347
+ dequantified_vars = {
348
+ name : da . pint . to_base_units () for name , da in self .ds .items ()
349
+ }
350
+ return Dataset ( dequantified_vars , coords = self . ds . coords , attrs = self .ds .attrs )
312
351
313
352
def to_base_units (self ):
314
- base_vars = {name : da .pint .to_base_units ()
315
- for name , da in self .ds .items ()}
316
- return Dataset (base_vars , coords = self .ds .coords ,
317
- attrs = self .ds .attrs )
353
+ base_vars = {name : da .pint .to_base_units () for name , da in self .ds .items ()}
354
+ return Dataset (base_vars , coords = self .ds .coords , attrs = self .ds .attrs )
318
355
319
356
# TODO unsure if the upstream capability exists in pint for this yet.
320
357
def to_system (self , system ):
321
358
raise NotImplementedError
322
359
323
- def sel (self , indexers = None , method = None , tolerance = None , drop = False ,
324
- ** indexers_kwargs ):
360
+ def sel (
361
+ self , indexers = None , method = None , tolerance = None , drop = False , ** indexers_kwargs
362
+ ):
325
363
...
326
364
327
365
@property
0 commit comments