2

I would like to graph a custom function including min and max :

import numpy as np
import matplotlib.pyplot as plt

f = lambda x: max(0, x)
x = np.linspace(-10, 10)
y = f(x)

plt.plot(x, y)
plt.show()

Result:

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

Some help will be welcome

alex
  • 5,661
  • 6
  • 33
  • 54
  • 1
    Usual problem of calling a scalar function with a vector. See e.g. this question: https://stackoverflow.com/questions/47661978/plot-discrete-distribution-using-np-linspace – ImportanceOfBeingErnest Jan 28 '18 at 19:22

4 Answers4

5

use vectorized np.clip() instead of f - this way you can set both lower (a_min) and upper (a_max) boundaries in one step:

y = np.clip(x, a_min=0, a_max=None)

or try to vectorize your scalar funcion:

In [146]: x = np.linspace(-1000, 1000, 10**6)

In [147]: x.shape
Out[147]: (1000000,)

In [148]: vf = np.vectorize(f)

In [149]: %timeit [f(i) for i in x]
1.46 s ± 5.42 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [150]: %timeit vf(x)
1.03 s ± 8.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
MaxU - stand with Ukraine
  • 205,989
  • 36
  • 386
  • 419
4

Instead of max, use np.maximum:

from matplotlib import pyplot as plt
import numpy as np

f = lambda x: np.maximum(0, x)
x = np.linspace(-10,10)
y = f(x)

plt.plot(x,y)
plt.show()

EDIT:

In case of more complex functions, look out for the numpy equivalents of the functions you intend to use. Most of the time the names are the same as in the math module, e.g. math.sin would become np.sin etc. However, as in the example, max should be replaced by np.maximum not np.max, the latter of which returns the maximum value of an np.ndarray.

Thomas Kühn
  • 9,412
  • 3
  • 47
  • 63
  • This is the most elegant, IMO, solution to the particular problem but the question is "Graph a custom function in python" and this just addresses the single case that was given by the OP. – roganjosh Jan 28 '18 at 19:28
  • @roganjosh Quite true, but on the other hand, `numpy` provides a lot of vectorised versions of mathematical functions. At least all the functions in `math` are also defined in `numpy`, so this example can easily be extended. – Thomas Kühn Jan 28 '18 at 19:31
  • 2
    The idea would of course be to replace all the `max` from the function by `np.maximum`. Interestingly `np.clip` seems to be a little faster than `np.maximum`. Using `np.vectorize` on the initial function is 100 times slower than any of the numpy functions. – ImportanceOfBeingErnest Jan 28 '18 at 19:33
  • @ImportanceOfBeingErnest could it be that `np.maximum` and `np.minimum` are implemented using `np.clip`? That would explain the better performance. – Thomas Kühn Jan 28 '18 at 19:37
  • I have no idea. By far the fastest solution would be to compare inplace, `def f(x): x[x<=0] = 0; return x` – ImportanceOfBeingErnest Jan 28 '18 at 19:51
2

You could just use a list comprehension to get the corresponding y value for every value of x.

import numpy as np
import matplotlib.pyplot as plt

f = lambda x: max(0, x)
x = np.linspace(-10, 10)
y = [f(i) for i in x]

plt.plot(x, y)
plt.show()
roganjosh
  • 12,594
  • 4
  • 29
  • 46
2

I found it useful to define one function that can be called using one line of code, so now I can plot any function (assuming the invoked components are valid, e.g. log is loaded as "from math import log", or is expressed as "math.log", and that the function can be evaluated over the specified range):

import matplotlib.pyplot as plt   #; plt.rcdefaults()
import numpy as np
from math import log

def plot_func(x,f):
  # x = tuple defining range of x
  # f = string defining function to plot, in terms of x
  x = np.linspace(x[0],x[1],201)
  y = list(map(lambda x: eval(f), x))
  plt.close()
  plt.title('f(x) = ' + f)
  plt.plot(x,y, 'b')

plot_func(x=(0, 1), f = '60 * (x**3) * (1-x)**2') 

Generates:

enter image description here

It's just nice to avoid formal definition of the function being plotted, and to be able to reuse this to plot anything with one simple line to be tweaked.

James
  • 673
  • 6
  • 19