subset
subset(x, subset, select, drop = FALSE, ...) subset() pulls rows (or elements) that satisfy a logical condition out of a vector, matrix, or data frame. It is a thin wrapper around base R’s [ operator with three ergonomic twists: the subset expression is evaluated inside the data frame, so column names can be written unquoted; a select argument lets you pick columns at the same time; and it is an S3 generic, so vectors, matrices, and data frames share a consistent interface.
subset lives in the base package, so it is always available. No library() call is required.
Signatures
subset is a generic. The three signatures you will actually call are:
subset(x, ...)
## Default S3 method:
subset(x, subset, ...)
## S3 method for class 'matrix' or 'data.frame':
subset(x, subset, select, drop = FALSE, ...)
The data frame and matrix methods share arguments because both ultimately dispatch to `[i, j]` indexing.
Arguments
| Argument | Type | Default | Description |
|---|---|---|---|
x | vector, matrix, or data frame | required | Object to subset. |
subset | logical expression | missing | Condition evaluated in x (data frame) or the calling environment (vectors). Rows or elements where the result is TRUE are kept. |
select | expression | missing | For matrices and data frames: column names, indices, or -col to drop. |
drop | logical | FALSE | Forwarded to [. Set to TRUE on a matrix to collapse a single-column result to a vector. |
... | further arguments | Forwarded to methods. |
Examples
The canonical examples from ?subset use the built-in airquality data frame:
subset(airquality, Temp > 80, select = c(Ozone, Temp))
subset(airquality, Day == 1, select = -Temp)
subset(airquality, select = Ozone:Wind)
A more realistic walkthrough with a small data frame gives you a feel for the typical call shape. You build df with three columns (id, group, and score), then filter rows and select columns in one go. Notice how column names appear unquoted inside the subset and select arguments, which is the non-standard evaluation at work:
df <- data.frame(
id = 1:6,
group = c("a", "a", "b", "b", "c", "c"),
score = c(10, 14, 7, 19, 5, 12)
)
# Keep rows where score > 10 and only the group and score columns
subset(df, score > 10, select = c(group, score))
#> group score
#> 2 a 14
#> 4 b 19
#> 6 c 12
# Drop the id column
subset(df, score > 5, select = -id)
subset also works on plain vectors through the default method, which has no select argument. The call shape is symmetrical with the data frame form: you pass the vector and a logical condition, and subset returns the elements where the condition is TRUE. The subset expression is evaluated in the calling environment here rather than inside the vector, which is a subtle difference from the data frame method:
v <- c(2, 7, 3, 9, 1)
subset(v, v > 4)
# [1] 7 9
And on matrices, where drop = TRUE collapses a single-column result to a vector. The matrix method accepts the same subset and select arguments as the data frame form. The example below sums each row, keeps only the rows where the sum exceeds 15, picks the a column, and asks for a vector back via drop = TRUE:
m <- matrix(1:12, nrow = 3, dimnames = list(NULL, c("a", "b", "c", "d")))
subset(m, rowSums(m) > 15, select = "a", drop = TRUE)
# [1] 1 2 3
NA handling
subset drops rows whose condition evaluates to NA. The official help says the subset argument is “logical expression indicating elements or rows to keep: missing values are taken as false.” That is one of the nicer properties of subset compared to raw [:
df2 <- data.frame(x = c(1, NA, 3, 4), y = 1:4)
subset(df2, x > 2)
#> x y
#> 3 3 3
#> 4 4 4
df2[df2$x > 2, ]
#> x y
#> NA NA NA
#> 3 3 3
#> 4 4 4
If you want NA-aware filtering from [, pair it with which(): df2[which(df2$x > 2), ].
Other gotchas
A single-column result is still a data frame, not a vector. subset(df, select = "x") returns a one-column data frame because drop = FALSE is the default. To collapse it to a vector, use subset(df, select = "x")$x, or pass drop = TRUE to [ directly.
Because the subset expression is evaluated inside the data frame, a variable in the calling environment with the same name as a column is shadowed by the column. That is normally what you want. Watch for typos like subset(airquality, temp > 80): R returns nothing because temp (lowercase) is not a column, and there is no error.
When not to use it
The ?subset help is explicit: “This is a convenience function intended for use interactively. For programming it is better to use the standard subsetting functions like [, and in particular the non-standard evaluation of argument subset can have unanticipated consequences.”
In practice, do not call subset() from inside your own functions or packages. The non-standard evaluation makes the call shape dependent on the calling environment, which is hard to reason about programmatically. Reach for `[i, j]` indexing (with which() for NA-free filtering) when writing reusable code, and for dplyr::filter() plus dplyr::select() when working in a tidyverse pipeline.
See also
base::filter()for time-series-style row filtering in base R.dplyr::filter()for the tidyverse equivalent.which()for picking the indices you would otherwise feed into[.