4

I would like to assign a function that has a boolean evaluation in it, using a fast way. Here is a simple example. I want the following function to be evaluated for arbitrary a and b:

a = 0.5
b = 0.6
def func(x):
    x=max(x,a)
    if x>b:
        return x**2
    else:
        return x**3

and then I want to assign the function values into an array in a vectorized manner (for speed):

xRange = np.arange(0, 1, 0.1)
arr_func = func(xRange)

But I get the error:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Now, I know I can assign the values in a loop. But that will be slow compared to the vectorized equivalent. Can I bypass this exception and still assign the values in a vectorized manner?

3
  • 1
    I think you could use docs.scipy.org/doc/numpy-1.10.0/reference/generated/… Commented Aug 16, 2017 at 11:44
  • 1
    Don't use np.vectorize here! Commented Aug 16, 2017 at 12:15
  • @Lukisn, you should consider deleting your comment since it got an upvote and is misleading, see your answer. Commented Dec 18, 2018 at 21:19

3 Answers 3

4

If I'm reading your code correctly, the vectorized version of this would be to use a couple np.where:

def func(x):
    x = np.where(a > x, a, x)
    return np.where(x > b, x**2, x**3)
Sign up to request clarification or add additional context in comments.

2 Comments

@DanielF -- Yes, you're probably right. However, that would modify the argument (xRange in the case of OP's example). That may not be desirable. Numpy is already making a bunch of copies in both cases -- the (frequently correct) assumption that is usually made when writing numpy code is that the overhead of the copies is small compared to the overhead of python loops.
True - I'm surprised pass by reference doesn't trip me up more considering how often I forget it :) Deleted the comment to prevent confusion
1

It's also possible to use np.select - it's a little wordier than necessary in this case, but is extensible to many conditions

def func(x):
    condlist =   [
                  x < a, 
                  (x >= a) & (x <= b), 
                  x > b
                 ]
    choicelist = [
                  a**3, 
                  x**3, 
                  x**2
                 ]
    return np.select(condlist, choicelist)

or

def func(x):
    condlist =   [
                  x < a,  
                  x > b
                 ]
    choicelist = [
                  a**3, 
                  x**2
                 ]
    return np.select(condlist, choicelist, default = x**3)

Comments

0

If you want (or have) to keep your original function, you could use numpy's vectorize function to create the "vectorized" version that accepts a numpy array as an input.

Note that this function is only for convenience and offers no performance improvements since it internally only implements a for loop. So there is no "real" vectorization!

import numpy as np

def func(x, a=0.5, b=0.6):
    x = max(x, a)
    if x > b:
        return x**2
    else:
        return x**3

vfunc = np.vectorize(func)  # this is it!

xRange = np.arange(0, 1, 0.1)
arr_func = vfunc(xRange)

print(arr_func)

The above code works and produces the following output:

[ 0.125  0.125  0.125  0.125  0.125  0.125  0.36   0.49   0.64   0.81 ]

2 Comments

This is a bad idea. You may want to read the "Notes" section of the vectorize documentation page.
Thanks for pointing that out. I edited my answer to make this clear.

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.