|
1 | 1 | # TODO is it possible to import pint-xarray from within xarray if pint is present?
|
| 2 | +import numpy as np |
| 3 | +import pint |
| 4 | +from pint.quantity import Quantity |
| 5 | +from pint.unit import Unit |
2 | 6 | from xarray import (
|
3 |
| - register_dataarray_accessor, |
4 |
| - register_dataset_accessor, |
5 | 7 | DataArray,
|
6 | 8 | Dataset,
|
7 | 9 | Variable,
|
| 10 | + register_dataarray_accessor, |
| 11 | + register_dataset_accessor, |
8 | 12 | )
|
9 | 13 | from xarray.core.npcompat import IS_NEP18_ACTIVE
|
10 | 14 |
|
11 |
| -import numpy as np |
12 |
| - |
13 |
| -import pint |
14 |
| -from pint.quantity import Quantity |
15 |
| -from pint.unit import Unit |
16 |
| - |
| 15 | +from . import conversion |
17 | 16 |
|
18 | 17 | if not hasattr(Quantity, "__array_function__"):
|
19 | 18 | raise ImportError(
|
|
32 | 31 | # TODO type hints
|
33 | 32 |
|
34 | 33 |
|
| 34 | +def is_dict_like(obj): |
| 35 | + return hasattr(obj, "keys") and hasattr(obj, "__getitem__") |
| 36 | + |
| 37 | + |
| 38 | +def either_dict_or_kwargs(positional, keywords, method_name): |
| 39 | + if positional is not None: |
| 40 | + if not is_dict_like(positional): |
| 41 | + raise ValueError( |
| 42 | + f"the first argument to .{method_name} must be a dictionary" |
| 43 | + ) |
| 44 | + if keywords: |
| 45 | + raise ValueError( |
| 46 | + "cannot specify both keyword and positional " |
| 47 | + f"arguments to .{method_name}" |
| 48 | + ) |
| 49 | + return positional |
| 50 | + else: |
| 51 | + # Need an explicit cast to appease mypy due to invariance; see |
| 52 | + # https://github.com/python/mypy/issues/6228 |
| 53 | + return keywords |
| 54 | + |
| 55 | + |
35 | 56 | def _array_attach_units(data, unit, convert_from=None):
|
36 | 57 | """
|
37 | 58 | Internal utility function for attaching units to a numpy-like array,
|
@@ -233,15 +254,19 @@ def registry(self):
|
233 | 254 | def registry(self, _):
|
234 | 255 | raise AttributeError("Don't try to change the registry once created")
|
235 | 256 |
|
236 |
| - def to(self, units): |
237 |
| - quantity = self.da.data.to(units) |
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 |
| - ) |
| 257 | + def to(self, units=None, *, registry=None, **unit_kwargs): |
| 258 | + if isinstance(units, (str, pint.Unit)): |
| 259 | + unit_kwargs[self.da.name] = units |
| 260 | + units = None |
| 261 | + elif not is_dict_like(units): |
| 262 | + raise ValueError( |
| 263 | + "units must be either a string, a pint.Unit object or a dict-like," |
| 264 | + f" but got {units!r}" |
| 265 | + ) |
| 266 | + |
| 267 | + units = either_dict_or_kwargs(units, unit_kwargs, "to") |
| 268 | + |
| 269 | + return conversion.convert_units(self.da, units) |
245 | 270 |
|
246 | 271 | def to_base_units(self):
|
247 | 272 | quantity = self.da.data.to_base_units()
|
|
0 commit comments