# plot issue with a self-defined piecewise function

The following code produces an error:

def f(x):
if x>3:
return(x^2)
if x<=3:
return(3*x)

plot(f(x),(x,0,5))


BUT, the code below works.

def f(x):
if x>3:
return(x^2)
if x<=3:
return(3*x)

plot(lambda x: f(x),(x,0,5))


So, my questions are: (1) why do you need the lambda function? and (2) when do you have to do this?

edit retag close merge delete

Sort by » oldest newest most voted

The problem in your first example is that when you do f(x), it is actually calling f and returning a value. However, you want to plot the function, not the return value from one call. So you should do this:

def f(x):
if x>3:
return(x^2)
if x<=3:
return(3*x)

plot(f,(x,0,5))


Notice that now, I haven't called the function. Instead, I'm just passing the function into plot, and plot will call the function with different values.

Your lambda function trick works because it is passing a function into plot. plot then calls the lambda function, which in turn calls the f function. But I think it's easier to what I did above.

more

Hmm, @calc314 originally asked me to answer this offline ... But good answer, and it should be available to all.

( 2012-01-18 09:28:31 -0500 )edit

Thanks for all of the help! I think I get it now.

( 2012-01-18 09:39:32 -0500 )edit

I asked Jeff to post these to ask.sagemath.org so that others could see the answers.

( 2012-01-18 11:12:15 -0500 )edit

Sorry, I meant that @calc314 asked me to answer them, and I said to put it on this site so *I* could answer it ;-)

( 2012-01-18 13:22:06 -0500 )edit

I have a follow up question, since I know that I will be asked this by students and colleagues. Why does a self-defined function attempt to evaluate f(x) when you give the command plot(f(x), (x,0,5)), but yet when you define a function as below it works? f(x) = x^2 plot(f(x),(x,-3,3))

( 2012-01-19 03:52:42 -0500 )edit

Another option is to use the (not very powerful) Piecewise class. You could at least do some basic calculus with it as well, then. However, you'd have to define endpoints.

sage: g = Piecewise([[(-10,1),x], [(1,10),x^2]], x)
sage: derivative(g)
Piecewise defined function with 2 parts, [[(-10, 1), x |--> 1], [(1, 10), x |--> 2*x]]
sage: integrate(g)
Piecewise defined function with 2 parts, [[(-10, 1), x |--> 1/2*x^2 - 50], [(1, 10), x |--> 1/3*x^3 - 299/6]]
sage: plot(g)


So it works. But see ticket 11225 for a nice list of ways in which piecewise plotting could be improved. For instance, with infinite endpoints, although it does math, it won't plot. Perhaps that makes sense, since we wouldn't see infinity anyway...

sage: f = Piecewise([[(-oo,1),x], [(1,oo),x^2]], x)
sage: f(-10^6)
-1000000
sage: plot(f)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: cannot convert float NaN to integer

more

To answer the 3d plot question, again, you need to make the functions calculate their values when the plotting routine is actually getting points, not before the plotting routine is called:

Y=parametric_plot3d([lambda s,t: (2+sin(s))*sin(t*f(s)),lambda s,t: cos(s),lambda s,t: (2+sin(s))*cos(t*f(s))],(s,0,2*pi),(t,-1,1))

more

Toward a related issue - how do I incorporate such a conditional in a 3d plot? Consider the following.

s,t=var('s,t')
def f(x):
if x<pi:
return(arccos(-2/(2+sin(x))))
if x>=pi:
return(pi)

plot(f,(x,0,2*pi)) --- to check that the function is properly defined

X=parametric_plot3d([(2+sin(s))*sin(t),cos(s),(2+sin(s))*cos(t)],(s,0,2*pi),(t,0,2*pi))  --- to produce a torus
X.show(aspect_ratio=[1,1,1])

Y=parametric_plot3d([(2+sin(s))*sin(t*f(s)),cos(s),(2+sin(s))*cos(t*f(s))],(s,0,2*pi),(t,-1,1))  --- to produce the part of the torus above the z=-2 plane
Y.show(aspect_ratio=[1,1,1])


This fails. Any hints?

more

To post code, highlight the code and click the "code" button (the button that has 1010 on it). Or just indent each line 4 spaces or so.

( 2012-01-31 06:41:26 -0500 )edit