Numpy support

Numpy support#

Numpy functions that work on pint Quantity ndarray objects also work on PintArray.

In [1]: pa = PintArray([1, 2, np.nan, 4, 10], dtype="pint[m]")

In [2]: np.clip(pa, 3 * ureg.m, 5 * ureg.m)
Out[2]: 
<PintArray>
[3.0, 3.0, <NA>, 4.0, 5.0]
Length: 5, dtype: pint[meter][Float64]

Note that this function errors when applied to a Series.

In [3]: df = pd.DataFrame({"A": pa})

In [4]: np.clip(df['A'], 3 * ureg.m, 5 * ureg.m)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 np.clip(df['A'], 3 * ureg.m, 5 * ureg.m)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/pint/facets/numpy/quantity.py:75, in NumpyQuantity.__array_function__(self, func, types, args, kwargs)
     74 def __array_function__(self, func, types, args, kwargs):
---> 75     return numpy_wrap("function", func, args, kwargs, types)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/pint/facets/numpy/numpy_func.py:1112, in numpy_wrap(func_type, func, args, kwargs, types)
   1110 if name not in handled or any(is_upcast_type(t) for t in types):
   1111     return NotImplemented
-> 1112 return handled[name](*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/pint/facets/numpy/numpy_func.py:874, in implement_consistent_units_by_argument.<locals>.implementation(*args, **kwargs)
    872 for i, unwrapped_unit_arg in enumerate(unwrapped_unit_args):
    873     bound_args.arguments[valid_unit_arguments[i]] = unwrapped_unit_arg
--> 874 ret = func(*bound_args.args, **bound_args.kwargs)
    876 # Conditionally wrap output
    877 if wrap_output:

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/numpy/_core/fromnumeric.py:2330, in clip(a, a_min, a_max, out, min, max, **kwargs)
   2326 elif min is not np._NoValue or max is not np._NoValue:
   2327     raise ValueError("Passing `min` or `max` keyword argument when "
   2328                      "`a_min` and `a_max` are provided is forbidden.")
-> 2330 return _wrapfunc(a, 'clip', a_min, a_max, out=out, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/numpy/_core/fromnumeric.py:54, in _wrapfunc(obj, method, *args, **kwds)
     52 bound = getattr(obj, method, None)
     53 if bound is None:
---> 54     return _wrapit(obj, method, *args, **kwds)
     56 try:
     57     return bound(*args, **kwds)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/numpy/_core/fromnumeric.py:46, in _wrapit(obj, method, *args, **kwds)
     43 # As this already tried the method, subok is maybe quite reasonable here
     44 # but this follows what was done before. TODO: revisit this.
     45 arr, = conv.as_arrays(subok=False)
---> 46 result = getattr(arr, method)(*args, **kwds)
     48 return conv.wrap(result, to_scalar=False)

File ~/checkouts/readthedocs.org/user_builds/pint-pandas-michaeltiemannosc/envs/latest/lib/python3.11/site-packages/numpy/_core/_methods.py:115, in _clip(a, min, max, out, **kwargs)
    113     return um.maximum(a, min, out=out, **kwargs)
    114 else:
--> 115     return um.clip(a, min, max, out=out, **kwargs)

File pandas/_libs/missing.pyx:392, in pandas._libs.missing.NAType.__bool__()

TypeError: boolean value of NA is ambiguous

Apply the function to the PintArray instead of the Series using Series.values.

In [5]: np.clip(df['A'].values, 3 * ureg.m, 5 * ureg.m)
Out[5]: 
<PintArray>
[3.0, 3.0, <NA>, 4.0, 5.0]
Length: 5, dtype: pint[meter][Float64]