8
8
from xarray .core .dtypes import NA
9
9
10
10
from . import conversion
11
+ from .conversion import no_unit_values
11
12
from .errors import format_error_message
12
13
14
+ _default = object ()
15
+
13
16
14
17
def setup_registry (registry ):
15
18
"""set up the given registry for use with pint_xarray
@@ -91,7 +94,7 @@ def units_to_str_or_none(mapping, unit_format):
91
94
# based on xarray.core.utils.either_dict_or_kwargs
92
95
# https://github.com/pydata/xarray/blob/v0.15.1/xarray/core/utils.py#L249-L268
93
96
def either_dict_or_kwargs (positional , keywords , method_name ):
94
- if positional is not None :
97
+ if positional not in ( _default , None ) :
95
98
if not is_dict_like (positional ):
96
99
raise ValueError (
97
100
f"the first argument to .{ method_name } must be a dictionary"
@@ -131,17 +134,16 @@ def get_registry(unit_registry, new_units, existing_units):
131
134
132
135
133
136
def _decide_units (units , registry , unit_attribute ):
134
- if units is None and unit_attribute is None :
137
+ if units is _default and unit_attribute is _default :
135
138
# or warn and return None?
136
139
raise ValueError ("no units given" )
137
- elif units is None :
138
- # TODO option to read and decode units according to CF conventions (see MetPy)?
140
+ elif units in no_unit_values or isinstance (units , Unit ):
141
+ # TODO what happens if they pass in a Unit from a different registry
142
+ return units
143
+ elif units is _default :
144
+ if unit_attribute in no_unit_values :
145
+ return unit_attribute
139
146
units = registry .parse_units (unit_attribute )
140
- elif isinstance (units , Unit ):
141
- # TODO do we have to check what happens if someone passes a Unit instance
142
- # without creating a unit registry?
143
- # TODO and what happens if they pass in a Unit from a different registry
144
- pass
145
147
else :
146
148
units = registry .parse_units (units )
147
149
return units
@@ -243,7 +245,7 @@ class PintDataArrayAccessor:
243
245
def __init__ (self , da ):
244
246
self .da = da
245
247
246
- def quantify (self , units = None , unit_registry = None , ** unit_kwargs ):
248
+ def quantify (self , units = _default , unit_registry = None , ** unit_kwargs ):
247
249
"""
248
250
Attach units to the DataArray.
249
251
@@ -269,7 +271,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
269
271
pint.Unit, will be used as the DataArray's units. If a
270
272
dict-like, it should map a variable name to the desired
271
273
unit (use the DataArray's name to refer to its data). If
272
- not provided, will try to read them from
274
+ not provided, ``quantify`` will try to read them from
273
275
``DataArray.attrs['units']`` using pint's parser. The
274
276
``"units"`` attribute will be removed from all variables
275
277
except from dimension coordinates.
@@ -285,6 +287,11 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
285
287
DataArray whose wrapped array data will now be a Quantity
286
288
array with the specified units.
287
289
290
+ Notes
291
+ -----
292
+ ``"none"`` and ``None`` can be used to mark variables that should not
293
+ be quantified.
294
+
288
295
Examples
289
296
--------
290
297
>>> da = xr.DataArray(
@@ -297,6 +304,18 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
297
304
<Quantity([0.4 0.9 1.7 4.8 3.2 9.1], 'hertz')>
298
305
Coordinates:
299
306
* wavelength (wavelength) float64 0.0001 0.0002 0.0004 0.0006 0.001 0.002
307
+
308
+ Don't quantify the data:
309
+
310
+ >>> da = xr.DataArray(
311
+ ... data=[0.4, 0.9],
312
+ ... dims=["wavelength"],
313
+ ... attrs={"units": "Hz"},
314
+ ... )
315
+ >>> da.pint.quantify(units=None)
316
+ <xarray.DataArray (wavelength: 2)>
317
+ array([0.4, 0.9])
318
+ Dimensions without coordinates: wavelength
300
319
"""
301
320
302
321
if isinstance (self .da .data , Quantity ):
@@ -305,7 +324,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
305
324
f"already has units { self .da .data .units } "
306
325
)
307
326
308
- if isinstance (units , (str , pint .Unit )):
327
+ if units is None or isinstance (units , (str , pint .Unit )):
309
328
if self .da .name in unit_kwargs :
310
329
raise ValueError (
311
330
f"ambiguous values given for { repr (self .da .name )} :"
@@ -320,15 +339,15 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
320
339
321
340
unit_attrs = conversion .extract_unit_attributes (self .da )
322
341
323
- possible_new_units = zip_mappings (units , unit_attrs )
342
+ possible_new_units = zip_mappings (units , unit_attrs , fill_value = _default )
324
343
new_units = {}
325
344
invalid_units = {}
326
345
for name , (unit , attr ) in possible_new_units .items ():
327
- if unit is not None or attr is not None :
346
+ if unit is not _default or attr is not _default :
328
347
try :
329
348
new_units [name ] = _decide_units (unit , registry , attr )
330
349
except (ValueError , pint .UndefinedUnitError ) as e :
331
- if unit is not None :
350
+ if unit is not _default :
332
351
type = "parameter"
333
352
reported_unit = unit
334
353
else :
@@ -880,7 +899,7 @@ class PintDatasetAccessor:
880
899
def __init__ (self , ds ):
881
900
self .ds = ds
882
901
883
- def quantify (self , units = None , unit_registry = None , ** unit_kwargs ):
902
+ def quantify (self , units = _default , unit_registry = None , ** unit_kwargs ):
884
903
"""
885
904
Attach units to the variables of the Dataset.
886
905
@@ -905,10 +924,10 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
905
924
units : mapping of hashable to unit-like, optional
906
925
Physical units to use for particular DataArrays in this
907
926
Dataset. It should map variable names to units (unit names
908
- or ``pint.Unit`` objects). If not provided, will try to
909
- read them from ``Dataset[var].attrs['units']`` using
910
- pint's parser. The ``"units"`` attribute will be removed
911
- from all variables except from dimension coordinates.
927
+ or ``pint.Unit`` objects). If not provided, ``quantify``
928
+ will try to read them from ``Dataset[var].attrs['units']``
929
+ using pint's parser. The ``"units"`` attribute will be
930
+ removed from all variables except from dimension coordinates.
912
931
unit_registry : pint.UnitRegistry, optional
913
932
Unit registry to be used for the units attached to each
914
933
DataArray in this Dataset. If not given then a default
@@ -922,6 +941,11 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
922
941
Dataset whose variables will now contain Quantity arrays
923
942
with units.
924
943
944
+ Notes
945
+ -----
946
+ ``"none"`` and ``None`` can be used to mark variables
947
+ that should not be quantified.
948
+
925
949
Examples
926
950
--------
927
951
>>> ds = xr.Dataset(
@@ -947,21 +971,33 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs):
947
971
Data variables:
948
972
a (x) int64 [m] 0 3 2
949
973
b (x) int64 [dm] 5 -2 1
974
+
975
+ Don't quantify specific variables:
976
+
977
+ >>> ds.pint.quantify({"a": None})
978
+ <xarray.Dataset>
979
+ Dimensions: (x: 3)
980
+ Coordinates:
981
+ * x (x) int64 0 1 2
982
+ u (x) int64 [s] -1 0 1
983
+ Data variables:
984
+ a (x) int64 0 3 2
985
+ b (x) int64 5 -2 1
950
986
"""
951
987
units = either_dict_or_kwargs (units , unit_kwargs , "quantify" )
952
988
registry = get_registry (unit_registry , units , conversion .extract_units (self .ds ))
953
989
954
990
unit_attrs = conversion .extract_unit_attributes (self .ds )
955
991
956
- possible_new_units = zip_mappings (units , unit_attrs )
992
+ possible_new_units = zip_mappings (units , unit_attrs , fill_value = _default )
957
993
new_units = {}
958
994
invalid_units = {}
959
995
for name , (unit , attr ) in possible_new_units .items ():
960
- if unit is not None or attr is not None :
996
+ if unit is not _default or attr is not _default :
961
997
try :
962
998
new_units [name ] = _decide_units (unit , registry , attr )
963
999
except (ValueError , pint .UndefinedUnitError ) as e :
964
- if unit is not None :
1000
+ if unit is not _default :
965
1001
type = "parameter"
966
1002
reported_unit = unit
967
1003
else :
0 commit comments