Analyzing functions: lab 3

© 2005 Ben Bolker

This lab will be somewhat shorter in terms of " R stuff" than the previous labs, because more of the new material is algebra and calculus than R commands. Try to do a reasonable amount of the work with paper and pencil before resorting to messing around in R.
derivatives ifelse for thresholds

1  Numerical experimentation: plotting curves

Here are the R commands used to generate Figure 1. They just use curve(), with add=FALSE (the default, which draws a new plot) and add=TRUE (adds the curve to an existing plot), particular values of from and to, and various graphical parameters (ylim, ylab, lty).
> curve(2 * exp(-x/2), from = 0, to = 7, ylim = c(0, 2), ylab = "")
> curve(2 * exp(-x), add = TRUE, lty = 4)
> curve(x * exp(-x/2), add = TRUE, lty = 2)
> curve(2 * x * exp(-x/2), add = TRUE, lty = 3)
> text(0.4, 1.9, expression(paste("exponential: ", 2 * e^(-x/2))), 
+     adj = 0)
> text(4, 0.7, expression(paste("Ricker: ", x * e^(-x/2))))
> text(4, 0.25, expression(paste("Ricker: ", 2 * x * e^(-x/2))), 
+     adj = 0)
> text(2.8, 0, expression(paste("exponential: ", 2 * e^(-x))))
 
The only new thing in this figure is the use of expression() to add a mathematical formula to an R graphic. text(x,y,"x^2") puts x^2 on the graph at position (x,y); text(x,y,expression(x^2)) (no quotation marks) puts x2 on the graph. See ?plotmath or ?demo(plotmath) for (much) more information.
An alternate way of plotting the exponential parts of this curve:
> xvec = seq(0, 7, length = 100)
> exp1_vec = 2 * exp(-xvec/2)
> exp2_vec = 2 * exp(-xvec)
> plot(xvec, exp1_vec, type = "l", ylim = c(0, 2), ylab = "")
> lines(xvec, exp2_vec, lty = 4)
 
or, since both exponential vectors are the same length, we could cbind() them together and use matplot():
> matplot(xvec, cbind(exp1_vec, exp2_vec), type = "l", ylab = "")
 
Finally, if you needed to use sapply() you could say:
> expfun = function(x, a = 1, b = 1) {
+     a * exp(-b * x)
+ }
> exp1_vec = sapply(xvec, expfun, a = 2, b = 1/2)
> exp2_vec = sapply(xvec, expfun, a = 2, b = 1)
 
The advantage of curve() is that you don't have to define any vectors: the advantage of doing things the other way arises when you want to keep the vectors around to do other calculations with them.
Exercise 1*: Construct a curve that has a maximum at (x=5, y=1). Write the equation, draw the curve in R, and explain how you got there.

1.1  A quick digression: ifelse() for piecewise functions

The ifelse() command in R is useful for constructing piecewise functions. Its basic syntax is ifelse(condition,value_if_true,value_if_false), where condition is a logical vector (e.g. x>0), value_if_true is a vector of alternatives to use if condition is TRUE, and value_if_false is a vector of alternatives to use if condition is FALSE. If you specify just one value, it will be expanded (recycled in R jargon) to be the right length. A simple example:
> x = c(-25, -16, -9, -4, -1, 0, 1, 4, 9, 16, 25)
> ifelse(x < 0, 0, sqrt(x))
 
 [1] 0 0 0 0 0 0 1 2 3 4 5

 
These commands produce a warning message, but it's OK to ignore it since you know you've taken care of the problem (if you said sqrt(ifelse(x<0,0,x)) instead you wouldn't get a warning: why not?)
Here are some examples of using ifelse() to generate (1) a simple threshold; (2) a Holling type I or "hockey stick"; (3) a more complicated piecewise model that grows exponentially and then decreases linearly; (4) a double-threshold model.
> op = par(mfrow = c(2, 2), mgp = c(2, 1, 0), mar = c(4.2, 3, 1, 
+     1))
> curve(ifelse(x < 2, 1, 3), from = 0, to = 5)
> curve(ifelse(x < 2, 2 * x, 4), from = 0, to = 5)
> curve(ifelse(x < 2, exp(x), exp(2) - 3 * (x - 2)), from = 0, 
+     to = 5)
> curve(ifelse(x < 2, 1, ifelse(x < 4, 3, 5)), from = 0, to = 5)
 
lab3-006.png
The double-threshold example (nested ifelse() commands) probably needs more explanation. In words, this command would go "if x is less than 2, set y to 1; otherwise (x ³ 2), if x is less than 4 (i.e. 2 £ x < 4), set y to 3; otherwise (x ³ 4), set y to 5".

2  Evaluating derivatives in R

R can evaluate derivatives, but it is not very good at simplifying them. In order for R to know that you really mean (e.g) x^2 to be a mathematical expression and not a calculation for R to try to do (and either fill in the current value of x or give an error is x is undefined), you have to specify it as expression(x^2); you also have to tell R (in quotation marks) what variable you want to differentiate with respect to:
> d1 = D(expression(x^2), "x")
> d1
 
2 * x

 
Use eval() to fill in a list of particular values for which you want a numeric answer:
> eval(d1, list(x = 2))
 
[1] 4

 
Taking the second derivative:
> D(d1, "x")
 
[1] 2

 
(As of version 2.0.1,) R knows how to take the derivatives of expressions including all the basic arithmetic operators; exponentials and logarithms; trigonometric inverse trig, and hyperbolic trig functions; square roots; and normal (Gaussian) density and cumulative density functions; and gamma and log-gamma functions. You're on your own for anything else (consider using a symbolic algebra package like Mathematica or Maple, at least to check your answers, if your problem is very complicated). deriv() is a slightly more complicated version of D() that is useful for incorporating the results of differentiation into functions: see the help page.

3  Figuring out the logistic curve

The last part of this exercise is an example of figuring out a function - Chapter 3 did this for the exponential, Ricker, and Michaelis-Menten functions The population-dynamics form of the logistic equation is
n(t) = K

1+ æ
è
K

n(0)
-1 ö
ø
exp(-r t)
(1)
where K is carrying capacity, r is intrinsic population growth rate, and n(0) is initial density.
At t=0, e-rt=1 and this reduces to n(0) (as it had better!)
Finding the derivative with respect to time is pretty ugly, but it will to reduce to something you may already know. Writing the equation as n(t) = K ·(stuff)-1 and using the chain rule we get n¢(t) = K ·(stuff)-2 ·d(stuff)/dt (stuff=1+(K/n(0)-1)exp(-rt)). The derivative d(stuff)/dt is (K/n(0)-1) ·-r exp(-rt). At t=0, stuff=K/n(0), and d(stuff)/dt=-r(K/n(0)-1). So this all comes out to
K ·(K/n(0))-2 ·-r (K/(n0)-1) = -r n(0)2/K ·(K/(n0)-1) = r n(0) (1-n(0)/K)
which should be reminiscent of intro. ecology: we have rediscovered, by working backwards from the time-dependent solution, that the logistic equation arises from a linearly decreasing per capita growth rate.
If n(0) is small we can do better than just getting the intercept and slope.
Exercise 2*: show that if n(0) is very small (and t is not too big), n(t) » n(0) exp(r t). (Start by showing that K/n(0) e-rt dominates all the other terms in the denominator.)
If t is small, this reduces (because ert » 1+rt) to n(t) » n(0) + r n(0) t, a linear increase with slope rn(0). Convince yourself that this matches the expression we got for the derivative when n(0) is small.
For large t, convince yourself tha the value of the function approaches K and (by revisiting the expressions for the derivative above) that the slope approaches zero.
The half-maximum occurs when the denominator (also known as stuff above) is 2; we can solve stuff=2 for t (getting to (K/n(0)-1) exp(-rt) = 1 and taking logarithms on both sides) to get t=log(K/n(0)-1)/r.
We have (roughly) three options:
  1. Use curve():
    > r = 1
    > K = 1
    > n0 = 0.1
    > curve(K/(1 + (K/n0 - 1) * exp(-r * x)), from = 0, to = 10)
     
    
    (note that we have to use x and not t in the expression for the logistic).
  2. Construct the time vector by hand and compute a vector of population values using vectorized operations:
    > t_vec = seq(0, 10, length = 100)
    > logist_vec = K/(1 + (K/n0 - 1) * exp(-r * t_vec))
    > plot(t_vec, logist_vec, type = "l")
     
    
  3. write our own function for the logistic and use sapply():
    > logistfun = function(t, r = 1, n0 = 0.1, K = 1) {
    +     K/(1 + (K/n0 - 1) * exp(-r * t))
    + }
    > logist_vec = sapply(t_vec, logistfun)
     
    
    When we use this function, it will no longer matter how r, n0 and K are defined in the workspace: the values that R uses in logistfun() are those that we define in the call to the function.
    > r = 17
    > logistfun(1, r = 2)
     
    
    [1] 0.4508531
    
     
    
    > r = 0
    > logistfun(1, r = 2)
     
    
    [1] 0.4508531
    
     
    
We can do more with this plot: let's see if our conjectures are right. Using abline() and curve() to add horizontal lines to a plot to test our estimates of starting value and ending value, vertical and horizontal lines that intersect at the half-maximum, a line with the intercept and initial linear slope, and a curve corresponding to the initial exponential increase:
> curve(logistfun(x), from = 0, to = 10, lwd = 2)
> abline(h = n0, lty = 2)
> abline(h = K, lty = 2)
> abline(h = K/2, lty = 3)
> abline(v = -log(n0/(K - n0))/r, lty = 4)
> r = 1
> abline(a = n0, b = r * n0 * (1 - n0/K), lty = 5)
> curve(n0 * exp(r * x), from = 0, lty = 6, add = TRUE)
 
lab3-014.png
Exercise 3*: Plot and analyze the function G(N)=[RN/((1+aN)b)], (the Shepherd function), which is a generalization of the Michaelis-Menten function. What are the effects of the R and a parameters on the curve? For what parameter values does this function become equivalent to the Michaelis-Menten function? What is the behavior (value, initial slope) at N=0? What is the behavior (asymptote [if any], slope) for large N, for b=0, 0 < b < 1, b=1, b > 1? Define an R function for the Shepherd function (call it shep). Draw a plot or plots showing the behavior for the ranges above, including lines that show the initial slope. Extra credit: when does the function have a maximum between 0 and ¥? What is the height of the maximum when it occurs? (Hint: when you're figuring out whether a fraction is zero or not, you don't have to think about the denominator at all.) The calculus isn't that hard, but you may also use the D() function in R. Draw horizontal and vertical lines onto the graph to test your answer.
Exercise 4*: Reparameterize the Holling type III functional response (f(x)=ax2/(1+bx2)) in terms of its asymptote and half-maximum.
Exercise 5*: Figure out the correspondence between the population-dynamic parameterization of the logistic function (eq. 1: parameters r, n(0), K) and the statistical parameterization (f(x)=exp(a+bx)/(1+exp(a+bx)): parameters a, b). Convince yourself you got the right answer by plotting the logistic with a=-5, b=2 (with lines), figuring out the equivalent values of K, r, and n(0), and then plotting the curve with both equations to make sure it overlaps. Plot the statistical version with lines (plot(...,type="l") or curve(...) and then add the population-dynamic version with points (points() or curve(...,type="p",add=TRUE)).
Small hint: the population-dynamic version has an extra parameter, so one of r, n(0), and K will be set to a constant when you translate to the statistical version.
Big hint: Multiply the numerator and denominator of the statistical form by exp(-a) and the numerator and denominator of the population-dynamic form by exp(rt), then compare the forms of the equations.



File translated from TEX by TTH, version 3.67.
On 14 Sep 2005, 16:42.