From e8bda365441374d6bd90afc3ea3fa2bee4884fcd Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 20 Jan 2021 23:45:13 +0100 Subject: [PATCH 1/8] check that the unit formatting util works correctly --- pint_xarray/tests/test_accessors.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pint_xarray/tests/test_accessors.py b/pint_xarray/tests/test_accessors.py index e7026884..0f54b908 100644 --- a/pint_xarray/tests/test_accessors.py +++ b/pint_xarray/tests/test_accessors.py @@ -6,7 +6,7 @@ from pint.errors import UndefinedUnitError from xarray.testing import assert_equal -from .. import conversion +from .. import accessors, conversion from .utils import raises_regex pytestmark = [ @@ -101,6 +101,23 @@ def test_parse_integer_inverse(self): assert result.pint.units == Unit("1 / meter") +@pytest.mark.parametrize("formatter", ("P", "C")) +@pytest.mark.parametrize("flags", ("", "~", "#", "~#")) +def test_units_to_str_or_none(formatter, flags): + unit_format = f"{{:{flags}{formatter}}}" + unit_attrs = {None: "m", "a": "s", "b": "degC", "c": "degF", "d": "degK"} + units = {key: unit_registry.Unit(value) for key, value in unit_attrs.items()} + + expected = {key: unit_format.format(value) for key, value in units.items()} + actual = accessors.units_to_str_or_none(units, unit_format) + + assert expected == actual + assert units == {key: unit_registry.Unit(value) for key, value in actual.items()} + + expected = {None: None} + assert expected == accessors.units_to_str_or_none(expected, unit_format) + + class TestDequantifyDataArray: def test_strip_units(self, example_quantity_da): result = example_quantity_da.pint.dequantify() From 13e8b62263518f8f0f46cb324c243759e8c65027 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 20 Jan 2021 23:45:47 +0100 Subject: [PATCH 2/8] use a format instead of casting to str --- pint_xarray/accessors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pint_xarray/accessors.py b/pint_xarray/accessors.py index bb1888e9..faaf05a4 100644 --- a/pint_xarray/accessors.py +++ b/pint_xarray/accessors.py @@ -77,9 +77,9 @@ def merge_mappings(first, *mappings): return result -def units_to_str_or_none(mapping): +def units_to_str_or_none(mapping, unit_format): return { - key: str(value) if isinstance(value, Unit) else value + key: unit_format.format(value) if isinstance(value, Unit) else value for key, value in mapping.items() } From 0c83f09b8a6fbec7c0afed13cd6b23c75793b444 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 20 Jan 2021 23:48:27 +0100 Subject: [PATCH 3/8] allow passing a format to .pint.dequantify --- pint_xarray/accessors.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pint_xarray/accessors.py b/pint_xarray/accessors.py index faaf05a4..f1bdaa4e 100644 --- a/pint_xarray/accessors.py +++ b/pint_xarray/accessors.py @@ -246,7 +246,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs): return conversion.attach_units(new_obj, units) - def dequantify(self): + def dequantify(self, format="~P"): """ Removes units from the DataArray and its coordinates. @@ -258,9 +258,12 @@ def dequantify(self): dequantified : DataArray DataArray whose array data is unitless, and of the type that was previously wrapped by `pint.Quantity`. + format : str, optional + The format used for the string representations. """ + unit_format = f"{{:{format}}}" - units = units_to_str_or_none(conversion.extract_units(self.da)) + units = units_to_str_or_none(conversion.extract_units(self.da), unit_format) new_obj = conversion.attach_unit_attributes( conversion.strip_units(self.da), units ) @@ -512,7 +515,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs): return conversion.attach_units(new_obj, units) - def dequantify(self): + def dequantify(self, format="~P"): """ Removes units from the Dataset and its coordinates. @@ -524,8 +527,11 @@ def dequantify(self): dequantified : Dataset Dataset whose data variables are unitless, and of the type that was previously wrapped by ``pint.Quantity``. + format : str, optional + The format used for the string representations. """ - units = units_to_str_or_none(conversion.extract_units(self.ds)) + unit_format = f"{{:{format}}}" + units = units_to_str_or_none(conversion.extract_units(self.ds), unit_format) new_obj = conversion.attach_unit_attributes( conversion.strip_units(self.ds), units ) From 40f433148444c8200fba5334632bbdc574a73172 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 21 Jan 2021 00:21:07 +0100 Subject: [PATCH 4/8] handle empty string formats --- pint_xarray/accessors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pint_xarray/accessors.py b/pint_xarray/accessors.py index f1bdaa4e..459693e5 100644 --- a/pint_xarray/accessors.py +++ b/pint_xarray/accessors.py @@ -78,8 +78,10 @@ def merge_mappings(first, *mappings): def units_to_str_or_none(mapping, unit_format): + formatter = str if not unit_format else lambda v: unit_format.format(v) + return { - key: unit_format.format(value) if isinstance(value, Unit) else value + key: formatter(value) if isinstance(value, Unit) else value for key, value in mapping.items() } From b34cdab5c5707d0d1a89bd349de22896432e0a85 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 21 Jan 2021 00:22:03 +0100 Subject: [PATCH 5/8] move the implementation of dequantify to a shared function --- pint_xarray/accessors.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/pint_xarray/accessors.py b/pint_xarray/accessors.py index 459693e5..904c42ab 100644 --- a/pint_xarray/accessors.py +++ b/pint_xarray/accessors.py @@ -86,6 +86,26 @@ def units_to_str_or_none(mapping, unit_format): } +def _dequantify(obj): + units = conversion.extract_units(obj) + attrs = { + k: v + for k, v in conversion.extract_unit_attributes(obj).items() + if isinstance(v, (Quantity, Unit)) + } + + if format is None: + registry = get_registry(None, units, attrs) + unit_format = registry.default_format + else: + unit_format = f"{{:{format}}}" + + units = units_to_str_or_none(merge_mappings(units, attrs), unit_format) + new_obj = conversion.attach_unit_attributes(conversion.strip_units(obj), units) + + return new_obj + + # based on xarray.core.utils.either_dict_or_kwargs # https://github.com/pydata/xarray/blob/v0.15.1/xarray/core/utils.py#L249-L268 def either_dict_or_kwargs(positional, keywords, method_name): @@ -248,7 +268,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs): return conversion.attach_units(new_obj, units) - def dequantify(self, format="~P"): + def dequantify(self, format=None): """ Removes units from the DataArray and its coordinates. @@ -263,14 +283,7 @@ def dequantify(self, format="~P"): format : str, optional The format used for the string representations. """ - unit_format = f"{{:{format}}}" - - units = units_to_str_or_none(conversion.extract_units(self.da), unit_format) - new_obj = conversion.attach_unit_attributes( - conversion.strip_units(self.da), units - ) - - return new_obj + return _dequantify(self.da) @property def magnitude(self): @@ -517,7 +530,7 @@ def quantify(self, units=None, unit_registry=None, **unit_kwargs): return conversion.attach_units(new_obj, units) - def dequantify(self, format="~P"): + def dequantify(self, format=None): """ Removes units from the Dataset and its coordinates. @@ -532,12 +545,7 @@ def dequantify(self, format="~P"): format : str, optional The format used for the string representations. """ - unit_format = f"{{:{format}}}" - units = units_to_str_or_none(conversion.extract_units(self.ds), unit_format) - new_obj = conversion.attach_unit_attributes( - conversion.strip_units(self.ds), units - ) - return new_obj + return _dequantify(self.ds) def to(self, units=None, **unit_kwargs): """convert the quantities in a DataArray From 88e649f975783bc219f22ec9c4d3eed5cd2540c6 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 21 Jan 2021 00:22:31 +0100 Subject: [PATCH 6/8] also test a empty string formatter --- pint_xarray/tests/test_accessors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint_xarray/tests/test_accessors.py b/pint_xarray/tests/test_accessors.py index 0f54b908..00929882 100644 --- a/pint_xarray/tests/test_accessors.py +++ b/pint_xarray/tests/test_accessors.py @@ -101,7 +101,7 @@ def test_parse_integer_inverse(self): assert result.pint.units == Unit("1 / meter") -@pytest.mark.parametrize("formatter", ("P", "C")) +@pytest.mark.parametrize("formatter", ("", "P", "C")) @pytest.mark.parametrize("flags", ("", "~", "#", "~#")) def test_units_to_str_or_none(formatter, flags): unit_format = f"{{:{flags}{formatter}}}" From e65a2f08fbe36319a901d30d02ff25a49ab3271a Mon Sep 17 00:00:00 2001 From: Keewis Date: Sat, 30 Jan 2021 21:34:51 +0100 Subject: [PATCH 7/8] actually use the format --- pint_xarray/accessors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pint_xarray/accessors.py b/pint_xarray/accessors.py index 904c42ab..f2187e39 100644 --- a/pint_xarray/accessors.py +++ b/pint_xarray/accessors.py @@ -86,7 +86,7 @@ def units_to_str_or_none(mapping, unit_format): } -def _dequantify(obj): +def _dequantify(obj, format): units = conversion.extract_units(obj) attrs = { k: v @@ -283,7 +283,7 @@ def dequantify(self, format=None): format : str, optional The format used for the string representations. """ - return _dequantify(self.da) + return _dequantify(self.da, format=format) @property def magnitude(self): @@ -545,7 +545,7 @@ def dequantify(self, format=None): format : str, optional The format used for the string representations. """ - return _dequantify(self.ds) + return _dequantify(self.ds, format=format) def to(self, units=None, **unit_kwargs): """convert the quantities in a DataArray From 3baa36ceb42cf770f821206ccf84f8c4aed24fff Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 8 Feb 2021 19:28:23 +0100 Subject: [PATCH 8/8] add a entry to whats-new.rst --- docs/whats-new.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/whats-new.rst b/docs/whats-new.rst index c3805c93..d6234592 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -6,6 +6,8 @@ What's new - rewrite :py:meth:`Dataset.pint.quantify` and :py:meth:`DataArray.pint.quantify`, to use pint's `parse_units` instead of `parse_expression` (:pull:`40`) - refactor the internal conversion functions (:pull:``) +- allow passing a format string to :py:meth:`Dataset.pint.dequantify` and + :py:meth:`DataArray.pint.dequantify` (:pull:`49`) v0.1 (October 26 2020) ----------------------