Written and maintained by CASRAI Editorial Board
Last updated
Short answer: to remove a column from a data frame in R, assign NULL to it (df$col <- NULL, which changes df in place), or build a copy without it with a negative index (df[, -2], df[, setdiff(names(df), "col")]), subset(df, select = -col), or dplyr’s select(df, !col). This guide is for researchers cleaning an analysis data set who need to know which method to use, and which ones fail quietly when a column name is wrong.
What it is used for in research
Dropping columns is a routine step when preparing a data set for analysis: removing direct identifiers before sharing data, discarding scratch or helper variables, or trimming a wide export to the variables named in a pre-specified analysis plan. Do the removal in a script that reads the raw file and writes a new object or file, never by editing the raw data. That keeps the step reproducible and reviewable. See transformation in R for the wider workflow and removing rows in R for the row-wise counterpart.
Install or access
The NULL assignment, negative indexing and subset() are part of base R, which you install from CRAN (cran.r-project.org); nothing else is needed. The select() function belongs to the dplyr package (dplyr.tidyverse.org):
install.packages("dplyr")
library(dplyr)
Step-by-step: remove one or more columns
The examples use the built-in iris data set, whose columns are Sepal.Length, Sepal.Width, Petal.Length, Petal.Width and Species. Work on a copy so you can compare the result.
1. Assign NULL (removes in place)
df <- iris
df$Species <- NULL
df[["Petal.Width"]] <- NULL
names(df)
The base R manual for data-frame extraction states that assigning NULL “deletes the column if a single column is selected”, and its own example is sw$new4 <- NULL # delete the column (R documentation, ?Extract.data.frame, read 2026-11-07). This changes the object itself rather than returning a new one.
2. Negative indexing (returns a copy)
df <- iris
df[, -5] # drop the fifth column
df[, -c(1, 3)] # drop columns 1 and 3
df[, !(names(df) %in% c("Sepal.Length", "Species"))] # drop by name
df[, setdiff(names(df), "Species")] # same idea
The R documentation for [ says negative integers in i and j indicate “elements/slices to leave out of the selection” (?Extract, read 2026-11-07). To drop by name, build the set of names to keep, as above; a character vector cannot be negated directly, and positive and negative indices cannot be mixed in one call.
3. subset() with select
subset(iris, select = -Species)
subset(iris, select = -c(Sepal.Length, Petal.Length))
The subset() help page describes select as an expression indicating the columns to select and notes that columns can be dropped (see its examples) (?subset, read 2026-11-07). See subset in R for rows and columns together.
4. dplyr select() with !
library(dplyr)
iris |> select(!Species)
iris |> select(!c(Sepal.Length, Petal.Length))
iris |> select(!ends_with("Width"))
The dplyr reference states that “The ! operator negates a selection” and gives these patterns, including select(!c(Sepal.Length, Petal.Length)) and select(!ends_with("Width")) (dplyr.tidyverse.org/reference/select.html, read 2026-11-07). More on this verb is in select in R.
Choosing a method: what changes and what fails
| Method | Changes the object in place? | Selects by | Column name that does not exist |
|---|---|---|---|
df$col <- NULL |
Yes | Name | No error; nothing is removed |
df[, -n] |
No, returns a copy | Position | Not applicable (positions, not names) |
df[, setdiff(names(df), "col")] |
No | Name | No error; nothing is removed |
select(df, !all_of("col")) |
No | Name | Error |
select(df, !any_of("col")) |
No | Name | No error; nothing is removed |
The dplyr reference defines the difference between the last two rows: all_of() throws an out-of-bounds error if any name is missing, while any_of() is “the same as all_of(), except that no error is thrown for names that don’t exist”. Use all_of() when a missing column means something went wrong upstream, and any_of() when you are tidying files that may or may not contain a column. The base R rows describe standard behaviour that you can confirm in a few lines on a throwaway data frame.
Output and how to read it
After any removal, check the result rather than assuming it:
names(df) # which columns remain
ncol(df) # how many (iris has 5 before removal)
str(df) # types of what is left
Comparing ncol() before and after confirms that the number of columns dropped is the number you intended. This catches the silent no-op cases in the table above.
Troubleshooting
The result is a vector, not a data frame
With df[, -1], if only one column remains, base R returns a vector, because the drop argument of [ defaults to dropping to the lowest dimension when one column is left. Use df[, -1, drop = FALSE] to keep a data frame.
Every column disappeared, or none did
The pattern df[, -which(names(df) == "col")] looks safe but is not: when the name does not match, which() returns integer(0), and negating an empty index selects no columns at all. Prefer setdiff(names(df), "col") or !(names(df) %in% "col").
“undefined columns selected” or “object not found”
These usually mean a misspelled name. Check the exact spelling and case with names(df), and use all_of() in dplyr if you want a misspelling to stop the script.
Removing columns by pattern
In dplyr, put selection helpers inside !, such as !starts_with() or !ends_with(), as in the examples above. In base R, filter the names first: df[, !grepl("Width", names(df))].
Free alternatives
| Tool | Drop a column | Documentation |
|---|---|---|
| Base R (CRAN) | df$col <- NULL, df[, -n], subset(df, select = -col) |
R manual pages ?Extract.data.frame, ?subset |
| dplyr (CRAN) | select(df, !col) |
dplyr.tidyverse.org/reference/select.html |
Both are free and open source. For the same task in Python, see how to drop columns in pandas.
FAQ
How do I remove a column in R by name?
Use df$col <- NULL to remove it in place, or select(df, !col) in dplyr to get a copy without it.
How do I remove multiple columns in R?
Use a vector of positions or names: df[, -c(1, 3)], subset(df, select = -c(a, b)), or select(df, !c(a, b)).
How do I remove a column that might not exist?
Use select(df, !any_of("col")), which does not throw an error for names that do not exist, or df$col <- NULL, which also does nothing when the column is absent.
Does removing a column change the original file?
No. These operations change the object in your R session. The file on disk changes only if you write the object back with a function such as write.csv(); write to a new file name and keep the raw data untouched. Related: how to rename columns in R.
Sources
- R Core Team, Extract.data.frame and Extract manual pages, stat.ethz.ch/R-manual/R-devel/library/base/html/ (read 2026-11-07).
- R Core Team, subset manual page, stat.ethz.ch/R-manual/R-devel/library/base/html/subset.html (read 2026-11-07).
- dplyr documentation, Keep or drop columns using their names and types, dplyr.tidyverse.org/reference/select.html (read 2026-11-07).







