2

I'm trying to vectorize a function that outputs a list. I wish to feed it all values from a numpy list and have it return a matrix, such that each row is an output for an element in the input vector.

import numpy as np

def func(x, n):
    o = []
    for i in range(n):
        o.append(x+i)
    return o

vec_func = np.vectorize(func)
matrix = vec_func(np.asarray([0, 1, 2]), 10)

however i get the error

ValueError: setting an array element with a sequence.

how can i fix this?

2
  • You want to return [[1, 2, 3], [2, 3, 4], [3, 4, 5]]? Commented Nov 19, 2017 at 7:48
  • @roganjosh yes. Commented Nov 19, 2017 at 8:04

1 Answer 1

4

frompyfunc might be better:

In [525]: def fun(x):
     ...:     return x+.1, x+.2, x+.3
     ...: 

I specify 1 input, 3 output values. It returns dtype object:

In [526]: np.frompyfunc(fun,1,3)(np.arange(5))
Out[526]: 
(array([0.1, 1.1, 2.1, 3.1, 4.1], dtype=object),
 array([0.2, 1.2, 2.2, 3.2, 4.2], dtype=object),
 array([0.3, 1.3, 2.3, 3.3, 4.3], dtype=object))

That's a tuple of 3 arrays. They can be turned into one 2d array with stack:

In [527]: np.stack(_, 1)
Out[527]: 
array([[0.1, 0.2, 0.3],
       [1.1, 1.2, 1.3],
       [2.1, 2.2, 2.3],
       [3.1, 3.2, 3.3],
       [4.1, 4.2, 4.3]], dtype=object)

I could take it a further step with a astype(float).

I assume, of course, that this is a toy func. For something this simple there's no need to use vectorize.

In [528]: fun(np.arange(5))
Out[528]: 
(array([ 0.1,  1.1,  2.1,  3.1,  4.1]),
 array([ 0.2,  1.2,  2.2,  3.2,  4.2]),
 array([ 0.3,  1.3,  2.3,  3.3,  4.3]))

All that vectorize needs is the otypes parameter:

In [536]: np.vectorize(fun, otypes='ddd')(np.arange(5))
Out[536]: 
(array([ 0.1,  1.1,  2.1,  3.1,  4.1]),
 array([ 0.2,  1.2,  2.2,  3.2,  4.2]),
 array([ 0.3,  1.3,  2.3,  3.3,  4.3]))

If the function returns an array instead of a tuple or list, we could use signature:

In [546]: def fun(x):
     ...:     return np.array([x+.1, x+.2, x+.3])

In [547]: np.vectorize(fun, signature='()->(n)')(np.arange(5))
Out[547]: 
array([[ 0.1,  0.2,  0.3],
       [ 1.1,  1.2,  1.3],
       [ 2.1,  2.2,  2.3],
       [ 3.1,  3.2,  3.3],
       [ 4.1,  4.2,  4.3]])

Or with the original tuple/list case, wrap it in a lambda, np.vectorize(lambda x:np.array(fun(x)), signature='()->(n)')

Experience suggests that the frompyfunc approach is fastest. vectorize with otypes is a bit slower (but it uses frompyfunc). signature is newer method, using different code, and somewhat slower.


With your new func, the signature approach still works. I added excluded so it doesn't try to broadcast the n argument:

In [553]: np.vectorize(lambda x,n:np.array(func(x,n)), signature='()->(n)',excluded=[1])(np.arange(5),3)
Out[553]: 
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6]])
In [554]: np.vectorize(lambda x,n:np.array(func(x,n)), signature='()->(n)',excluded=[1])(np.arange(5),7)
Out[554]: 
array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 1,  2,  3,  4,  5,  6,  7],
       [ 2,  3,  4,  5,  6,  7,  8],
       [ 3,  4,  5,  6,  7,  8,  9],
       [ 4,  5,  6,  7,  8,  9, 10]])
Sign up to request clarification or add additional context in comments.

1 Comment

thanks! using multiple return values is not an option (afaik) as my toy function was a bit too oversimplified. in fact, the size of the output list is dynamic. Passing a numpy array directly is also not possible. I tried the otype parameter, but passed the wrong type. But If I understand correctly, this solution also fails because of the dynamic return size. I updated my original toy function to capture a bit more of the complexity.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.