Programming in R
It’s now time for us to start writing code in R
No more copy and trust
Goals:
- Modify existing code to new applications
- Write code to use existing libraries
- Read and debug code
Execution Model
Three models of executing code:
- Line-by-line at
Console
- REPL: Read Evaluate Print Loop
- Best for transient, one-off actions; trying new things
- Script writing in a separate file
- Write in a separate (
.R) file
- Executes in same session; persistent state
- Best for longer analyses with complex commands, developing code
- Code in a Quarto document
- Write code in chunks inside a
qmd file
- Executes in a fresh session
- Best for documenting and conveying analysis, archiving results
Arithmetic in R
Basic arithmetic in R runs as expected
PEMDAS Ordering: Parentheses, Exponents, Multiplication/Division, Addition/Subtraction
Arithmetic in R
\[3^{2 * 5 - 1} / 24^5\]
\[\frac{1^1 + 2^2 + 3^3}{3^1 + 2^2 + 1^3}\]
(1^1 + 2^2 + 3^3) / (3^1 + 2^2 + 1^3)
When in doubt, extra parentheses don’t hurt
Function Calls
To go beyond basic arithmetic, need to invoke functions
\[ \cos(\pi) + \tan\left(\frac{\pi}{4}\right) + \sqrt{\sin(\pi/2)} - e^1\]
cos(pi) + tan(pi / 4) + sqrt(sin(pi/2)) - exp(1)
Function Calls
All function calls have a fundamental syntax:
name()
e.g.,
To get help with any function in R, type ?name
Function Calls
Most interesting functions require input:
name(argument)
Here, the argument is input to the function:
Multiple arguments are separated by commas
Function Calls
Type a name without () to see its implementation
function (x, na.rm = FALSE, type = 7)
diff(quantile(as.numeric(x), c(0.25, 0.75), na.rm = na.rm, names = FALSE,
type = type))
<bytecode: 0x11737fa88>
<environment: namespace:stats>
and
function (x, na.rm = FALSE)
sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x),
na.rm = na.rm))
<bytecode: 0x117635110>
<environment: namespace:stats>
Conceptually sqrt vs sqrt(4) is “concept of square rooting” vs “the actual square root of 4, i.e., 2”
Function Calls
Most important for users are the first few lines (args)
- Define optional and required inputs
function (x, base = exp(1))
Two arguments:
x: the input
base: optional 2nd argument with default \(e\) (natural log)
Function Calls
Every argument has a name, but not always required
R is usually smart about knowing what you meant
These are equivalent:
log(10)
log(10, exp(1))
log(x = 10)
log(x = 10, exp(1))
log(x = 10, base = exp(1))
log(base = exp(1), x = 10)
log(base = exp(1), 10)
Vector Semantics
Often when dealing with data, we want to transform related data similarly:
- E.g., change all temperatures in data set from F to C
Dangerous to only do part
R has vectorized semantics - whenever possible, do same operation to all numbers together
sqrt(1:10) # Keeps same vector structure
[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427
[9] 3.000000 3.162278
Vector Semantics
“Vectorization” comes from math (linear algebra):
\[\begin{pmatrix} 1 \\ 2 \\ 3 \end{pmatrix} + \begin{pmatrix} 4 \\ 5 \\ 6 \end{pmatrix} = \begin{pmatrix} 5 \\ 7 \\ 9 \end{pmatrix}\]
but extends beyond just vector addition
Vector Semantics
Most functions in R try to vectorize, but not always possible
[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427
[9] 3.000000 3.162278
[1] 0.7071068 0.0000000 -0.7071068 -1.0000000 -0.7071068 0.0000000 0.7071068
[8] 1.0000000
Vector Index Printing
The [1] you sometimes see is R just letting you know where in a vector you are:
Each line gets a new index:
[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427
[9] 3.000000 3.162278
More complex objects have alternate print styles:
matrix(1:9, nrow=3, ncol=3)
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
Print width is controlled by getOption("width").
Variables
Often, we want to save a several values as a single ‘thing’
This is an assignment operator. Vector formed by 1:5 is labeled x
Naming x later gives us this vector
Can be used in functions
[1] 1.000000 1.414214 1.732051 2.000000 2.236068
Vectors and Variables
If you need to write a vector ‘by hand’, use the c function:
x <- c(1, 4, 9, 16, 25)
x
then
We won’t usually hand-write vectors like this:
- Data comes in vectors (e.g., spreadsheet columns)
Vector Access
Use [] operator to get individual elements of a vector:
Can do more complex indexing, but we won’t use it much:
[1] 1.000000 1.414214 1.732051 2.000000 2.236068
[1] 2.449490 2.645751 2.828427 3.000000 3.162278
[1] 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 3.000000
[9] 3.162278
Weekly Lab
Lab #03
New topics to cover:
- classes of objects
- Using Packages
- Comments
- Writing functions
- Control Flow
Object classes
Everything in R has a type or class:
- Integer, Double (i.e. 64 bit number allowing decimals), Character, …
All vectors elements must have the same class - this is the vector’s class
x <- 1:5
y <- sqrt(x)
class(x)
Using Packages
A package is a set of code (and data) packaged up for distribution and use
R has many helpful packages - these are distributed via CRAN (presently 25055)
Using packages is a two-step process:
- Get package from CRAN to your computer (one time)
- Loading into
R (every time)
Think of regular software: you download MS Office once but need to start it whenever you want to use it
Using Packages
The install.packages function will download and install a package:
install.packages("ggplot2")
If that package uses other packages, R will sort that out automatically
When ready to use a package, use the library() command to ‘start’ it:
Now I have access to everything in that package
Recycling Rules
Alignment by default:
Recycle warning when vectors don’t fit together cleanly:
Warning in x + y: longer object length is not a multiple of shorter object
length
Recycling Warning
Warning in x + y: longer object length is not a multiple of shorter object
length
Not a problem per se, but often a sign that something has gone wrong.
- scalar + vector is usually safe
- 2 vectors of same size is usually safe
- vectors of different size is usually a programming mistake
Warnings vs Errors
- Warnings: heuristics pointing at typical problem
- Code still executed without a problem
- Try to fix these unless you’re certain it’s not a problem
- Errors: code failed to execute
- You have to fix these to run your code
Changing Functions
Most built-in functions can’t / shouldn’t be changed.
Some allow alternate behavior via additional arguments:
log(10) # Default is natural (base e) logarithm
If you want different behavior, write your own function:
cosd <- function(x){
## Cosine in degrees
cos(x * pi / 180)
}
cosd(90)
Always try ?name to see documentation.
Comments
Comments are text inside the code that
RignoresCompare
with