Lecture notes. Written from the material of Berkeley Stat 243, licensed CC BY 4.0. These are notes, not a transcript: the material has been reorganised and rewritten. This adaptation carries the same licence, and the original is linked above.
28. R Practice: Structures, Functions, I/O¶
What this covers¶
This chapter works through a self-contained practice worksheet from the STAT 243 discussion sections: building
and subsetting basic R data structures, writing vectorized code instead of loops, using the apply family of
functions, writing small functions with defensive input-checking, and reading and saving data files. It assumes
the reader already knows the material from the course's R bootcamp — vectors, indexing, control flow — since the
worksheet itself points there for anything not covered below.
Vectors, factors, and data frames¶
R's basic containers are vectors (numeric, character, or logical), factors, and data frames. A vector is built
from a fixed-length sequence of values of one type; seq and rep are the two general tools for generating one
without typing out every element, and comparison operators applied to a vector return a logical vector of the
same length, one entry per element. A factor is a vector of category labels together with the fixed list of
levels those labels are allowed to take; factor() converts a character vector into one.
A data frame is a list of column vectors of equal length, given column names, and displayed as a table; unlike a
matrix, its columns need not all be the same type. Building one from several vectors already computed
separately — rather than typing the data in — is the natural way to construct it once each column is a vector of
the right length. A data frame's row labels are a distinct piece of metadata from any of its columns: a vector
can be used as row.names instead of as a column, which changes what df[i, ] returns and whether the values are reachable as a column at all.
Subsetting¶
The subsetting operations for a matrix or data frame are indexing by position, by name, and by a logical vector of the same length as the dimension being indexed — the last of these is what lets you keep only the rows satisfying a condition without writing a loop over rows. A matrix converted from a data frame keeps only the values, dropping the column names and the possibility of mixed column types that a data frame allows.
Vectorized calculation, outer, and the apply family¶
R's arithmetic and elementary functions (exp, sqrt, cos, and so on) are vectorized: applied to a vector,
they return a vector of the function applied elementwise, so an expression like \(e^{2x}x^{\sqrt{x}}\) evaluates at
an entire grid of \(x\) values in one call, with no explicit loop. row(x) and col(x) return matrices, the same
shape as x, holding the row index and the column index of each entry; the difference row(x) - col(x) is
therefore the distance of each entry from the diagonal, which is what a banded or distance-from-diagonal pattern
in a matrix is built from. outer(a, b, FUN) builds a matrix whose \((i,j)\) entry is FUN(a[i], b[j]), and is
the direct way to build a matrix from an addition (or other two-argument) table over two index vectors, including
one that wraps around using the remainder after division.
apply(X, margin, FUN) applies FUN to each row (margin = 1) or column (margin = 2) of a matrix and
collects the results; because it always calls FUN once per row or column, a purpose-built summary such as
rowSums is faster on a large matrix even though the two compute the same numbers. sapply and lapply do the
analogous thing over the elements of a list: lapply always returns a list, one element per input element,
while sapply tries to simplify that list into a vector or matrix when the results are all the same length and
type, and falls back to a list when they are not — which is why the two can return objects of different classes
applied to the same object, depending on what is inside it.
Writing functions¶
stopifnot(condition) stops the function with an error if condition is not TRUE, and is the standard way to
check that an argument satisfies a type or shape assumption before the rest of the function body relies on it.
A function argument can be given a default value in its definition (na.rm = FALSE), so that a caller only needs
to supply it to change the default behaviour, such as removing NAs before a computation.
sample() draws a random sample, and, used with replace = TRUE, is the standard way to simulate coin flips or
any other draw with replacement. set.seed() fixes the state of R's random number generator, so that a call to
sample (or any other random-number function) after it is reproducible; calling it once before a sequence of
random draws you want to be able to reproduce is the usual pattern, whereas calling it inside a function that is
meant to be called many times — as in generating many independent simulation replicates — would reset the
generator to the same state on every call and make every replicate identical. replicate(n, expr) evaluates
expr n times and collects the results, which is the tool for running a stochastic function many times to
build up a distribution of its output, such as a histogram of simulated outcomes.
A function can branch on the value of a character argument (its operation) using conditional logic, with a
final branch that issues a warning when the argument does not match any of the expected cases. Finally,
cumsum(x) is R's built-in cumulative sum; writing the same computation explicitly with a for loop that keeps
a running total is a way to check, by comparison with the built-in, that the loop's logic is right — the general
technique for reimplementing a vectorized built-in in order to understand what it does.
Loading and saving data¶
Reading a file into R takes its path, not its name alone: the exercises deliberately keep the data file where it
is and require either a full path or a path relative to the current location, rather than copying the file
alongside the script or changing the working directory to match it. Stata .dta files, CSV files, and
whitespace-delimited text files each need their own reading function; once loaded, class, str, length,
dim, and sapply (applied over the columns) are the standard tools for finding out what kind of object came
back and what is in it.
Saving part or all of the current workspace to an .RData file with save, and reading it back with load in a
fresh R session, is how objects are carried between sessions without recomputing them; ls() lists what is
currently in the workspace, which is how you check that a fresh session starts empty and that load puts back
what save wrote out.
Exercises¶
(From the STAT 243 "R Practice" worksheet; assumes R bootcamp modules 1-4 and 6.)
Creating vectors and a data frame¶
-
Create the following vectors: a. \(1, 2, 3, \dots, 49, 50\) b. a logical vector that is
TRUEexactly when the corresponding element of (a) is even c. \(50, 49, \dots, 3, 2, 1\) d. \(1, 2, 3, \dots, 49, 50, 49, \dots, 3, 2, 1\) e. \(-10, -9, -8, \dots, 8, 9, 10\) f. \(3, 6, 9, \dots, 45, 48\) g."3", "6", "9", ..., "45", "48"(build it from the vector in (f)) h."a", "a", "a", "a", "b", "b", "b", "c", "c", "d"(usingrep) i. the character vector from (h), turned into a factor j. 200 numbers between \(-1\) and \(1\) inclusive (usingseq) -
Build a data frame
df1: a. a vector \(1, 2, \dots, 50\) calledxb.y, the cosine ofxc.z, the tangent ofyd.w, the elementwise product ofyandze. a logical vectorfthat isTRUEexactly whenxis between 10 and 29 inclusive f. a data framedf1with columnsx, y, z, w, fin that order g. how would you change the column names ofdf1to uppercase? h. what would you have done differently to usexas row names instead of as a column — and would you still have needed to constructxfirst?
Subsetting data structures¶
- a. a matrix
m1of only the numeric columns ofdf1b. a matrixm2of only the rows ofdf1wherefisTRUEc. a data framedf2of only the rows wherezis non-negative, with all columns exceptzd. a data framedf3that isdf1without its 3rd and 17th rows e. a data framedf4of only the even-numbered rows ofdf1
Vectorized calculations¶
-
A vector of values \(e^{2x}x^{\sqrt{x}}\) for \(x = 1, 1.1, 1.2, \dots, 2.9, 3.0\).
-
a. a \(5\times5\) matrix of zeros called
xb. inspect whatrow(x)andcol(x)return c. usingrow(x)andcol(x), build $\(\begin{pmatrix} 0 & 1 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 0 \\ 0 & 1 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 & 1 \\ 0 & 0 & 0 & 1 & 0 \end{pmatrix}\)$ d. usingrow(x)andcol(x), build $\(\begin{pmatrix} 0 & 1 & 2 & 3 & 4 \\ 1 & 0 & 1 & 2 & 3 \\ 2 & 1 & 0 & 1 & 2 \\ 3 & 2 & 1 & 0 & 1 \\ 4 & 3 & 2 & 1 & 0 \end{pmatrix}\)$ -
Using
outer(look at itsFUNargument): a. $\(\begin{pmatrix} 0 & 1 & 2 & 3 & 4 \\ 1 & 2 & 3 & 4 & 5 \\ 2 & 3 & 4 & 5 & 6 \\ 3 & 4 & 5 & 6 & 7 \\ 4 & 5 & 6 & 7 & 8 \end{pmatrix}\)$ b. modify (a) to get $\(\begin{pmatrix} 0 & 1 & 2 & 3 & 4 \\ 1 & 2 & 3 & 4 & 0 \\ 2 & 3 & 4 & 0 & 1 \\ 3 & 4 & 0 & 1 & 2 \\ 4 & 0 & 1 & 2 & 3 \end{pmatrix}\)$
apply, sapply, lapply¶
-
Normalize rows and columns: a. a \(5\times6\) matrix
m1of numbers drawn uniformly from \((1, 100)\) b. a matrixm2, usingapplywith a small function you write, that normalizes the rows ofm1to sum to 1; check the dimensions ofm2againstm1— are they the same, and why? c. verify withapplythat the rows ofm2sum to 1, then verify again withrowSums— why preferrowSumstoapplyhere? d. repeat (b)-(c), normalizing the columns instead -
A linear model: a. a vector
xof \(1, 1.1, 1.2, \dots, 9.9, 10.0\) b. a vectoryequal to twicexplus standard Gaussian noise c. a scatterplot ofyagainstxd. anlmobjectmy_lmregressingyonxe. the classes of the elements ofmy_lm, usinglapplyf. the classes of the elements ofmy_lm, usingsapplyg. do the two differ? Why, or why not — and when in general would they?
Functions¶
-
A function returning the sum of absolute deviations from the median of a numeric vector
x: a. check thatxis numeric (usingstopifnot) b. add anna.rmargument, defaulting toFALSE, that removesNAs from the computation whenTRUE -
Simulating a coin toss: a. sample, with replacement, a vector
xof 100 0s and 1s, then do it again and call ity— would it make sense to callset.seedbefore doing this, and why? b. writesumHeads(n)returning the number of heads (coded as 1) innsimulated flips — would it make sense to callset.seedinside the body of this function, and why or why not? c. build a vectorsums_vecof 10,000 calls tosumHeads(200)(usingreplicate) d. plot a histogram ofsums_vec -
A function of two numeric vectors
x,y, and an argumentoperationdefaulting to"add": a. ifoperationis"add", returnx + yb. if"subtract", returnx - yc. if"multiply", returnx * yd. if"divide", returnx / ye. otherwise, issue a warning that the operation is unknown -
A function returning the cumulative sum of a vector
x, implemented with aforloop — check the result against R's built-incumsum.
Loading and saving data¶
-
Load the earnings data (e.g.
sections/01/data/heights.dta, from the course's Github repository) without copying it locally or changing the working directory; call the resultearnings. a. what doesclass(earnings)return? b. what doesstr(earnings)return? c. what doeslength(earnings)return? d. what doesdim(earnings)return? e. usesapplyto find the class of each column ofearningsf. usesapplyto runsummaryon just the height-related columns -
Load a CSV file (e.g.
sections/01/data/cpds.csv) and a whitespace-delimited text file (e.g.sections/01/data/stateIncome.txt). -
Saving R objects: a. use
ls()to see what is in your workspace b. save some of those objects to an.RDatafile in the directory above your current one c. open a fresh R session d. confirm the new session's workspace is empty e. load the.RDatafile you saved f. usels(),class(),str(),names(), and anything else useful, to examine what was loaded
Sources¶
01-creating-datastructures.md(STAT 243 "R Practice" worksheet, sections "Creating datastructures" through "Using apply, sapply, and lapply"), converted fromsections/RPractice/R-practice.pdfin the berkeley-stat243/stat243-fall-2021 repository.02-functions.md(same worksheet, "Functions" section).03-loading-and-saving-data.md(same worksheet, "Loading (and saving) data" section).- The worksheet itself refers to, but does not supply, "modules 1-4 and 6 of the R bootcamp" and the data files
sections/01/data/heights.dta,cpds.csv, andstateIncome.txtfrom the course's 2020 Github repository; none of these are among the material given here. - All three converted files note that the source PDF had no extractable text layer and was reconstructed by a model, so the exact original wording (and the matrices transcribed above) are marked unverified against the PDF.
← 27. Python Fundamentals for R Users · Contents · 29. Installing R & RStudio →