Menu

dplyr in R: filter, select, mutate, summarize — A Practical Intro

What dplyr is, how to install it, and how its six core verbs plus the pipe turn messy data-frame code into readable pipelines.

This page includes runnable editors - edit, run, and see output instantly.

What dplyr Is

dplyr is R's most popular package for data manipulation - a "grammar of data" built from a handful of verbs. Each verb is a function that takes a data frame, does exactly one job (keep some rows, add a column, compute a group summary), and returns a new data frame. Because every verb has the same shape - data frame goes in, data frame comes out - they snap together with the pipe into pipelines that read top to bottom like a recipe. dplyr is the core of the tidyverse, a family of packages (tidyr, ggplot2, readr) that share this design.

Everything dplyr does, base R also does. Here is a small analysis in pure base R - filter the built-in mtcars data to 6-cylinder cars, compute a new column, and average it by gear count. This one runs:

That works, and it is good R. But notice how the story is smeared across bracket indexing, a $ assignment, and a formula. The dplyr version of the same analysis:

library(dplyr)

mtcars |>
    filter(cyl == 4) |>
    mutate(kml = mpg * 0.425) |>
    group_by(gear) |>
    summarize(avg_kml = round(mean(kml), 1))

Four verbs, in the order you would explain them out loud. That readability is the whole sales pitch, and on a twenty-step pipeline it is decisive.

The dplyr snippets on this page are static because this editor runs base R only - to run them, install dplyr on your own machine as shown next.

Install and Load

One-time install, then load it in every script that uses it:

install.packages("dplyr")   # once, per machine
library(dplyr)              # every script

Installing install.packages("tidyverse") instead pulls in dplyr plus its siblings. When dplyr loads, it warns that it masks stats::filter and stats::lag - that is normal, not an error.

The Six Core Verbs

Every verb takes the data frame as its first argument and column names bare, without quotes or df$ prefixes.

filter() keeps rows that match a condition:

mtcars |> filter(mpg > 25)
mtcars |> filter(cyl == 4, hp < 70)   # comma = AND

select() keeps columns by name, range, or helper:

mtcars |> select(mpg, cyl, hp)
mtcars |> select(mpg:hp)              # a range of adjacent columns
mtcars |> select(starts_with("d"))    # disp, drat

mutate() adds or transforms columns:

mtcars |> mutate(kml = mpg * 0.425, heavy = wt > 3.5)

arrange() sorts rows - wrap a column in desc() for descending:

mtcars |> arrange(desc(mpg))

summarize() collapses rows to one summary row, and group_by() makes it collapse per group instead:

mtcars |>
    group_by(cyl) |>
    summarize(n = n(), avg_mpg = mean(mpg))

group_by() does nothing visible on its own - it just tags the data frame so the next summarize() (or mutate()) works group-by-group. Each verb has a full doc in this chapter: filtering rows, adding columns, sorting, and grouped summaries.

Chaining Verbs with the Pipe

The pipe |> takes the value before it and feeds it in as the first argument of the function after it - x |> f(y) means f(x, y). Since every dplyr verb takes and returns a data frame, verbs chain indefinitely:

mtcars |>
    filter(hp > 100) |>
    mutate(kml = mpg * 0.425) |>
    group_by(cyl) |>
    summarize(cars = n(), avg_kml = round(mean(kml), 1)) |>
    arrange(desc(avg_kml))

Read it aloud: take mtcars, keep cars over 100 horsepower, add a km-per-liter column, group by cylinders, count and average each group, sort by the average. No intermediate variables, no nesting. Older code uses %>% from the magrittr package instead of |> - for dplyr work they are interchangeable, and |> (built into R 4.1+) is covered in the pipes doc.

count() and n(): The Counting Helpers

Counting is so common that dplyr has shortcuts. n() gives the number of rows in the current group (only valid inside summarize() or mutate()), and count() collapses the whole group_by() + summarize(n = n()) dance into one call:

mtcars |> count(cyl)                    # rows per cyl value
mtcars |> count(cyl, gear, sort = TRUE) # two groupers, biggest first

If you find yourself writing group_by(x) |> summarize(n = n()), replace it with count(x).

A Note on Tibbles

dplyr verbs often return a tibble - the tidyverse's variant of a data frame. It is a data frame (everything that accepts one accepts a tibble), with friendlier printing: only the first 10 rows, only the columns that fit, with types shown under each name. Two differences worth knowing: tibbles never use row names (which is why mtcars |> as_tibble() loses the car names - the tidyverse expects identifiers to live in a real column), and single-bracket indexing always returns a tibble rather than sometimes dropping to a vector. Neither should surprise you day to day; just don't panic when str() says tbl_df.

What You Take Away

  • dplyr is a grammar: six verbs, each doing one job to a data frame, chained with the pipe.
  • filter() picks rows, select() picks columns, mutate() adds columns, arrange() sorts, group_by() + summarize() aggregates.
  • x |> f(y) is f(x, y) - pipelines read top to bottom in the order things happen.
  • count() and n() cover most counting needs.
  • Base R can do all of it; dplyr wins on readability once pipelines grow.

Next up: filtering and subsetting in depth - the base R bracket idioms and where dplyr's filter() fits.

Frequently Asked Questions

What is dplyr in R?

dplyr is the most widely used R package for manipulating data frames. It gives you a small set of verbs - filter(), select(), mutate(), arrange(), summarize(), group_by() - that each do one thing to a data frame and chain together with the pipe into readable pipelines. It is part of the tidyverse.

How do I install dplyr?

Run install.packages("dplyr") once, then library(dplyr) at the top of every script that uses it. Installing tidyverse instead gets you dplyr plus its sibling packages (tidyr, ggplot2, readr) in one go.

Do I need dplyr, or is base R enough?

Base R can do everything dplyr does - subsetting, aggregate(), order() - and it is worth knowing because it works everywhere with zero dependencies. dplyr earns its install when pipelines get long: five chained verbs read like a sentence, where the base equivalent reads like nested bracket puzzles.

What is the difference between summarize and summarise in dplyr?

Nothing. They are the same function with US and UK spellings; dplyr exports both. Use whichever your team uses and be consistent.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED