R Select(), Filter(), Arrange(), Pipeline with Example

⚡ Smart Summary

Select, Filter and Arrange in R are the three dplyr verbs that reduce a data frame to the columns, rows and order you need. This walkthrough chains them with the pipe operator on a 205-row travel-times dataset.

  • 📋 Column Selection: select(df, A, B) keeps named columns, select(df, A:C) keeps a range, and select(df, -C) drops one.
  • 🔎 Row Filtering: filter(df, condition) keeps matching rows, and conditions combine with the ampersand for AND or the vertical bar for OR.
  • 🔣 Sorting: arrange() orders rows ascending by default, and desc() reverses any single column.
  • 🔗 Pipe Operator: The %>% operator passes each result straight into the next verb, so no intermediate objects clutter the environment.
  • 🧹 Helper Functions: starts_with(), contains() and where() select columns by pattern or type instead of by name.
  • 📐 Verb Order Matters: Filter early to shrink the data, then select, then arrange, which keeps every later step cheaper.

R Select Filter Arrange Pipeline

What is dplyr in R?

dplyr is the data manipulation package of the tidyverse. It replaces the bracket notation of base R with a small set of verbs, each one named after the action it performs and each one taking the data frame as its first argument. That consistent shape is what allows the verbs to be chained together.

Verb Acts on What it does
select() Columns Keeps or drops variables
filter() Rows Keeps rows matching a condition
arrange() Rows Reorders rows by one or more columns
mutate() Columns Creates or modifies a variable
summarise() Whole table Collapses many rows into a single statistic
group_by() Whole table Splits the data so later verbs run per group

This tutorial covers the first three verbs plus the pipe. The aggregate function tutorial covers group_by() and summarise() in depth.

Dataset Used in This Tutorial

The library called dplyr contains valuable verbs to navigate inside the dataset. Through this tutorial, you will use the Travel times dataset. The dataset records the trips a driver makes between home and workplace. There are fourteen variables in the dataset, including:

  • DayOfWeek: Identify the day of the week the driver uses his car
  • Distance: The total distance of the journey
  • MaxSpeed: The maximum speed of the journey
  • TotalTime: The length in minutes of the journey

The dataset holds 205 observations, and the rides took place from Monday to Friday.

First of all, you need to:

  • load the dataset
  • check the structure of the data.

One handy dplyr function is glimpse(). It plays the same role as base R str() but prints one row per variable with the type and the first few values, which is far easier to scan on a wide dataset.

library(dplyr) 
PATH <- "https://raw.githubusercontent.com/guru99-edu/R-Programming/master/travel_times.csv"
df <- read.csv(PATH)
glimpse(df)

Output:

## Observations: 205
## Variables: 14
## $ X              <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, ...
## $ Date           <fctr> 1/6/2012, 1/6/2012, 1/4/2012, 1/4/2012, 1/3/20...
## $ StartTime      <fctr> 16:37, 08:20, 16:17, 07:53, 18:57, 07:57, 17:3...
## $ DayOfWeek      <fctr> Friday, Friday, Wednesday, Wednesday, Tuesday,...
## $ GoingTo        <fctr> Home, GSK, Home, GSK, Home, GSK, Home, GSK, GS...
## $ Distance       <dbl> 51.29, 51.63, 51.27, 49.17, 51.15, 51.80, 51.37...
## $ MaxSpeed       <dbl> 127.4, 130.3, 127.4, 132.3, 136.2, 135.8, 123.2...
## $ AvgSpeed       <dbl> 78.3, 81.8, 82.0, 74.2, 83.4, 84.5, 82.9, 77.5,...
## $ AvgMovingSpeed <dbl> 84.8, 88.9, 85.8, 82.9, 88.1, 88.8, 87.3, 85.9,...
## $ FuelEconomy    <fctr> , , , , , , -, -, 8.89, 8.89, 8.89, 8.89, 8.89...
## $ TotalTime      <dbl> 39.3, 37.9, 37.5, 39.8, 36.8, 36.8, 37.2, 37.9,...
## $ MovingTime     <dbl> 36.3, 34.9, 35.9, 35.6, 34.8, 35.0, 35.3, 34.3,...
## $ Take407All     <fctr> No, No, No, No, No, No, No, No, No, No, No, No...
## $ Comments       <fctr> , , , , , , , , , , , , , , , Put snow tires o...	

The Comments variable clearly needs a closer look: the first observations are all empty.

sum(df$Comments =="")

Code Explanation

  • sum(df$Comments == “”): Count the observations equal to an empty string in the Comments column of df

Output:

## [1] 181

With the data loaded, start with the verb that trims the columns.

select()

We will begin with the select() verb. We don’t necessarily need all the variables, and a good practice is to select only the variables you find relevant.

We have 181 missing observations, almost 90 percent of the dataset. If you decide to exclude them, you won’t be able to carry on the analysis.

The other possibility is to drop the variable Comment with the select() verb.

We can select variables in different ways with select(). Note that, the first argument is the dataset.

- `select(df, A, B ,C)`: Select the variables A, B and C from df dataset.
- `select(df, A:C)`: Select all variables from A to C from df dataset.
- `select(df, -C)`: Exclude C from the dataset from df dataset.	

You can use the third way to exclude the Comments variable.

step_1_df <- select(df, -Comments)
dim(df)

Output:

## [1] 205  14
dim(step_1_df)

Output:

## [1] 205  13

The original dataset has 14 features while the step_1_df has 13.

select() Helper Functions in R

Naming every column becomes impractical once a dataset has dozens of variables. dplyr ships with helpers that select columns by pattern or by type instead.

Helper Selects columns that Example
starts_with() Begin with a string select(df, starts_with(“Avg”))
ends_with() End with a string select(df, ends_with(“Time”))
contains() Contain a string anywhere select(df, contains(“Speed”))
matches() Match a regular expression select(df, matches(“^Max|^Avg”))
where() Satisfy a type test select(df, where(is.numeric))
everything() Have not been named yet select(df, TotalTime, everything())
# Keep only the timing columns
df %>% select(ends_with("Time"))

# Keep every numeric column
df %>% select(where(is.numeric))

# Move TotalTime to the front, keep the rest in place
df %>% select(TotalTime, everything())

Two more verbs pair naturally with select(). Use rename(new_name = old_name) to relabel a column without dropping the others, and relocate() to move columns without listing them all.

filter()

The filter() verb keeps the observations that satisfy a condition. The filter() works exactly like select(), you pass the data frame first and then a condition separated by a comma:

filter(df, condition)
arguments:
- df: dataset used to filter the data
- condition:  Condition used to filter the data	

One criteria

First of all, you can count the number of observations within each level of a factor variable.

table(step_1_df$GoingTo)

Code Explanation

  • table(): Count the number of observations per level. It expects a factor or character variable
  • table(step_1_df$GoingTo): Count the number of trips towards each destination

Output:

## 
##  GSK Home 
##  105  100	

The function table() indicates 105 rides are going to GSK and 100 to Home.

We can filter the data to return one dataset with 105 observations and another one with 100 observations.

# Select observations where GoingTo == Home
select_home <- filter(df, GoingTo == "Home")
dim(select_home)

Output:

## [1] 100  14
# Select observations where GoingTo == GSK (work)
select_work <- filter(df, GoingTo == "GSK")
dim(select_work)

Output:

## [1] 105  14

Multiple criteria

We can filter a dataset with more than one criteria. For instance, you can extract the observations where the destination is Home and the trip occurred on a Wednesday.

select_home_wed <- filter(df, GoingTo == "Home" & DayOfWeek == "Wednesday")
dim(select_home_wed)

Output:

## [1] 23 14

23 observations matched this criterion.

Common filter() Conditions and Operators in R

filter() keeps every row for which the condition evaluates to TRUE. Rows that evaluate to NA are dropped, which is the behaviour that surprises most beginners.

Operator Meaning Example
== Equal to filter(df, GoingTo == “Home”)
!= Not equal to filter(df, DayOfWeek != “Monday”)
& AND, both must hold filter(df, Distance > 50 & MaxSpeed > 130)
| OR, either may hold filter(df, DayOfWeek == “Monday” | DayOfWeek == “Friday”)
%in% Matches any value in a vector filter(df, DayOfWeek %in% c(“Monday”, “Friday”))
between() Falls inside a numeric range filter(df, between(Distance, 48, 52))
is.na() Is a missing value filter(df, is.na(FuelEconomy))

Three habits prevent most filter() bugs.

  • Use %in% instead of chained OR. filter(df, DayOfWeek %in% c(“Monday”, “Friday”)) is shorter and far harder to mistype than two == comparisons joined by a vertical bar.
  • Never test a missing value with ==. The expression x == NA returns NA, not TRUE, so the row is silently dropped. Use is.na(x) instead.
  • Commas mean AND. filter(df, a, b) is identical to filter(df, a & b), which is why the example earlier in this tutorial works either way.

The Pipe Operator in dplyr

The creation of a dataset requires a lot of operations, such as:

  • importing
  • merging
  • selecting
  • filtering
  • and so on

The dplyr library comes with a practical operator, %>%, called the pipe. It makes data manipulation cleaner, faster and less prone to error.

This operator is a code which performs steps without saving intermediate steps to the hard drive. If you are back to our example from above, you can select the variables of interest and filter them. We have three steps:

  • Step 1: Import data: Import the gps data
  • Step 2: Select data: Select GoingTo and DayOfWeek
  • Step 3: Filter data: Return only Home and Wednesday

We can use the hard way to do it:

# Step 1
step_1 <- read.csv(PATH)

# Step 2 
step_2 <- select(step_1, GoingTo, DayOfWeek)

# Step 3 
step_3 <- filter(step_2, GoingTo == "Home", DayOfWeek == "Wednesday")

head(step_3)

Output:

##   GoingTo DayOfWeek
## 1    Home Wednesday
## 2    Home Wednesday
## 3    Home Wednesday
## 4    Home Wednesday
## 5    Home Wednesday
## 6    Home Wednesday	

That is not a convenient way to chain many operations. Every intermediate result stays in the environment, and the names quickly stop meaning anything.

Use the pipe operator %>% instead. You name the data frame once at the start and every step flows from it.

Basic syntax of pipeline

New_df <- df %>%
step 1 %>%
step 2 %>%
...
arguments
- New_df: Name of the new data frame 
- df: Data frame used to compute the step
- step: Instruction for each step
- Note: the last instruction does not need a trailing `%>%`, because nothing follows it
- Note: assigning the result to a new object is optional. Without it, the output prints in the console.

You can create your first pipe following the steps enumerated above.

# Create the data frame filter_home_wed.It will be the object return at the end of the pipeline
filter_home_wed <- 

#Step 1
read.csv(PATH) %>% 

#Step 2
select(GoingTo, DayOfWeek) %>% 

#Step 3
filter(GoingTo == "Home",DayOfWeek == "Wednesday")
identical(step_3, filter_home_wed)

Output:

## [1] TRUE

The two objects are identical, so the pipe produced exactly the same result in a single readable statement.

arrange()

In the previous tutorial, you learn how to sort the values with the function sort(). The library dplyr has its sorting function. It works like a charm with the pipeline. The arrange() verb can reorder one or many rows, either ascending (default) or descending.

- `arrange(A)`: Ascending sort of variable A
- `arrange(A, B)`: Ascending sort of variable A and B
- `arrange(desc(A), B)`: Descending sort of variable A and ascending sort of B

We can sort the distance by destination.

# Sort by destination and distance
step_2_df <- step_1_df %>%
    arrange(GoingTo, Distance)
head(step_2_df)

Output:

##     X       Date StartTime DayOfWeek GoingTo Distance MaxSpeed AvgSpeed
## 1 193  7/25/2011     08:06    Monday     GSK    48.32    121.2     63.4
## 2 196  7/21/2011     07:59  Thursday     GSK    48.35    129.3     81.5
## 3 198  7/20/2011     08:24 Wednesday     GSK    48.50    125.8     75.7
## 4 189  7/27/2011     08:15 Wednesday     GSK    48.82    124.5     70.4
## 5  95 10/11/2011     08:25   Tuesday     GSK    48.94    130.8     85.7
## 6 171  8/10/2011     08:13 Wednesday     GSK    48.98    124.8     72.8
##   AvgMovingSpeed FuelEconomy TotalTime MovingTime Take407All
## 1           78.4        8.45      45.7       37.0         No
## 2           89.0        8.28      35.6       32.6        Yes
## 3           87.3        7.89      38.5       33.3        Yes
## 4           77.8        8.45      41.6       37.6         No
## 5           93.2        7.81      34.3       31.5        Yes
## 6           78.8        8.54      40.4       37.3         No

dplyr Verbs in R: Quick Reference

The table below lists every verb used in this tutorial, with the syntax and what each call returns:

Verb Objective Code Explanation
glimpse check the structure of a df
glimpse(df)
Same purpose as str(), easier to read
select() Select/exclude the variables
select(df, A, B ,C)
Select the variables A, B and C
select(df, A:C)
Select all variables from A to C
select(df, -C)
Exclude C
filter() Keep the rows matching one or more conditions
filter(df, condition1)
One condition
filter(df, condition1 & condition2)
Two conditions combined with AND
arrange() Sort the dataset with one or many variables
arrange(A)
Ascending sort of variable A
arrange(A, B)
Ascending sort of variable A and B
arrange(desc(A), B)
Descending sort of variable A and ascending sort of B
%>% Create a pipeline between each step
step 1 %>% step 2 %>% step 3

FAQs

The magrittr %>% pipe ships with dplyr and supports placeholder syntax with a dot. The native |> pipe arrived in R 4.1 and needs no package. Both pass the left result into the first argument on the right.

No. Every dplyr verb returns a new data frame and leaves the input untouched. You must assign the result to an object, or pipe it onward, for the change to persist.

A comparison against NA returns NA rather than TRUE, and filter() keeps only rows that are TRUE. Use is.na() to select missing values deliberately, or drop_na() from tidyr to remove them.

Feature engineering for AI models is mostly filtering rows, selecting columns, and sorting. dplyr expresses those steps as readable pipelines that reviewers can audit, which matters when a training dataset must be reproducible.

Yes. AI assistants can translate bracket subsetting into select() and filter() calls and explain the pipe. Always run both versions and compare dimensions, because base R and dplyr treat missing values differently.

Summarize this post with: