Stat 579: Writing Functions Ranjan Maitra

advertisement
Stat 579: Writing Functions
Ranjan Maitra
2220 Snedecor Hall
Department of Statistics
Iowa State University.
Phone: 515-294-7757
maitra@iastate.edu
,
1/13
Writing Functions
One of the most important features of R is the ability to
write and modify functions to perform computations for
which built-in R functiona are not already available.
General form of a R function is:
function (arguments) expression
Simple example – square a number:
sqr <- function(x) x*x
The function is called using:
> sqr(2)
> h
APE BOX CAT DOG
15.1 11.3 7.0 9.0
> sqr(h)
APE BOX CAT DOG
228.01 127.69 49.00 81.00
The function sqr is vectorized because the operation “*” is
vectorized. It is important to make sure that all operations
in a function definition are vectorized if the function is
required to be vectorized.
,
2/13
Writing Functions – II
A function can take in multiple arguments:
logbb <- function(x,b) log(x)/log(b)
logbb(8,2)
An example of a function using recursion:
fact <- function(n) if (n<=1) 1 else n*fact(n-1)
In recursion, a function may call itself recursively, and is a
capability not available in some programming languages.
In general, using a recursion might be less efficient than
other methods such as using the prod() function to
compute the factorial, depending on how recursion is
implemented in a language.
The above functions all used a single R expression.
Usually, functions for performing many types of
computations are more complicated and require a
substantial number of R expressions. When more than a
single R expression is needed they must all be enclosed in
a pair of braces.
,
3/13
Writing functions – III
The function below, named larger1(), takes two vectors
(assumed here to be of the same length) as arguments,
compares them element by element, and returns a vector
that consists of elements that are the larger in magnitude
of the corresponding elements in the two vectors.
larger1 <- function(x,y) {
n <-length(x)
z <- rep(n,0)
for (i in 1:n) {
if (y[i] > x[i]) z[i] <- y[i] else z[i] <- x[i]
}
z
}
The above is not particularly efficient, using, as it does,
loops. So, we redo the same, avoiding loops:
bigger <- function(x,y) {
ifelse(y > x, y, x)
}
,
4/13
Returning Multiple Items
In general, functions return single values, therefore
multiple items are returned as (named) lists
quad <- function(a, b, c) {
x <- NA
d <- bˆ2 - 4 * a * c
if (d < 0) cat(‘‘*** Real roots do not exist!
else x <- (-b + c(-1, 1) * sqrt(d)) / (2 * a)
list(Root1 = x[1], Root2 = x[2])
}
result <- quad(6, -5, 1)
result$Root1
result$Root2
*** \n’’)
Our next application will be to write functions to perform
some iterative methods. For that, we will need some
background.
,
5/13
An Overview of Some Iterative Methods
A variety of problems exist where the solution to a problem
can be improved by applying the same calculation
repeatedly, each time using the previous solution as the
starting value. Such a mathematical process is said to
form a convergent sequence, if the solution converges to a
value such that the error becomes uniformly smaller in
each iteration. The problem of finding the solution to the
equation f (x) = 0 where f is a nonlinear function of a
single variable x, is used in this class as an example of
application of this method.
Example: Find the root of f (x) = x 3 − 3x + 1 = 0
contained in the interval [0, 1] by an iterative method.
There are several approaches to obtaining iterative
algorithms for this problem. We look at a few of these
methods next.
,
6/13
The Bisection Algorithm
The class of methods where only the value of the function
at different points are calculated is known as search
methods.
The best known iterative search methods are called
bracketing methods since they attempt to reduce the width
of the interval where the root is located while keeping the
successive iterates bracketed within that interval.
The simplest bracketing method is the bisection algorithm
where the interval of uncertainty is halved at each iteration,
and from the two resulting intervals, the one that straddles
the root is chosen as the next interval.
Example: Consider the function f (x) to be continuous in
the interval [x0 , x1 ] where x0 < x1 and that it is known that a
root of f (x) = 0 exists in this interval.
,
7/13
The Bisection Algorithm – Pseudo-code
Select x , f , andstarting values x0 < x1 3 f (x0 ) ∗ f (x1 ) < 0
Repeat
1
2
3
set x2 = (x0 + x1 )/2
if f (x2 ) ∗ f (x0 ) < 0, set x1 = x2
else
set x0 = x2
until |x1 − x0 | < x or |f (x2 )| < f
Return x2
Stopping Rule: When the bracket is small enough or when
the function value is close enough to zero.
To use the bisection algorithm, it is easily verified by
plotting the function that the f (x) = x 3 − 3x + 1 = 0 has a
single root in the interval [0, 1]:
>
>
>
>
,
f <- function(x) { xˆ3 - 3 * x + 1}
x <- seq(from = 0, to = 1, length = 100)
plot(x = x, y = f(x), type="l")
abline(h = 0, lty = 3)
8/13
The Bisection Algorithm
bisection <- function(fun, lower, upper, epx = 1e-03, epf =
1e-03)
{
if (lower > upper) {
x1 <- lower
lower <- upper
upper <- x1
}
x0 <- lower
x1 <- upper
if ((fun(x0) * f(x1)) >= 0) {
cat(’No root in interval. Come back with better brackets’)
}
else {
repeat {
x2 <- (x0 + x1) / 2
if (f(x2) * f(x0) < 0) {
x1 <- x2
}
else x0 <- x2
if ((abs(x1 - x0) < epx) | (abs(f(x2)) < epf)) break
}
x2
}
}
,
9/13
Fixed-point Iteration
By re-expressing f (x) = 0 in the form x = g(x), one can
obtain an iterative formula x(i+1) = g(x(i) ) for finding roots
of nonlinear equations. Any such function g, called the
mapping function, must satisfy conditions so that the
sequence x(1) , x(2) , . . . converges to a unique solution in a
specified interval [a, b].
To use the fixed point method for f (x) = x 3 − 3x + 1 = 0 ,
rewrite it in the form x = (x 3 + 1)/3 giving
g(x) = (x 3 + 1)/3. It can be verified that this choice of g
satisfies the necessary conditions for the iteration to
produce a convergent sequence.
To obtain a starting value, we use the plot of f (x) in the
interval [0, 1]. The plot clearly shows that a root exists is
(0.3, 0.4). In practice, several intervals may be searched
for the existence of roots by trial and error, and is called a
grid search.
,
10/13
Fixed-point Iteration Algorithm: R function
function(g, x0, nlim, eps)
{
iterno <- 0
repeat {
iterno <- iterno + 1
if (iterno > nlim) {
cat(’Iteration Limit Exceeded: Current = ’,iterno, fill = T)
x1 <- NA
break
}
else
{
x1 <- g(x0)
cat(’****Iter. No: ’, iterno, ’ Current Iterate = ’, x1, fill
= T)
if (abs(x1 - x0) < eps || abs(x1 - g(x1)) < 1e-10)
break
x0 <- x1
}
}
return(x1)
}
> simple(g, 0.4, 100, 1e-8)
,
11/13
Newton-Raphson Iteration
Consider a starting value x(0) for obtaining an iterative
formula to solve f (x) = 0.
If the next iterate x(1) is very close to x(0) then f 0 (x(0) ) can
be approximated as f 0 (x(0) ) ≈
f (x(0) )
x(0) −x(1) .
f (x
)
This leads to the update x(1) ≈ x(0) − f 0 (x(0) ) , which leads to
(0)
the Newton-Raphson iterative formula
x(i+1) = x(i) −
f (x(i) )
f 0 (x(i) )
Newton-Raphson iterations produces a convergent
sequence as long as x(0) is chosen to be close to a root of
f (x) = 0. This method has quadratic convergence, i.e., the
error is reduced quadratically at each iteration.
For f (x) = x 3 − 3x + 1, we have f 0 (x) = 3x 2 − 3. The
Newton-Raphson iteration for solving x 3 − 3x + 1 = 0 is
x(i+1) = x(i) −
,
3
x(i)
− 3x(i) + 1
2 −3
3x(i)
12/13
Newton-Raphson Iteration: R function
newton <- function(fun, derf, x0, eps)
{
iter <- 0
repeat {
iter <- iter + 1
x1 <- x0 - fun(x0) / derf(x0)
if (abs(x0 - x1) < eps || abs(fun(x1)) < 1e-10)
break
x0 <- x1
cat(’****** Iter. No: ’, iter, ’ Current Iterate = ’,
x1,fill=T)
}
return(x1)
}
> fun <- function(x){xˆ3 - 3*x + 1}
> derf <- function(x) { 3*xˆ2 -3 }
> newton(fun, derf, 0.4, eps = 0.000001)
> fun2 <- function(x) {x*exp(-x) - 0.2}
> x <- seq(from = -1, to = 4, length = 100)
> plot(x,fun2(x),type="l")
> abline(h=0,lty=2)
# clear that there is a root near 0.0 and another near 2.0
> derf2 <- function(x) { (1 - x) * exp(-x) }
> newton(fun2, derf2, 0.0, 0.000001)
> newton(fun2, derf2, 2.0, 0.000001)
,
13/13
Download