From 1943f2b4f11a1d53fc5b7523986cd7f55baffd8d Mon Sep 17 00:00:00 2001 From: Michael Tiemann <72577720+MichaelTiemannOSC@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:56:08 -0400 Subject: [PATCH] Pr/137 compat check (#245) * _get_common_dtype must check PintTypes compat PR number 137 adds _get_common_dtype to PintType so that `PintType` operations can be performed on a mix of `PintType` and numeric values (with the later being promoted to the `PintType` for the purposes of the operation). However, when there are multiple `PintType` elements present, it is important that all elements are in fact compatible, lest the operation attempt to combine two `PintType` elements that are not unit-compatible. * Fix pre-commit and add CHANGES Clean up PR for submission. * Update CHANGES * Update pint_array.py * fix issue --------- Co-authored-by: andrewgsavage --- CHANGES | 4 ++-- pint_pandas/pint_array.py | 13 +++++++++-- pint_pandas/testsuite/test_issues.py | 34 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index e945626..b733508 100644 --- a/CHANGES +++ b/CHANGES @@ -2,10 +2,10 @@ pint-pandas Changelog ===================== -0.7 (unreleased) +0.6.2 (2024-07-29) -------------------- -- Nothing added yet. +- Fix PintType._get_common_dtype (added via #137) to check compatibility of all `PintType`s in `dtypes` 0.6.1 (2024-07-13) diff --git a/pint_pandas/pint_array.py b/pint_pandas/pint_array.py index 9c5efab..93c787e 100644 --- a/pint_pandas/pint_array.py +++ b/pint_pandas/pint_array.py @@ -201,8 +201,9 @@ def _get_common_dtype(self, dtypes): If this function is called this means at least on of the ``dtypes`` list is a ``PintType`` - In order to be able to be able to perform operation on ``PintType`` + In order to be able to perform operation on ``PintType`` with scalars, mix of ``PintType`` and numeric values are allowed. + But all ``PintType`` elements must be compatible. Parameters @@ -213,10 +214,18 @@ def _get_common_dtype(self, dtypes): ------- returns self for acceptable cases or None otherwise """ + # Return self (PintType with same units) if possible if all( - isinstance(x, PintType) or pd.api.types.is_numeric_dtype(x) for x in dtypes + isinstance(dtype, PintType) and dtype.units == self.units + for dtype in dtypes ): return self + # Otherwise return PintType with undefined units + elif all( + isinstance(dtype, PintType) or pd.api.types.is_numeric_dtype(dtype) + for dtype in dtypes + ): + return PintType else: return None diff --git a/pint_pandas/testsuite/test_issues.py b/pint_pandas/testsuite/test_issues.py index 25709c5..74f26eb 100644 --- a/pint_pandas/testsuite/test_issues.py +++ b/pint_pandas/testsuite/test_issues.py @@ -277,3 +277,37 @@ def test_eval(self): ) tm.assert_series_equal(df.eval("a / b"), df["a"] / df["b"]) tm.assert_series_equal(df.eval("a / c"), df["a"] / df["c"]) + tm.assert_series_equal(df.eval("a / a"), df["a"] / df["a"]) + + def test_mixed_df(self): + df = pd.DataFrame( + { + "a": pd.Series([1.0, 2.0, 3.0], dtype="pint[meter]"), + "b": pd.Series([4.0, 5.0, 6.0], dtype="pint[second]"), + "c": [1.0, 2.0, 3.0], + } + ) + + assert df["a"][0] == df.iloc[0, 0] + + +class TestIssue246(BaseExtensionTests): + def test_issue246(self): + df = pd.DataFrame( + { + "a": [1, 2, 3], + "b": [4, 5, 6], + "c": [7, 8, 9], + } + ) + + df = df.astype( + { + "a": "pint[m]", + "b": "pint[m/s]", + "c": "pint[kN]", + } + ) + + # now an operation where each cell is independent from each other + df.apply(lambda x: x * 2, axis=1)