1

I'm having troubles with a vectorized function:

def func(y):
    return sum(x**(-y) for x in range(1, 10))
vect_func = np.vectorize(func)
vect_func([1, 2, 3, 4, 5])

# Output:
# ValueError: Integers to negative integer powers are not allowed.

Whereas the following works just fine:

[func(t) for t in [1, 2, 3, 4, 5]]

# Output:
# [2.8289682539682537, 1.5397677311665408, 1.1965319856741932, 1.0819365834937567, 1.0368973413446938]

Is it possible to get np.vectorize to work on this example?

Davide_sd
  • 10,578
  • 3
  • 18
  • 30
  • Does this answer your question? [Why can't I raise to a negative power in numpy?](https://stackoverflow.com/questions/43287311/why-cant-i-raise-to-a-negative-power-in-numpy) – ades Jan 05 '22 at 15:41
  • Why are you trying to use `vectorize` if the list comprehension works just fine? – hpaulj Jan 05 '22 at 15:51
  • @hpaulj because in my application `func` is an arbitrary function with an arbitrary number of arguments. It just so happens that that particular function is creating problems. – Davide_sd Jan 05 '22 at 15:54

1 Answers1

3

When a function doesn't work in np.vectorize, it's a good idea to verify what the argument is.

Let's add a type print to the function:

In [36]: def func(y):
    ...:     print(type(y))
    ...:     return sum(x**(-y) for x in range(1, 10))
    ...: 

In the list comprehension, Python int are passed:

In [37]: func(3)
<class 'int'>
Out[37]: 1.1965319856741932

with the vectorized version:

In [40]: vect_func(3)
<class 'numpy.int64'>
Traceback (most recent call last):
  File "<ipython-input-40-65e2816d8003>", line 1, in <module>
    vect_func(3)
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 2163, in __call__
    return self._vectorize_call(func=func, args=vargs)
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 2241, in _vectorize_call
    ufunc, otypes = self._get_ufunc_and_otypes(func=func, args=args)
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 2201, in _get_ufunc_and_otypes
    outputs = func(*inputs)
  File "<ipython-input-36-311d525a60ba>", line 3, in func
    return sum(x**(-y) for x in range(1, 10))
  File "<ipython-input-36-311d525a60ba>", line 3, in <genexpr>
    return sum(x**(-y) for x in range(1, 10))
ValueError: Integers to negative integer powers are not allowed.

y is not a python int, it is a numpy int.

In [41]: func(np.int64(3))
<class 'numpy.int64'>
Traceback (most recent call last):
  File "<ipython-input-41-c34830937ffd>", line 1, in <module>
    func(np.int64(3))
  File "<ipython-input-36-311d525a60ba>", line 3, in func
    return sum(x**(-y) for x in range(1, 10))
  File "<ipython-input-36-311d525a60ba>", line 3, in <genexpr>
    return sum(x**(-y) for x in range(1, 10))
ValueError: Integers to negative integer powers are not allowed.

If we deliberately pass Python int it works:

In [42]: vect_func(np.array([1,2,3], object))
<class 'int'>
<class 'int'>
<class 'int'>
<class 'int'>
Out[42]: array([2.82896825, 1.53976773, 1.19653199])

np.vectorize can be handy when passing several arrays to a scalar function, and you want to take advantage of broadcasting. But for a list, or lists that can be zipped, it doesn't add anything to a list comprehension - not even speed.

And as this case illustrates, it has some gotchas than can catch the novice (and even more experienced users). Here it's the nature of the input. For many other SO questions, it's nature of the return value (the automatic otypes).

hpaulj
  • 221,503
  • 14
  • 230
  • 353