Chapter 2 Vectors and Data Structures
Choosing the correct data structure makes R code easier to reason about. The core structures differ by dimensionality and whether they permit mixed types.
| Structure | Dimensions | Value types |
|---|---|---|
| Atomic vector | 1 | One underlying type |
| Matrix | 2 | One underlying type |
| List | 1 | Mixed objects allowed |
| Data frame | 2 | Each column may have a different type |
2.1 Creating sequences
Use seq() when the start, end, or increment should be explicit. Use
seq_along(x) when indices must follow the length of another object.
seq(from = 2, to = 10, by = 2)
#> [1] 2 4 6 8 10
seq_len(5)
#> [1] 1 2 3 4 5
labels <- c("A", "B", "C")
seq_along(labels)
#> [1] 1 2 3seq_along() is safer than 1:length(x) because it correctly returns an empty
integer vector when x has length zero.
2.2 Indexing vectors
R supports position, logical, and name-based indexing.
sales <- c(north = 120, south = 95, west = 140, east = 110)
sales[c(1, 3)]
#> north west
#> 120 140
sales[sales >= 120]
#> north west
#> 120 140
sales[c("north", "east")]
#> north east
#> 120 110Negative positions exclude elements. Do not mix positive and negative positions in the same index.
2.3 Matrices
A matrix is an atomic vector with a two-dimensional dim attribute. Because a
matrix has one underlying type, inserting character data coerces numeric values
to character.
measurements <- matrix(
1:12,
nrow = 3,
byrow = TRUE,
dimnames = list(NULL, c("a", "b", "c", "d"))
)
measurements
#> a b c d
#> [1,] 1 2 3 4
#> [2,] 5 6 7 8
#> [3,] 9 10 11 12
measurements[, c("a", "c"), drop = FALSE]
#> a c
#> [1,] 1 3
#> [2,] 5 7
#> [3,] 9 11drop = FALSE preserves the matrix structure when a selection contains only
one row or column.
2.4 Lists
A list can contain objects of different types and sizes.
model_summary <- list(
model = "linear regression",
coefficients = c(intercept = 1.2, slope = 0.8),
converged = TRUE
)
model_summary[["coefficients"]]
#> intercept slope
#> 1.2 0.8
model_summary["coefficients"]
#> $coefficients
#> intercept slope
#> 1.2 0.8[[ extracts the object stored in one list element. [ returns a sub-list.
That distinction is a frequent interview question and a common source of
unexpected results.
2.5 Data frames
A data frame is a list of equal-length columns. Columns may use different types, but each column must have a consistent underlying type.
customers <- data.frame(
customer_id = 101:104,
segment = c("new", "returning", "new", "returning"),
revenue = c(35.0, 82.5, 41.0, NA_real_)
)
str(customers)
#> 'data.frame': 4 obs. of 3 variables:
#> $ customer_id: int 101 102 103 104
#> $ segment : chr "new" "returning" "new" "returning"
#> $ revenue : num 35 82.5 41 NA
customers[customers$segment == "new", c("customer_id", "revenue")]
#> customer_id revenue
#> 1 101 35
#> 3 103 41Use NA_real_, NA_integer_, and related typed missing values when the desired
column type should be explicit.
2.6 Factors
A factor stores categorical values as integer codes with associated labels. Use an ordered factor only when the levels have a meaningful ranking.
priority <- factor(
c("medium", "high", "low", "medium"),
levels = c("low", "medium", "high"),
ordered = TRUE
)
priority
#> [1] medium high low medium
#> Levels: low < medium < high
priority > "low"
#> [1] TRUE TRUE FALSE TRUENever rely on alphabetical ordering when the domain defines the order.
2.7 Combining data
cbind() combines objects by columns and rbind() combines them by rows. R
may recycle shorter vectors, sometimes with only a warning, so validate lengths
before combining independent inputs.
customer_id <- 1:3
revenue <- c(25, 40, 30)
data.frame(customer_id, revenue)
#> customer_id revenue
#> 1 1 25
#> 2 2 40
#> 3 3 30For production analysis, prefer joining tables by explicit keys rather than assuming two independently created objects have the same row order.
2.8 Inspecting unfamiliar objects
Use a small set of inspection functions before manipulating unfamiliar data.
dim(customers)
#> [1] 4 3
names(customers)
#> [1] "customer_id" "segment" "revenue"
head(customers, 2)
#> customer_id segment revenue
#> 1 101 new 35.0
#> 2 102 returning 82.5
summary(customers)
#> customer_id segment revenue
#> Min. :101.0 Length:4 Min. :35.00
#> 1st Qu.:101.8 Class :character 1st Qu.:38.00
#> Median :102.5 Mode :character Median :41.00
#> Mean :102.5 Mean :52.83
#> 3rd Qu.:103.2 3rd Qu.:61.75
#> Max. :104.0 Max. :82.50
#> NA's :1str() is usually the most concise first check because it exposes dimensions,
column types, and representative values.
2.9 Interview checkpoints
- Compare an atomic vector, matrix, list, and data frame.
- Explain the difference between
[and[[for lists. - Explain when matrix subsetting drops dimensions and how to prevent it.
- Describe how factors store values and why explicit level order matters.
- Explain why joining by keys is safer than relying on row order.