R Basics
vectors · factors · data frames · lists · control flow · functions · apply family
Sheet 1 of 2
R 4.x
Beginner
Printable
R Data Types & Structures at a Glance
| Structure | Dimensions | Homogeneous? | Mutable? | Create with | Use when… |
|---|---|---|---|---|---|
| vector | 1D | Yes | Yes | c(1, 2, 3) | Single sequence of same-type values |
| factor | 1D | Yes | Yes | factor(c("a","b")) | Categorical/ordinal data |
| matrix | 2D | Yes | Yes | matrix(1:6, 2, 3) | Numeric grid, linear algebra |
| array | nD | Yes | Yes | array(1:24, c(2,3,4)) | Multi-dimensional tables |
| list | 1D | No | Yes | list(a=1, b="hi") | Mixed types, nested structures |
| data.frame | 2D | No | Yes | data.frame(x=1:3, y=c("a","b","c")) | Tabular data (like a spreadsheet) |
| tibble | 2D | No | Yes | tibble::tibble() | Modern data frame (tidyverse) |
R is 1-indexed — the first element is at index [1], not [0] like Python or JavaScript. Assignment uses <- (preferred) or =.
Vectors
Create & Access
nums <- c(10, 20, 30, 40) words<- c("r", "is", "great") bools<- c(TRUE, FALSE, TRUE) seq1 <- 1:5 # 1 2 3 4 5 seq2 <- seq(1,10,by=2) # 1 3 5 7 9 rep1 <- rep(0, times=4) # 0 0 0 0 nums[1] # 10 (1-indexed!) nums[2:4] # 20 30 40 nums[c(1,3)] # 10 30 nums[-1] # 20 30 40 (drop 1st)
Vector Operations (vectorized!)
x <- c(1,2,3); y <- c(10,20,30) x + y # 11 22 33 x * 2 # 2 4 6 (scalar) x ^ 2 # 1 4 9 x > 1 # F T T (logical) sum(x) # 6 mean(x) # 2 length(x) # 3 sort(x, dec=TRUE) # 3 2 1 rev(x) # 3 2 1 which(x > 1) # 2 3 (indices)
Vectorization: R operations apply element-wise automatically — no loops needed for basic math. This is R's superpower for data work.
Factors
Create & Inspect
sizes <- factor(c("S","M","L","M","S")) levels(sizes) # "L" "M" "S" (sorted) nlevels(sizes) # 3 table(sizes) # L=1 M=2 S=2 # Ordered factor (e.g. ratings) rating <- factor( c("low","high","med"), levels = c("low","med","high"), ordered = TRUE ) rating[1] < rating[2] # TRUE
Modify Factors
# Add a new level levels(sizes) <- c(levels(sizes),"XL") # Drop unused levels sizes2 <- droplevels(sizes) # Convert to integer codes as.integer(sizes) # 3 2 1 2 3 # Convert back to character as.character(sizes) # "S" "M" "L"...
Use factors for categorical columns in data frames — they save memory and work correctly in models and ggplot2 colour/shape mappings.
Lists
Create & Access
person <- list( name = "Alice", age = 25, scores = c(90,85,92), active = TRUE ) # Three access styles person[["name"]] # "Alice" — value person$name # "Alice" — shortcut person["name"] # list w/ 1 element person[[3]] # c(90,85,92) — by pos
Modify & Inspect
# Add / update elements person$email <- "a@b.com" person[["age"]] <- 26 # Remove an element person$active <- NULL # Inspect structure length(person) # 4 names(person) # "name" "age"... str(person) # compact overview is.list(person) # TRUE
Nested Lists & lapply
# Nested structure users <- list( list(name="Alice", age=25), list(name="Bob", age=30) ) users[[1]]$name # "Alice" # Apply a function over a list lapply(users, function(u) u$age) # returns list: [[1]] 25 [[2]] 30 sapply(users, function(u) u$age) # simplifies to vector: 25 30
[[ ]] vs [ ]: list[["key"]] extracts the value. list["key"] extracts a sub-list. Always use [[]] or $ when you want the actual element.
Data Frames
Create & Inspect
df <- data.frame( name = c("Alice","Bob","Carol"), score = c(92, 78, 85), pass = c(TRUE,TRUE,TRUE) ) nrow(df) # 3 rows ncol(df) # 3 cols dim(df) # 3 3 names(df) # column names str(df) # structure overview summary(df) # stats per column head(df, 2) # first 2 rows
Access & Filter
# Access columns df$name # "Alice" "Bob"... df[["score"]] # 92 78 85 df[, "score"] # same # Access rows — df[row, col] df[1, ] # first row df[1, "name"] # "Alice" # Filter rows by condition df[df$score > 80, ] # Alice (92) and Carol (85) # subset() convenience subset(df, score > 80, c(name,score))
Modify & Add Columns
# Add a new column df$grade <- ifelse(df$score>=90,"A","B") # Update existing values df$score[2] <- 80 # Add rows new_row <- data.frame(name="Dan", score=70, pass=FALSE) df <- rbind(df, new_row) # Merge two data frames merge(df1, df2, by = "id")
stringsAsFactors: In R < 4.0, strings in data frames became factors by default. Since R 4.0+ this is FALSE by default. Add stringsAsFactors = TRUE explicitly if needed.
Control Flow
if / else if / else
x <- 85 if (x >= 90) { cat("A\n") } else if (x >= 80) { cat("B\n") # prints "B" } else { cat("C\n") } # Inline ifelse() — vectorized ifelse(c(80,95,70) >= 80, "pass", "fail") # "pass" "pass" "fail"
for / while / repeat
# for loop — over a vector for (i in 1:3) { cat(i, "\n") } # for loop — over list / vector for (fruit in c("apple","mango")) print(fruit) # while loop n <- 1 while (n <= 3) { cat(n, "\n") n <- n + 1 } # break and next (like continue) for (i in 1:5) { if (i == 3) next # skip 3 if (i == 5) break # stop at 5 }
Avoid growing objects in loops! Pre-allocate with result <- vector("numeric", n) before looping, then fill by index. Growing with c() inside a loop is very slow in R.
Functions
Define & Call
# Basic function greet <- function(name, greeting = "Hello") { paste(greeting, name) # last expr returned } greet("Alice") # "Hello Alice" greet("Bob", "Hi") # "Hi Bob" # Explicit return divide <- function(a, b) { if (b == 0) return(NA) a / b }
... (dots) & Multiple Returns
# Pass-through extra args with ... my_mean <- function(x, ...) { mean(x, ...) } my_mean(c(1,NA,3), na.rm=TRUE) # 2 # Return multiple values via list stats <- function(x) { list(mean=mean(x), sd=sd(x)) } res <- stats(1:10) res$mean # 5.5 res$sd # 3.02...
Last expression wins: R functions automatically return the last evaluated expression — no return() needed. Use return() only for early exits.
Apply Family — Loop Alternatives
| Function | Input | Output | Use when… | Example |
|---|---|---|---|---|
| apply() | matrix/array | vector/list | Row or column summaries | apply(m, 1, sum) |
| lapply() | list/vector | list | Always returns a list | lapply(lst, mean) |
| sapply() | list/vector | simplified | Simplify to vector/matrix | sapply(lst, length) |
| vapply() | list/vector | typed vector | Safer sapply with type check | vapply(lst,mean,numeric(1)) |
| tapply() | vector+groups | array | Group-by summaries | tapply(score, group, mean) |
| mapply() | multiple vectors | list/vector | Apply over multiple args | mapply(rep, 1:3, 3:1) |
apply() — matrix rows/cols
m <- matrix(1:9, nrow=3) # [,1] [,2] [,3] # [1,] 1 4 7 # [2,] 2 5 8 # [3,] 3 6 9 apply(m, 1, sum) # row sums: 12 15 18 apply(m, 2, sum) # col sums: 6 15 24
sapply() / lapply()
nums <- list(a=1:5, b=6:10) lapply(nums, mean) # list: $a 3 $b 8 sapply(nums, mean) # a b (named vector) # 3 8 # with anonymous function sapply(1:4, function(x) x^2) # 1 4 9 16
tapply() — group summaries
scores <- c(80,90,70,95,60,88) groups <- c("A","B","A","B","A","B") tapply(scores, groups, mean) # A B # 70.0 91.0 # like GROUP BY in SQL!
Essential Built-in Functions
| Function | What it does | Example |
|---|---|---|
| print() / cat() | Print to console | cat("Hi\n") |
| paste() / paste0() | Concatenate strings | paste("a","b",sep="-") |
| nchar() | String length | nchar("hello") # 5 |
| toupper/tolower() | Change string case | toupper("hi") # "HI" |
| grep() / grepl() | Pattern match (returns idx / logical) | grepl("^A", names) |
| gsub() / sub() | Replace all / first match | gsub("a","@","banana") |
| is.na() / na.omit() | Detect / remove NAs | na.omit(c(1,NA,3)) |
| unique() | Remove duplicates | unique(c(1,2,2,3)) |
| table() | Frequency count | table(c("a","b","a")) |
| order() / rank() | Indices for sorting / ranks | x[order(x)] |
| Sys.time() | Current timestamp | t0 <- Sys.time() |
| tryCatch() | Error handling | tryCatch(log(-1), warning=...) |
Operators Quick Reference
| Op | Meaning | Example |
|---|---|---|
| + - * / | Arithmetic | 3 + 2 # 5 |
| ^ or ** | Exponent | 2^8 # 256 |
| %% | Modulo | 7 %% 3 # 1 |
| %/% | Integer divide | 7 %/% 3 # 2 |
| %in% | Element in set | 3 %in% 1:5 # T |
| %*% | Matrix multiply | A %*% B |
| Op | Meaning | Note |
|---|---|---|
| <- | Assign (preferred) | Alt+- shortcut in RStudio |
| == != | Equal / not equal | Returns logical vector |
| & | | AND / OR (vectorized) | Element-wise |
| && || | AND / OR (scalar) | Use in if() conditions |
| ! | NOT | !TRUE → FALSE |
| |> | Native pipe (R 4.1+) | x |> mean() |
Pipe Operator |> (R 4.1+)
# Without pipe round(mean(sqrt(c(1,4,9,16))), 2) # With pipe — reads left to right c(1,4,9,16) |> sqrt() |> mean() |> round(2) # same result — much more readable!
R Basics Mastery Checklist
| Vectors & Factors | Key point |
|---|---|
| Create a vector | c(1, 2, 3) |
| Index (1-based!) | x[1] x[2:4] |
| Vectorized math | x * 2 x + y |
| Make a factor | factor(vec) |
| Ordered factor | ordered=TRUE |
| Lists & Data Frames | Key point |
|---|---|
| Access list element | lst$name lst[["k"]] |
| Filter data frame rows | df[df$col > 5, ] |
| Add column to df | df$new <- values |
| Inspect structure | str(df) summary(df) |
| Merge data frames | merge(df1, df2, by=) |
| Functions & Apply | Key point |
|---|---|
| Define a function | function(x) { ... } |
| Default argument | function(x, n=10) |
| Apply over list | sapply(lst, fn) |
| Group summary | tapply(x, grp, mean) |
| Pipe result forward | x |> fn() |
Next up → Sheet 2: R tidyverse ·
dplyr · ggplot2 · tidyr · readr · purrr · filter · mutate · group_by · summarise · pivot