Skip to main content

R Basics

This guide covers the base R operations needed to inspect and transform small in-memory datasets. It assumes R is already installed. Run expressions in the console to inspect values, and save a sequence of expressions in a script to repeat the work. Statistical inference and object-oriented systems are outside this introduction. The official language manual is An Introduction to R.

Try the base-R vector examples in the webR browser console. Compare 1:0 with seq_along(numeric(0)), then test the recycling examples. This gives a small scratch session for language behavior before moving file and package work into your own R project.

Basic Syntax​

Elements of a Program​

  • Data Structures: Information carriers (vectors, matrices, data frames, lists)
  • Algorithms: Steps to complete tasks

Expressions and assignment​

x <- c(2, 4, 6)
x + 1 # 3 5 7
sum(x) # 12
identical(1, 1L) # FALSE

<- binds a name to a value; == compares values. R is case-sensitive. # starts a comment. 1 normally has type double, while 1L has type integer; class() describes how an object is treated by methods, while typeof() describes its internal type. Combining atomic values coerces them to a common type: c(1, "2") is character, not a mixed numeric/text vector.

Data Structures​

Basic Data Types​

  1. Numeric: Includes integers and floating-point numbers
  2. Character: Represents textual information
  3. Logical: Boolean values (TRUE, FALSE)

Vectors​

An atomic vector contains elements of the same type; an ordinary scalar is a vector of length 1. Lists are also vectors in R terminology, but can contain different types.

Creating Numeric Vectors​

c(1.70, 1.72, 1.80, 1.66, 1.65, 1.88)

Checking Data Types​

typeof(3.14)
class(3.14)

Operations on Vectors​

heights <- c(1.70, 1.72, 1.80, 1.66, 1.65, 1.88)
mean(heights)
sd(heights)

Indexing, recycling, and missing values​

Subsetting is one-based. Positive indexes select, negative indexes exclude, and zero selects nothing; do not mix positive and negative indexes except with zero.

x <- c(10, 20, NA_real_, 40)
x[c(1, 4)] # 10 40
x[-2] # 10 NA 40
x[!is.na(x) & x > 15] # 20 40
mean(x) # NA
mean(x, na.rm = TRUE) # 23.33333...
stopifnot(isTRUE(all.equal(mean(x, na.rm = TRUE), 70 / 3)))
c(1, 2, 3, 4) + c(10, 20) # 11 22 13 24

NA means missing, not zero; test it with is.na(), not x == NA. NULL represents absence and has length zero; NaN is an undefined numeric result, and Inf is infinity. is.na() also detects NaN; is.finite() excludes missing and infinite numbers. Removing missing values changes the population used by a summary, so decide whether omission fits the analysis.

Arithmetic recycles shorter vectors. A non-multiple length warns, but an exact multiple can recycle silently even when unintended. For pairwise measurements, check equal lengths unless broadcasting a scalar is deliberate.

Character Vectors​

c("Male", "Female", "Female", "Male")

Factors​

Factors are used to represent categorical data with levels.

sex <- factor(c("Male", "Female", "Female", "Male"))
levels(sex)

Logical Vectors​

heights > 1.7
heights[heights > 1.7]

Arrays and Matrices​

2D and higher-dimensional arrays.

matrix(1:12, nrow = 4, ncol = 3)

Data Frames​

Data frames can hold different types of data.

df <- data.frame(
sex = c("F", "M", "M", "F"),
age = c(17, 29, 20, 33),
heights = c(1.66, 1.84, 1.83, 1.56)
)
str(df)

Lists​

Lists can hold different types of elements.

l <- list(
sex = c("F", "M"),
age = c(17, 29, 20),
heights = c(1.66, 1.84, 1.83, 1.56)
)
l$sex

Extract a container or its contents​

is.list(l["sex"]) # TRUE: a one-element list
is.character(l[["sex"]]) # TRUE: its character vector
is.data.frame(df[, "age", drop = FALSE]) # TRUE
is.numeric(df[["age"]]) # TRUE

[ selects a subset, whereas [[ extracts one element; for a data frame, that element is usually a column. $sex selects a named element. Data-frame columns have the same row count, unlike arbitrary list elements. Use drop = FALSE when selecting rows or columns must preserve a two-dimensional table. A matrix is filled by columns by default, so the first column of matrix(1:12, nrow = 4) is 1, 2, 3, 4; specify byrow = TRUE to fill by rows.

Control Structures​

Conditional Statements​

If-else​

age <- 16
if (age >= 18) {
message("Meets the example age threshold")
} else {
message("Below the example age threshold")
}

Switch​

ch <- "b"
switch(EXPR = ch, a = 1, b = 2:3)

if requires one non-missing logical result, as specified by R control flow. It cannot directly consume a vector such as heights > 1.7. Use a logical subset for elementwise selection, or any()/all() for a single aggregate decision, with an explicit missing-value policy. & and | work elementwise; && and || short-circuit scalar conditions. With character selection, switch() matches a named alternative; an unmatched name without a default returns NULL.

Looping Structures​

For Loop​

for (i in 1:10) {
print(i)
}

While Loop​

v <- 10
while(v > 2) {
print(v)
v <- v - 1.1
}

Repeat Loop​

i <- 1
repeat {
print(i)
i <- i * 2
if (i > 100) break
}

For an index loop over a possibly empty object, use seq_along(x), not 1:length(x): when length is zero, 1:0 is the two-element sequence 1, 0. break leaves the innermost loop; next skips to its next iteration. A while or repeat loop needs progress toward its stopping condition.

Functions and Functional Programming​

Creating Functions​

customMean <- function(x) {
s <- i <- 0
for (j in x) {
s <- s + j
i <- i + 1
}
return(s / i)
}

This teaching version assumes a numeric vector and sums each element once. It returns NaN for an empty vector (0 / 0) and propagates NA; it does not implement mean()'s options or numerical care. Prefer mean() for analysis. A function returns its last evaluated expression if return() is omitted; printing a value is not the same contract as returning it.

Scope​

Ordinary assignments inside a function create local bindings. A name not found locally is looked up through the environments enclosing the function definition: R uses lexical scope. In this example, a comes from that enclosing environment, so Sum(10) returns 13; changing that a before another call changes the result.

a <- 3
Sum <- function(b) {
a + b
}
Sum(10)

Passing Functions​

Functions can be passed as arguments.

f <- function(x, fun) {
fun(x)
}
f(1:10, mean)

Packages​

Installing Packages​

install.packages("ggplot2")
BiocManager::install("maftools")
remotes::install_github("tidyverse/ggplot2")

Installing downloads package code into a library and is separate from attaching it in a session. The Bioconductor and GitHub examples require BiocManager and remotes to be installed first. These are alternative sources, not three installation steps for every project. Use the project's recorded dependencies and trusted package sources rather than running unfamiliar installation commands.

Loading Packages​

library(ggplot2)

Common Problems and Solutions​

Complex Numbers​

Complex numbers can be represented using i for the imaginary part.

1 + 2i

Differences between = and <-​

Use <- for an ordinary assignment and = to name an argument in a call. In customMean(x = 1:100), x names the function parameter; it does not assign a caller variable. The following deliberately different example assigns the caller’s x while passing its value, a side effect usually best written as a separate statement.

x <- NULL
customMean(x <- 1:100)
x

Using ::​

package::function() calls an exported function without attaching the package:

stats::median(1:5)

The triple-colon form, package:::function(), reaches unexported internals. Those functions can change without notice, so application code should not depend on them.

Adding factor levels​

The following rebuilds a factor after extending its character values; it adds categories rather than reordering an existing factor. To control order, supply levels = ... explicitly. Assigning an unknown label directly into a factor produces NA with a warning unless the level is added first. as.numeric(factor) returns internal level codes, not numeric-looking labels; convert via character when those labels represent numbers.

sex <- factor(c(as.character(sex), "M", "M"))
Explore connectionsOpen network