hgrecco / pint-pandas

Pandas support for pint
Other
172 stars 42 forks source link

`numpy.allclose` not working for `PintArray` #108

Open rwijtvliet opened 2 years ago

rwijtvliet commented 2 years ago

Issue

import pandas as pd
import numpy as np
from pint_pandas import PintArray

>>> pa1 = pa2 = PintArray([1, 45, -4.5], 'm')
>>> np.allclose(pa1, pa2)

# ---------------------------------------------------------------------------
# TypeError                                 Traceback (most recent call last)
#       16 pa = PintArray([1, 45, -4.5], 'm')
# ----> 17 np.allclose(pa1, pa2)

# <__array_function__ internals> in allclose(*args, **kwargs)

# ~\Anaconda3\envs\lb38\lib\site-packages\numpy\core\numeric.py in allclose(a, b, rtol, atol, equal_nan)
#    2248     """
# -> 2249     res = all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan))
#    2250     return bool(res)

# <__array_function__ internals> in isclose(*args, **kwargs)

# ~\Anaconda3\envs\lb38\lib\site-packages\numpy\core\numeric.py in isclose(a, b, rtol, atol, equal_nan)
#    2354 
# -> 2355     xfin = isfinite(x)
#    2356     yfin = isfinite(y)

# TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

I'm on pint version 0.18 and pint_pandas 0.2

Non-complete workaround

>>> pa1.quantity.units == pa2.quantity.units and np.allclose(pa1.quantity.m, pa2.quantity.m)
True

This is not watertight! Workaround incorrectly returns False when comparing 1 m with 100 cm.

Watertight (?) workaround


def pintarray_allclose(pa1, pa2):
    if pa1.quantity.dimensionality != pa2.quantity.dimensionality:
        return False
    a1 = pa1.quantity.magnitude
    a2 = pa2.astype(f"pint[{pa1.quantity.units}]").quantity.magnitude
    return np.allclose(a1, a2)

>>> pintarray_allclose(pa1, pa2)
True

>>> pintarray_allclose(pa1, pa2.astype('pint[km]'))
True
andrewgsavage commented 2 years ago

this will need __array_function__implemented

as a workaround you can use pint's quantity:

np.allclose(pa1.quantity, pa2.quantity) True