Question:
I have a function defined in Python that works correctly for scalar values:
>>> def signo(x):
... if x < 0:
... return -1
... elif x > 0:
... return 1
... else:
... return 0
...
>>> signo(10)
1
>>> signo(-5)
-1
But when I pass it an array from NumPy, it fails with a ValueError
:
>>> import numpy as np
>>> t = np.arange(-5, 5)
>>> t
array([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4])
>>> signo(t)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in signo
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Why does this happen?
Answer:
In this case the error message clearly explains the problem: "the truth value of an array of more than one element is ambiguous". At the moment Python has to evaluate this conditional:
if x < 0: # x es ahora array([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4])
it cannot return either True
or False
, because there are elements of the array that meet the condition and elements that do not. This is a very typical problem.
Assuming that we want another array, the same size as the input, with True
or False
values depending on whether the corresponding element meets the condition or not , there would be two ways to solve it:
1) Implement all the logic internally: it would have to be evaluated in a loop for each of the elements of the array. An implementation would be this:
>>> def signo(x):
... x = np.atleast_1d(x) # Convertimos a array
... sgn = np.zeros_like(x)
... for ii in range(len(sgn)):
... if x[ii] < 0:
... sgn[ii] = -1
... elif x[ii] > 0:
... sgn[ii] = 1
... else:
... pass # sgn[ii] ya vale 0
... return sgn
...
>>> signo(10)
array([1])
>>> signo(-5)
array([-1])
>>> signo(t)
array([-1, -1, -1, -1, -1, 0, 1, 1, 1, 1])
2) Vectorize the function, using np.vectorize
. Note that this is how the scalar case is handled more elegantly:
>>> def signo(x):
... if x < 0:
... return -1
... elif x > 0:
... return 1
... else:
... return 0
...
>>> signo = np.vectorize(signo) # Vectorizamos la función
>>> signo(10)
array(1)
>>> signo(-5)
array(-1)
>>> signo(t)
array([-1, -1, -1, -1, -1, 0, 1, 1, 1, 1])
In this particular problem the recommendation to use .any()
or .all()
doesn't work, but on another occasion they can be useful functions.