df[rows, cols], subset(), and the trailing comma
◈ 11 cardsorders[rows, cols]: blank rows means all rows, blank cols all columns. orders[orders$amount > 500, ] keeps 5 rows; orders[, c("order_id", "amount")] keeps two columns; subset(orders, amount > 500, select = c(order_id, amount)) does both. Forget the comma and R says "undefined columns selected".
One bracket, two slots
A data frame is indexed with two positions inside the square brackets, separated by a comma: orders[rows, cols]. Leave a slot blank and it means all of them. That comma is the whole lesson — the final's most-asked R syntax item, and the most common error in student scripts.
Worked example — columns, then rows, then both
Columns — a vector of names after the comma:
> orders[, c("order_id", "amount")]
order_id amount
1 1001 420
2 1002 180
3 1003 760
(twelve rows; three shown). This is hiding columns in Sheets. A single name, orders[, "amount"], drops to a vector — like $.
Rows — a condition before the comma, blank after it:
> orders[orders$amount > 500, ]
order_id prov channel amount units order_date
3 1003 ON Store 760 5 2025-01-15
5 1005 AB Online 1320 8 2025-02-03
6 1006 ON Online 510 4 2025-02-10
8 1008 BC Store 640 4 2025-02-27
10 1010 AB Store 980 6 2025-03-11
Five rows, all columns — the AutoFilter on amount > 500, with the row names saying which rows survived. A number works in either slot: orders[2, ] is the second record, orders[2, "amount"] is 180.
Both — subset() reads like a sentence and needs no orders$ prefix inside:
> subset(orders, amount > 500, select = c(order_id, amount))
order_id amount
3 1003 760
5 1005 1320
6 1006 510
8 1008 640
10 1010 980
subset(orders, amount > 500) alone keeps every column; nrow() of it is 5.
The error the comma prevents
> orders[orders$amount > 500]
Error in `[.data.frame`(orders, orders$amount > 500) :
undefined columns selected
With one thing in the bracket, R reads it as a column selection — a data frame is a list of columns, so orders[x] means "these columns". Twelve TRUE/FALSE values against six columns is nonsense, and the message says so in its own words. The fix is the comma: orders[orders$amount > 500, ]. Read "undefined columns selected" as "you forgot the comma" until proven otherwise.
subset() and [ ] disagree about NA
With one amount missing — ona is orders with order 1003's amount set to NA, the frame Lesson 11.1 summarised — the condition amount > 500 is NA for that row. [ ] keeps it, as a row of NAs with the row name NA, in front of the four genuine matches:
> ona[ona$amount > 500, ]
order_id prov channel amount units order_date
NA NA <NA> <NA> NA NA <NA>
5 1005 AB Online 1320 8 2025-02-03
6 1006 ON Online 510 4 2025-02-10
8 1008 BC Store 640 4 2025-02-27
10 1010 AB Store 980 6 2025-03-11
subset() drops it: nrow(ona[ona$amount > 500, ]) is 5, nrow(subset(ona, amount > 500)) is 4. A ghost row of NAs in a filter result is this, and the cure is subset() or which() inside the bracket.
Type the three selections, then read the count
CRISP-DM: filtering rows and choosing columns is data preparation → select data.