HOWTO · R

How to Drop Multiple Columns From a Data Frame Using Dplyr

Learn how to remove named columns, character-vector selections, ranges, name patterns, numeric suffix ranges, or predicate-matched columns with dplyr.

On this page

To remove multiple columns with dplyr, use dplyr::select() with the complement operator - (or !) to keep every column except those selected for removal.

How to Drop Multiple Columns with dplyr

Choose direct names when the set is fixed, all_of() or any_of() when names live in a character vector, a range for consecutive columns, a helper for naming patterns, or where() for a data-based rule. Check the resulting names before using the transformed data downstream.

Set Up the R Session and Example Data

dplyr provides select() and the tidyselect helpers used below. In select(), bare column names are expressions, while names stored in an object must be character strings. The examples show the dplyr syntax; this self-contained base R example demonstrates the same name-based exclusion without requiring an installed package.

Run this example:

records <- data.frame(
  id = 1:3,
  name = c("Ada", "Lin", "Sam"),
  score = c(91, 84, 88),
  debug_note = c("first", "second", "third"),
  group = c("A", "B", "A")
)

to_remove <- c("id", "debug_note")
remaining <- records[setdiff(names(records), to_remove)]
cat(paste(names(remaining), collapse = ", "), "\n", sep = "")
name, score, group

Drop Named Columns Directly

When the columns to remove are known in advance, write their unquoted names inside select(). For example, select(records, -c(id, debug_note)) drops both columns and retains the others. You can also write select(records, -id, -debug_note). These forms express the same exclusion.

The complement of a union is another equivalent form: select(records, -(id | debug_note)). The | operator combines selections, and the leading - excludes that combined set. Use whichever form makes the intended set easiest to read. Positional exclusion is also available, as in select(records, -c(1, 4)), but positions depend on column order; prefer names when the input schema can change.

Drop Columns from a Character Vector

If a configuration, user choice, or earlier calculation supplies the names, store them as strings and pass them through a helper. select(records, -all_of(to_remove)) requires every requested name to exist. This is useful when a missing name should reveal a mismatch between the expected and actual schema.

Use select(records, -any_of(to_remove)) when names may be absent and the intended operation is “remove whichever of these columns are present.” any_of() makes the removal tolerant of schema variation; it does not remove any other columns. For dependable pipelines, choose based on whether absence should be treated as a problem or accepted as normal.

Drop a Range of Columns

The colon operator selects an inclusive sequence. select(records, -(name:score)) excludes columns from name through score according to their current order in the data. select(records, -(2:4)) does the same by position. A name range is convenient when its endpoints are stable; positional ranges are concise but can target different columns after a reorder. Neither syntax means alphabetical ordering.

Drop Columns by Name Patterns

Use starts_with(), ends_with(), or contains() to select names by a shared prefix, suffix, or substring, then negate that selection. This separate frame includes names for each helper below:

pattern_records <- data.frame(
  id = 1:2,
  name = c("Ada", "Lin"),
  raw_payload = c("x", "y"),
  tmp_cache = c("p", "q"),
  score_old = c(7, 8),
  temp_flag = c(TRUE, FALSE),
  measure_1 = c(10, 20),
  measure_2 = c(30, 40),
  group = c("A", "B")
)

With columns id, name, raw_payload, tmp_cache, score_old, temp_flag, measure_1, measure_2, and group, select(pattern_records, -starts_with("raw_")) retains id, name, tmp_cache, score_old, temp_flag, measure_1, measure_2, and group. select(pattern_records, -starts_with(c("raw_", "tmp_"))) also removes tmp_cache, retaining id, name, score_old, temp_flag, measure_1, measure_2, and group. select(pattern_records, -ends_with("_old")) retains id, name, raw_payload, tmp_cache, temp_flag, measure_1, measure_2, and group; select(pattern_records, -contains("temp")) retains id, name, raw_payload, tmp_cache, score_old, measure_1, measure_2, and group.

These helpers ignore case by default; set ignore.case = FALSE when case must distinguish names. To use a regular expression, write select(pattern_records, -matches("^measure_[0-9]+$")); it removes measure_1 and measure_2, retaining id, name, raw_payload, tmp_cache, score_old, temp_flag, and group. This pattern matches names beginning with measure_ followed by one or more digits and nothing else. matches() is flexible, but use it only when the pattern is clear to everyone maintaining the code.

Drop Columns by Numeric Name Ranges

For consistently prefixed names with numeric suffixes, num_range() selects a suffix sequence without spelling out each full name. With the pattern_records columns listed above, select(pattern_records, -num_range("measure_", 1:2)) removes measure_1 and measure_2, retaining id, name, raw_payload, tmp_cache, score_old, temp_flag, and group. The first argument is the shared prefix and the second is the numeric sequence. Set width when suffixes use fixed padding, such as width = 2 for names like measure_01. This helper addresses numeric suffixes; it does not select by the values stored in those columns.

Drop Columns by a Predicate

where() applies a predicate to each column and selects the columns for which it returns TRUE; negate it to drop that selection. For example, select(records, -where(is.character)) removes all character columns. Use this when a data property, rather than a particular name, defines the group to remove.

Choose a predicate that is appropriate for every column it may inspect. A numeric summary such as mean() is not suitable for arbitrary mixed data, and a broad rule can remove more columns than intended. If only some numeric columns should go, combine a type check with a condition that is safe for those columns, or select the specific names instead.

Troubleshoot select() and Find Help

If an unqualified select() call behaves unexpectedly, another attached package may have masked it. Try dplyr::select(data, ...); if that works, use the namespace prefix or adjust package loading so the intended function is called.

The dplyr select() reference documents column selection and links to its helpers. The tidyselect selection language reference explains selection expressions and operators. For help in R, use ?select or search the Help pane for select without parentheses.

The general rule is to describe the columns to remove with the clearest suitable name, vector, range, pattern, or predicate selection, then take its complement. This keeps the retained columns explicit in the code’s logic and makes it easier to adapt the selection when the data shape changes.