Scientific Programming with R Guide
Scientific Programming with R Guide
What is R ?
Trustworthy software
The complexity of the data processes and of the computations applied to
them mean that those who receive the results of modern data analysis have
limited opportunity to verify the results by direct observation. Users of the
• Statistical computing environment, and programming language.
analysis have no option but to trust the analysis, and by extension the
software that produced it. Both the data analyst and the software provider • Very popular in many areas of statistics, computational biology.
therefore have a strong responsibility to produce a result that is trustworthy, • “Programming with data” (Chambers)
and, if possible, one that can be shown to be trustworthy. • Approach: command-line for one-liners; interactive usage; write
scripts/functions for larger work (edit/run cycle); develop package for
This places an obligation on all creators of software to program in such a consolidation and distribution.
way that the computations can be understood and trusted.
7 / 191 8 / 191
History Strengths of R
9 / 191 10 / 191
• Flexible language, similar to matlab, but definitely not “everything is a • Start-up: type ‘R’ at command line.
matrix”. Frames, lists, vectors . . . • Type commands interactively, and get results.
• From matlab to R : • Type commands into a file; source( ' myfile .R'); edit file . . .
[Link] • Mac/Win has a GUI for interactive use, with internal editors.
• Comprehensive matlab and R guide: http: • All platforms have a command-line interface
//[Link]/faculty/hiebeler/comp/[Link] • Many external editors have support for R , including
• Use x[i] not x(i) for indexing vectors. • Emacs through ESS ([Link]
• Making vectors: x <- c(10, 9, 5, 1) • Eclipse IDE ([Link]
• Rstudio ([Link]
• Assignment: best to use <- rather than =.
• ...
13 / 191 14 / 191
15 / 191 16 / 191
Objects and Functions Objects and Functions
[1] 3
17 / 191 18 / 191
21 / 191
[1] 2.995732 3.891820 2.772589 4.094345 4.605170 22 / 191
[1] 104 105 106 107 108 109 110 111 112 113 114 115 116 117
23 / 191
[15] 118 119 24 / 191
Accessing and setting elements Accessing and setting elements
Can also provide a logical vector of same length as vector (logical values
explained later). Elements can be set in several ways
25 / 191 26 / 191
[1] 6 5 10 4 2
27 / 191 28 / 191
Recycling rule (Advanced) Naming indexes of a vector
> x <- 1:10
> y <- x * 2 > joe <- c(24, 1.70)
> z <- x^2 > joe
> y + z
[1] 24.0 1.7
[1] 3 8 15 24 35 48 63 80 99 120
> names(joe)
> x + 1:2
NULL
[1] 2 4 4 6 6 8 8 10 10 12
> names(joe) <- c("age", "height") ## replacement function
> x + 1:3 > joe
• length()
> joe["height"] == joe[2]
• rev()
height • sum(), cumsum(), prod(), cumprod()
TRUE • mean(), sd(), var(), median()
• min(), max(), range(), summary()
Refering to index by name rather than by position can make code more
readable, and flexible. Cannot do things like x[1:4] easily though, since • exp(), log(), sin(), cos(), tan() (radians, not degrees)
you need to name all four elements you want. • round(), ceiling(), floor(), signif()
Although extremly useful, names have a cost when processing large objects. • sort(), order(), rank()
• which(), [Link]()
Note: in second use of names() above, we are actually using the
• any(), all()
replacement function names<-, see later.
31 / 191 32 / 191
Functions as function args Outline
Introduction
Vectors
Calling functions
Functions can be called within function calls; the following are equivalent: Scripts
Matrices
> x <- c(3, 2, 9, 4) Boolean logic
> Lists
> y <- exp(x); z1 <- which(y > 20) ## case 1 Factors
> z2 <- which ( exp(x) > 20) ## case 2 Character arrays
> Objects in your environment
> [Link](z1, z2) Basic plotting
Reading/writing data to file system
[1] TRUE
Writing functions
Conditionals and looping
Vectorization
Random number generation
Debugging
33 / 191 34 / 191
Default values for function arguments Default values for function arguments
35 / 191 36 / 191
Argument matching Argument matching
Typical calls are as follows:
> seq(1, 3, 0.5) ## positional matching
R has a flexible method for specifying arguments to function. We can either
provide an actual value for a formal argument, or give arguments as [1] 1.0 1.5 2.0 2.5 3.0
key=value (or formal=actual).
> seq(1, 5,[Link]=3) ## can skip args (e.g. by)
As an example, let’s look at help for seq:
[1] 1 3 5
seq(from = 1, to = 1, by = ((to - from)/([Link] - 1)),
[Link] = NULL, [Link] = NULL, ...) > seq(to=5) ## order not important.
[1] 1 2 3 4 5
(NB: in seq(from=x), from is the formal argument of the function, and
here x is the actual value.) > seq(f=5,t=1) ## abbrev tags.
The ... notation allows for other arguments to be passed, which are not [1] 5 4 3 2 1
used by this function. > seq(len=5, 1,2) ## tags removed before positional matching
37 / 191 38 / 191
Why do some functions, like sqrt, require only one argument, yet others
take many arguments?
Functions like c, cbind, have ... in the arguments: > x <- 1:5
Usage: > x
[1] 5
...: objects to be concatenated.
The ... indicate any number of objects may be passed, not just (say) one > length(x) <- 2
or two. > x
The result of c() is to combine them all into one long vector, taking into
account if the keyword “recursive” is provided (when args are first flattened). [1] 1 2
The ... can also indicate that other arguments can be provided which are
not processed directly by this function, but may be useful for other functions
(e.g. popular when plotting).
39 / 191 40 / 191
Replacement functions (Advanced) Getting help: key commands
41 / 191 42 / 191
[1] FALSE
> [Link](1L)
[1] TRUE
43 / 191 44 / 191
Numbers and special values Numbers and special values
> typeof(NA)
> typeof(NULL)
[1] "NULL"
45 / 191 46 / 191
> 3 * 4 + 2 != 3 * (4 + 2)
[1] TRUE
> 3 * 4 + 2 != 3 * (4 + 2)
> 2^3+1 != 2^(3+1) > 2^3+1 != 2^(3+1)
> 1:5-1
[1] TRUE
> 1:5-1
[1] 0 1 2 3 4
47 / 191 48 / 191
Operator precedence ?Syntax Operators
Subset taken from ?Syntax, see that page for full list. Highest precedence
at top.
'[ [[' indexing Most operators will be familiar, but some may not:
'$ @' component / slot extraction
'^' exponentiation (right to left) x <- 10
'- +' unary minus and plus x == 4 ## test for equality
':' sequence operator x != 10 ## not equal?
'%any%' special operators 7 %/% 2 ## division, ignoring remainder. (3)
'* /' multiply, divide 7 %% 2 ## remainder (1)
'+ -' (binary) add, subtract x <- 9 ## assignment
'< > <= >= == !=' ordering and comparison x <<- 9 ## assign x to 9 in the global env. (BAD)
'!' negation ## Raising to a power can be done in two ways.
'& &&' and [Link]( 10.1 ** 2.5, 10.1^2.5 )
'| ||' or
'<- <<-' assignment (right to left)
'?' help (unary and binary)
Bottom line: use parentheses to order preference.
49 / 191 50 / 191
55 / 191 56 / 191
Running scripts in batch (Advanced) Rscript
• Rscript works as an interpreter. It is quicker to start than R (fewer
packages are loaded?).
• At the command line, type R CMD BATCH trig.R. R will start up,
• Can use interactively or for standalone scripts.
process your commands and then quit.
• Output is stored in the file [Link] $ Rscript -e 'round(runif(10))'
• If there were no errors, the last line of the output file shows the time
[1] 0 1 0 0 1 0 1 1 0 0
taken to run the script.
• Any output is not shown on the screen but sent to a PDF called #!/usr/bin/env Rscript
[Link]. args <- commandArgs(TRUE)
• This is a GREAT way of testing your scripts, since R starts with an
stopifnot(length(args)==3)
empty workspace, you will see if you have all the steps needed. args = [Link](args)
• Aim to always leave your scripts in a working state at the end of a n = args[1]; mean = args[2]; sd = args[3]
session, so that a few days later you don’t have to remember why it rnorm(n, mean, sd)
wasn’t working!
$ ./simple_rnorm.R 5 10 2
[1] 8.651807 13.372094 10.063347 10.703155 6.351886
57 / 191 58 / 191
> ## 1: ok - all fits onto one line, just. > ## 3: solved, by moving the operator (+) up.
> (x <- sqrt( c(100, 200, 300, 400, 500) ) + 10) > x <- sqrt( c(100, 200, 300, 400, 500) ) +
+ 10
[1] 20.00000 24.14214 27.32051 30.00000 32.36068
> x
> ## 2: not okay -- first line is seen as complete.
[1] 20.00000 24.14214 27.32051 30.00000 32.36068
> x <- sqrt( c(100, 200, 300, 400, 500) )
> + 10 > ## 4: as 3, but indentation makes it clearer.
> x <- sqrt( c(100, 200, 300, 400, 500) ) +
[1] 10
+ 10
> x
> x
[1] 20.00000 24.14214 27.32051 30.00000 32.36068
[1] 10.00000 14.14214 17.32051 20.00000 22.36068
61 / 191 62 / 191
Outline Matrices
Introduction A matrix is just a vector with some additional markup to reformat it. Matrix
Vectors stored in column-major order (like fortran, unlike C).
Calling functions > x <- 1:6
Scripts > [Link](x)
[1] 4
> x[, 2, drop=TRUE] ## default
> x[1,] ## extracting row
[1] 3 4
[1] 1 3 5
> x[, 2, drop=FALSE] ## gotcha!
> x[1:2, 2:3]
[,1]
[,1] [,2] [1,] 3
[1,] 3 5 [2,] 4
[2,] 4 6
[1] 3 4
65 / 191 66 / 191
• matrix()
• cbind() > matrix( floor(runif(6, max = 50)), nrow = 3 ) ## ncol = 2
• rbind()
[,1] [,2]
[1,] 43 37
> m <- matrix( floor(runif(6, max=50)), nrow=3) [2,] 6 8
> x <- rbind( c(1,4,9), c(2,6,8), c(3,2,1)) [3,] 47 48
> y <- cbind( c(1,2,3), 5, c(4,5,6))
67 / 191 68 / 191
Typical matrix construction methods Typical matrix construction methods
> rbind( c(1,4,9), c(2,6,8), c(3,2,1) ) > cbind( c(1,2,3), 5, c(4,5,6) ) ## recycling again
69 / 191 70 / 191
[,1] [,2]
[,1] [,2] [1,] 5 7
[2,] 6 8
[1,] TRUE TRUE
[2,] TRUE TRUE , , 3
[,1] [,2]
[1,] 9 11
[2,] 10 12
73 / 191 74 / 191
> d[which(d>5.0)]
> d <- c(3.2, 1.0, 4.0, 9.2, 2.3, 8.1, 6.3) [1] 9.2 8.1 6.3
> d > 5.0
> [Link] <- (d > 3.0) & (d< 5.0)
[1] FALSE FALSE FALSE TRUE FALSE TRUE TRUE > d[[Link]]
> d[d> 5.0] [1] 3.2 4.0
[1] 9.2 8.1 6.3 > d[![Link]]
> which(d>5.0) [1] 1.0 9.2 2.3 8.1 6.3
[1] 4 6 7 > ifelse(d > 3.0, 1.0, 0.0) ## same as [Link](d > 3)
[1] 1 0 1 1 0 1 1
77 / 191 78 / 191
2
example from Hadley Wickham’s devtools
81 / 191 82 / 191
> length(l)
[1] 1.7
85 / 191 86 / 191
87 / 191 88 / 191
Modifying lists (Advanced) Modifying lists (Advanced)
89 / 191 90 / 191
91 / 191 92 / 191
Data frames Data frames
> cbind(a, ht) ## matrix, preserves > cbind(nms, ht) ## matrix, ht col becomes strings.
a ht nms ht
[1,] 24 1.70 [1,] "joe" "1.7"
[2,] 19 1.80 [2,] "fred" "1.8"
[3,] 30 1.75 [3,] "harry" "1.75"
93 / 191 94 / 191
95 / 191 96 / 191
Data frames Outline
Compare how a data frame (d) is printed, compared to printing Introduction
[Link](d). Vectors
> d Calling functions
Scripts
name age height student
1 joe 24 1.70 TRUE Matrices
2 fred 19 1.80 FALSE Boolean logic
3 harry 30 1.75 TRUE
Lists
> [Link](d) Factors
Character arrays
$name
[1] joe fred harry Objects in your environment
Levels: fred harry joe Basic plotting
$age Reading/writing data to file system
[1] 24 19 30 Writing functions
$height Conditionals and looping
[1] 1.70 1.80 1.75 Vectorization
$student Random number generation
[1] TRUE FALSE TRUE Debugging
97 / 191 98 / 191
[1] 2 3 1 3 1 1 2
sqrt of__134__is__11.57584__
> cat("5\t9\n_")
5 9
_ Don’t forget paste0() where sep=”.
(Humour): [Link]
paste() returns a string, e.g. for assignment.
paste0-is-statistical-computings-most-influential-contribution-of-
> x <- 1:5; [Link] <- '/home/stephen/res'
> file <- paste([Link], '/expt_res', x, '.dat', sep='')
> file
[1] "/home/stephen/res/expt_res1.dat"
[2] "/home/stephen/res/expt_res2.dat"
[3] "/home/stephen/res/expt_res3.dat"
[4] "/home/stephen/res/expt_res4.dat"
[5] "/home/stephen/res/expt_res5.dat"
105 / 191 106 / 191
Strings Strings
[1] TRUE
111 / 191 112 / 191
Inspecting variables and the environment Converting an object from one type to another
Some ways of finding out what kind of object you have and converting.
x <- 9
y<- c(2,4,5) mode(y)
m <- matrix(2:5, 12,10) typeof(y)
objects() ## what vars do I have? class(y)
ls() ## shorthand for objects. [Link](y)
str(m) ## Display compact representation [Link](y) ## [Link]() check if type XYZ
head(m) [Link](y)
tail(m) [Link](m) ## convert object to type XYZ
rm(list = ls()) ## clear up the working environment l <- list(x=c(1,4), y=c(6,9,12))
ls() unlist(l)
[1] 10 Bioconductor provides many ad hoc classes to store, manipulate and process
microarray, RNA Seq, proteomics, flow cytometry, . . . data.
> sum
[1] 12
115 / 191 116 / 191
Outline Basic plotting
Introduction
Vectors
Calling functions
Scripts
Matrices
Boolean logic • Basic x,y plots (scatter plots)
Lists • Multiple plots in one figure
Factors • Saving your plots
Character arrays
Objects in your environment This section will just introduce the mechanics of making basic plots, rather
Basic plotting than worry about interpreting them.
Reading/writing data to file system
Writing functions
Conditionals and looping
Vectorization
Random number generation
Debugging
117 / 191 118 / 191
●
●
●
●●
●
●
●●
●
●
●●
●
● ●
●
●
●
●
●
●●
●
●
●●
●
●
●●
●
●
●●
●
●
●●
●
●
●● ●●
●
●
●
●
●
●
●●
●
●
●●
● cos(2x)
●●
●
● ●
●● ●
●
●● ●●
●
●
●●
● ●●
● ●
●
● ●
●
●
●
● ●
●
● ●
●
● ●
●
●
●
● ●
●
● ●● ●
●
●● ●● ●
● ●
●
● ● ● ●
1.0
●
● ●
● ●
●
● ●
●
●
● ●
● ●● ●
●
●
● ●
● ●
● ●
●
● ●
● ●● ●
●
●
● ● ● ●
●
0.5
●
● ●● ●
● ●
●
●
●●
●
●
●●
● ●
●
●
●
●
●
●
●
●
●
●
●●
●
●
●●
●
●
●
●
●
●
● ● ●
● ●
0.5
●● ● ●
● ●
● ●● ● ●
●
●
● ● ●
● ●
●
● ● ●
● ●
● ●
● ● ●●
●● ● ●
● ●
0.0
●
● ● ●● ●
● ●
y
●● ● ●
● ●
●
● ●● ●● ●
●
●● ● ●
● ●
0.0
● ● ● ●
● ●
y
●
● ● ● ●●
●
● ●
● ●
● ●
● ● ●
● ●
●● ●
● ● ●
●
●
● ● ●● ●
●
● ● ● ●
−0.5
●
● ●
● ●
● ●
●
● ●
● ●
● ●
●
●
● ●
● ●
● ●
●
●
−0.5
●
● ●
● ●
● ●
●
● ●
● ●
● ●
●
●
● ●
● ●
● ●
●
●
● ●
● ●
● ●
●
●
● ● ●
● ●
●
●
●
●
●●
●
●
●●
●
●
●● ●
●
●
●
●
●
●
●
●
●
●
●
● ●
●●
●
●
●●
●
●
●●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
cos(2x)
● ●
−1.0
●
●
●●
● ●
●
●● ●
●
●
● ●●
●
●
●
●
●●
●
● ●
●
●
●● ●
●
●
●● ●
●●
●
●
−1.0
●●
●
●
●●
●
●
●●
●
●
● ●
●
●●
●
●
●●
●
●
●●
● sin(2x)
0 1 2 3 4 5 6 0 1 2 3 4 5 6
●
●
3.5
●
●
● ●
●
● ●
●
3.0
●
5
● ●
● ●
●
●
● ●
2.5
●
● ●
●
●
4
● ●
●
2.0
● ●
●
● ●
z
●
1.5
●
3
● ●
●
●
1.0
●
●
●
0.5
●
●
0.0
●
1
0 5 10 15 20 25 30 0 5 10 15 20 25 30
x x
1.0
0.5
sin(2 * x)
0.0
pdf(file = 'mfrow_eg.pdf',
width = 4, height = 6)
−1.0
−1.0
par(mfrow = c(3,2))
par(mar = c(3.5, 3.5, 1.5, 0.5),
mgp = c(2.5, 1, 0))
0 1 2 3
x
4 5 6 0 1 2 3
x
4 5 6 > [Link]()
x <- seq(from = 0, to = 2*pi, sin (3x) cos (x)
> pdf(file='[Link]', width=7, height=7) ## inch
1.0
1.0
len = 100)
> [Link]()
0.5
0.5
cos(x)
type = 'l')
0.0
0.0
1.0
0.5
type = 'l')
> [Link]()
cos(2 * x)
cos(3 * x)
0.0
type = 'l')
[Link]()
−1.0
−1.0
0 1 2 3 4 5 6 0 1 2 3 4 5 6
Zoom in on text of PNG to see limitations of this format.
x x
Estimated Density 0 1
60 65 70 75 80
Soprano 2 Soprano 1 6
0.25
0.20
●
0.15 ●
●
0.10
0.05
R has a vast range of functions for plotting particular data types. You may
●●●●●
●●●●● ● ●●●●●●
●●●● 0.00
Alto 2 Alto 1
0.25
factor(cyl)
wt
● ●
0.00 ●●●●● ●●
● ●●●●●●●●●●● ● ●
● ● ●● ● ● 6
Tenor 2 Tenor 1
●
● ●
● 8
●
0.25
• ggplot2 – [Link]
●
● ●●●● ● ● ●●●●●●●●●●● ● 0.00 ●
●
Bass 2 Bass 1 2 ●
●
0.25
0.20 ●
●
0.15
0.10
0.05
0.00 ●●●●●●● ●● ● ●●●●●●●●
●
60 65 70 75 80
10 15 20 25 20 25 30
Height (inches) mpg
Outline
Introduction
Vectors
Calling functions
Here are some starting points to explore: Scripts
• demo(graphics) to see diversity of plots. Matrices
• low-level functions: symbols(), rect(), segments(), abline(). Boolean logic
Lists
• curve(x*log(x))
Factors
• R graphics gallery
Character arrays
[Link] Objects in your environment
[Link]
Basic plotting
Reading/writing data to file system
Writing functions
Conditionals and looping
Vectorization
Random number generation
Debugging
127 / 191 128 / 191
Reading/writing data to file system Interacting with the file system
Saving your workspace with .RData files Further I/O functions (Advanced)
Functions Functions
[1] 379
143 / 191 144 / 191
Handling unbound variables (2) The ... arguments
In this case, better to define thresh as an argument of the function, and
provide a default value:
> p <- function(x, y, ...) { Convention for a replacement function is that the name should end with <-.
+ if (diff(range(x)) > diff(range(y))) {
The last argument of the replacement function must be called value and is
+ plot(x, y, ...)
+ } else { the RHS of the assignment.
+ plot(y, x, ...)
> "threshold<-" <- function(x, value) {
+ } + ## X is the object to update
+ } + ## VALUE is the value on the RHS.
> p(rnorm(10, 0, 1), rnorm(10, 0, 5), main = "Expect swap", + y <- ifelse(x > value, 1, 0)
+ return(y)
+ col = "red") + }
> p(rnorm(10, 0, 5), rnorm(10, 0, 1), main = "No swap", pch = 19) > x <- c(0.3, 0.1, 0.6, 0.7, 0.9, 0.2)
> threshold(x) <- 0.4
Expect swap No swap
> x
2.5
● ●
0.5
● ● ●
[1] 0 0 1 1 1 0
1.5
●
●
0.0
x
● ●
0.5
● ●
●
● ●
−0.5
object slots.
●
●
−1.0
●
● ●
−10 −5 0 5 −5 0 5 10
y x
> x <- 8;
>
> if (x > 10) {
• if + ## condition was true
• switch + cat("x is bigger than 10\n")
+ } else {
• for
+ cat("x is 10 or less\n")
• while
+ }
• Vectorization
• simple applications – numerics x is 10 or less
Notes:
“else ...” can be omitted if you do not need it.
if returns a value, which can be assigned, e.g. y <- if (x <10) 40
else 20. A better solution in this case however is the vectorized form
y <- ifelse(x<10, 40, 20)
151 / 191 152 / 191
Braces in conditional constructs switch (Advanced)
Curly braces not needed if there is only one expression in the if clause: Nested if ... else commands can get a bit messy. Like other languages, R has
a switch construct. From ?switch:
if ( x > 10 ) { if ( x > 10 )
y <- 1 y <- 1 > centre <- function(x, type) {
} ## + switch(type,
+ mean = mean(x),
But braces are needed in multiline if/else statement: + median = median(x),
+ trimmed = mean(x, trim = .1))
+ }
if ( x > 10 ) { if ( x > 10 ) > x <- rcauchy(10)
y <- 1 y <- 1 > centre(x, "mean")
} else { else
y <- 0 ## OK y <- 0 ## NOT OK [1] -2.801097
} ##
> centre(x, "median")
From ?Control: Note that it is a common mistake to forget to put
braces ('{ .. }') around your statements, e.g., after 'if(..)' or [1] 0.08003974
'for(....)'. In particular, you should not have a newline between
> centre(x, "trimmed")
'}' and 'else' to avoid a syntax error in entering a 'if ... else'
construct at the keyboard or via 'source'. For that reason, one
[1] -0.05755348
(somewhat extreme) attitude of defensive programming is to always
use braces, e.g., for 'if' clauses. 153 / 191 154 / 191
[1] TRUE
repeat expr will repeatedly execute expr until you break out of the loop. next allows you to skip to next iteration of a loop. Both next and break
can be used within other loops (while, for).
> i <- 3
> repeat { > for (i in 1:10) {
+ if (i == 5) { + if ((i %% 2) == 0)
+ break + next
+ } else { + print(i)
+ cat("i is", i, "\n") + }
+ i<- i+1
+ } [1] 1
+ } [1] 3
[1] 5
i is 3 [1] 7
i is 4 [1] 9
> trial1 <- function(n, [Link]) { > trial2 <- function(n, [Link]) { > trial1 <- function(n, [Link]) { > trial2 <- function(n, [Link]) {
+ count <- 0 + [Link] <- runif(n) + count <- 0 + [Link] <- runif(n)
+ for (i in 1:n) { + sum( [Link] < [Link]) + for (i in 1:n) { + sum( [Link] < [Link])
+ if (runif(1) < [Link]) + } + if (runif(1) < [Link]) + }
+ count <- count +1 > + count <- count +1 >
+ } > res <- replicate(ntrials, + } > res <- replicate(ntrials,
+ count + trial2(n, p)) + count + trial2(n, p))
+ } > hist(res) + } > hist(res)
> >
> res <- rep(0, ntrials) > res <- rep(0, ntrials)
> for (j in 1:ntrials) { > for (j in 1:ntrials) {
+ res[j] <- trial1(n, p) + res[j] <- trial1(n, p)
+ } + }
> hist(res) > hist(res)
165 / 191 In this case, hist( rbinom(1000, 100, 0.6)) would also work! 166 / 191
e.g. how to compute sum of each row of a matrix? sum(A) will normally Other functions: lapply (apply to list), sapply (simplify), replicate, . . .
return the sum of all elements of A.
> lapply(ls(), [Link])
apply(X, MARGIN, FUN, ...) > sapply(ls(), [Link])
MARGIN = 1 for row, 2 for cols. > tapply(1:20, factor(rep(letters[1:5], each = 4)), sum)
FUN = function to apply > ## see also ?by
... = extra args to function. > mapply(sum, 1:5, 1:5, 1:5)
> hist( replicate(200, mean(rnorm(100)) ))
Sometimes you don’t want to pollute name space by defining a new function,
so just use an “anonymous function”, i.e. a function without a name. 0, 1, 1, 2, 3, 5, 8, 13, 21, . . .
Particularly useful e.g. in an apply call.
f [n] = f [n − 1] + f [n − 2]
> [Link] <- matrix(1:10, ncol=5)
How to vectorize?
> apply([Link], 2, function(x) { sum(x^2) + 10 })
Exercise: write a function, fibonnaci(n) that returns the nth element of
the sequence. Assume that fibonnaci(1) == 0, fibonacci(2) == 1.
Since functions are just objects, anonymous functions are just objects Exercise: use fibonacci() to estimate the golden ratio.
without names, similar to ’anonymous numbers’ like a+b in an expression
a+b+c.
3
This is not optimisation, but rather good coding practice.
171 / 191 172 / 191
Numerics issues Numerics issues
Although integer arithmic is reliable, floating-point arithmetic is to be
treated with care! (All R ’s calculations are in what C programmers call
“double precision”.) Compare:
> 1.0 + 2.0 == 3.0
[1] FALSE
> a * a - 2
[1] 4.440892e-16
[1] FALSE
175 / 191 176 / 191
Outline Random number generation
Introduction
Vectors
Computers usaully generate “pseudo-random numbers”. They are generated
Calling functions
based on some iterative formula:
Scripts
Matrices xnew = f (xold ) mod N
Boolean logic
Lists where modulo operation provides the “remainder” division.
Factors To generate the first random number, you need a seed.
Character arrays Setting the seed allows you to reliably generate the same sequence of
Objects in your environment numbers, which can be useful when debugging programs.
R has many routines for generating random samples from various
Basic plotting
distributions, but for now we will just use runif(), (and maybe rnorm()).
Reading/writing data to file system
Exercise: write a random number generator. See: “Randu: a bad random
Writing functions
number generator”. [Link]
Conditionals and looping
Exercise: Apply the central limit theorem to generate samples from a
Vectorization
normal distribution by adding together samples from a uniform distribution.
Random number generation
Debugging
177 / 191 178 / 191
x <− rnorm ( 1 0 0 0 )
(1− pnorm ( 0 . 7 ) ) ∗ l e n g t h ( x ) ## e x p e c t e d .
f i n d . high (x , 0.7)
183 / 191 184 / 191
recover Packages
recover() is like browser(), except you can choose which level to inspect, • R has a packaging system for external code.
rather than the level at which browser was called. • A package is loaded from a library using library("[Link]").
Following allows recover() to be launched when you hit an error:
• Beware: don’t call a package a library! A library is a group of folders
o p t i o n s ( e r r o r=r e c o v e r ) where packages are stored . . .
Here we simply tell R that when an error is generated, we call the function
“recover”. The default is NULL, in which case stop is called. > library() ## view available packages
> library(help="cluster") ## what's in this?
From ?options: > library("cluster") ## load package
Note that these need to specified as e.g. > example(pam) ## can use pam and friends.
options(error=utils::recover) in startup files such as .Rprofile. > detach("package:cluster") ## remove pkg.
• Use one file to store code and document. Best shown by way of
example... [Link]
> library("tkWidgets")
> vExplorer()
• knitr is the natural successor to Sweave. It rocks!
191 / 191
Naming indexes of a vector in R allows for referencing vector elements by name rather than by position, enhancing code readability and making it more intuitive. It improves flexibility because code is easier to understand and maintain, especially when the data structure changes or becomes more complex .
When managing multiple libraries in R, it's essential to set appropriate library paths using '.libPaths()' for custom package storage and to ensure write permissions for installation. Utilizing environment variables in configuration files like '.bashrc' can streamline library management. Responsible use includes ensuring no overwriting of existing settings and maintaining clear documentation for easy package accessibility and updates .
Control flow constructs like 'if' and 'ifelse' can optimize computational efficiency by selectively executing code based on logical conditions, thereby preventing unnecessary computation. The 'ifelse' function is particularly efficient as it is vectorized, allowing simultaneous evaluation and assignment across vector elements, making it faster and more concise than using multiple 'if' statements .
The 'round()' function in R serves as a practical example of using both default and explicitly specified arguments. When no arguments are specified for 'round()', it results in an error since at least one argument is required. Specifying only the vector rounds each element to the nearest whole number by default. Providing both a vector and a 'digits' argument allows specific control over decimal places, demonstrating the significance of properly using function arguments for intended output and precision in data analysis .
The '...' operator in R allows functions to accept an indefinite number of additional arguments, offering flexibility in function definition and usage. This is particularly useful when writing wrapper functions or when the exact arguments needed depend on the context or conditions within a function body .
Replacement functions in R provide a way to assign new values or attributes to specific elements or slots within data structures, promoting modular code design and dynamic updates. A potential pitfall is that misuse or errors within replacement functions can lead to unintended data overwrites or corruption, thus requiring careful design and testing to ensure data integrity .
CRAN, the Comprehensive R Archive Network, is a vital repository for R packages that supports the R ecosystem by providing a centralized platform for accessing thousands of contributed packages. It enhances R's utility in statistical computing by allowing users to leverage a vast array of pre-written functions for diverse analytical needs, thereby fostering both collaboration and innovation within the R community .
Interactive use of R caters to exploratory data analysis or quick computations where immediate feedback is necessary, making it suitable for the initial stages of projects or for casual users. In contrast, scripting is crucial for reproducibility, automation, and scaling tasks, catering to systematic analyses and complex projects needing consistent re-run abilities that rely less on user intervention .
While using named indexes improves readability and flexibility, it comes with a cost when processing large objects because maintaining names requires additional memory and computing resources which can impact performance .
Recycling rules in R allow for element-wise operations between vectors of different lengths by repeating the shorter vector. This promotes code efficiency by eliminating the need for explicit loops to match vector lengths. However, it may lead to potential errors or unexpected results when the shorter vector's length is not a multiple of the longer vector's length, prompting a warning and possibly scrambling intended logic .