-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
NEP 13 should mention how we handle unknown scalar types #12258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
One question here: how does numpy decide whether to coerce the unknown object or rather return NotImplemented? Because at this point, you need to decide: is this a scalar like object for which we want to apply the ufunc element-wise with the objects in the array (call the ufunc), or this is rather an unknown container-like object to which we want to dispatch the ufunc (return NotImplemented). This is the aspect for which I am unsure what to do in the pandas case. |
|
So probably we should do something similar for pandas? So for object dtype instead of checking a specific list of handled types (like in the NDArrayOperatorsMixin example implementation), we could also check for unknown objects if it defines
And then for object dtype, we would not return NotImplemented for anything else, but actually apply ufunc. |
It does seem like it would be worthwhile to figure out the canonical example of how to write a I think the right answer is a mix-up of the class ArrayLike(np.lib.mixins.NDArrayOperatorsMixin):
def __init__(self, value):
self.value = np.asarray(value)
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
out = kwargs.get('out', ())
for item in inputs + outputs:
if (hasattr(item, '__array_ufunc__') and
type(item).__array_ufunc__ is not np.ndarray.__array_ufunc__):
return NotImplemented
# Defer to the implementation of the ufunc on unwrapped values.
inputs = tuple(x.value if isinstance(x, ArrayLike) else x
for x in inputs)
if out:
kwargs['out'] = tuple(
x.value if isinstance(x, ArrayLike) else x
for x in out)
result = getattr(ufunc, method)(*inputs, **kwargs)
# FIXME: should also check ufunc.signature to ensure it's not a gufunc
if type(result) is tuple:
# multiple return values
return tuple(type(self)(x) for x in result)
elif method == 'at':
# no return value
return None
else:
# one return value
return type(self)(result)
def __repr__(self):
return '%s(%r)' % (type(self).__name__, self.value) Note that def _disables_array_ufunc(obj):
try:
return obj.__array_ufunc__ is None
except AttributeError:
return False
class NDArrayOperatorsMixin:
...
def __add__(self, other):
if _disables_array_ufunc(other):
return NotImplemented
return np.add(self, other) |
This section of NEP 13 describes how
__array_ufunc__
interacts with Python's binary operations:http://www.numpy.org/neps/nep-0013-ufunc-overrides.html#behavior-in-combination-with-python-s-binary-operations
But it doesn't fully describe what happens an object of unknown type that implements arithmetic (e.g.,
decimal.Decimal
) but doesn't implement any NumPy special methods like__array_ufunc__
or__array_priority__
is encountered in arithmetic with a NumPy array:NotImplemented
from binary operations for unrecognized arguments, the numpy ufunc will get called.np.add
) will coerce the unknown object into a scalar NumPy array with object dtype.If this seems correct, I will make a minor addendum to add this clarification. This is particularly valuable as an example to other projects (e.g., see pandas-dev/pandas#23293) of the right way to implement arithmetic like NumPy.
The text was updated successfully, but these errors were encountered: