Cut a price table down to the rows and columns you want, first with base R square brackets and then with the dplyr verbs filter, select and arrange.
A data frame takes two indices: p[rows, columns]. Rows go before the comma, columns after it. Leave a side blank and you keep all of it.
In this lesson I cut the price table down two ways. First with square brackets, which are base R and always there. Then with filter(), select() and arrange() from dplyr, which say the same thing in words. Both give the same rows.
Step 1. Load the table
prices.csv holds simulated closing prices for three tickers over 40 business days. read.csv() hands back Dates as text, so I convert the column with as.Date() and the sorting later comes out by calendar order rather than alphabetically.
p <-read.csv("prices.csv") # 120 rows, four columnsp$Date <-as.Date(p$Date) # text to real datesprint(dim(p)) # -> [1] 120 4
[1] 120 4
print(head(p, 3))
Date Ticker Close Volume
1 2026-01-02 AAA 186.66 2183494
2 2026-01-02 CCC 416.98 3117814
3 2026-01-02 DDD 125.18 7447373
Three tickers stacked on top of each other, one row per ticker per day.
Step 2. Rows with a condition
p$Ticker == "AAA" compares every element of the column against "AAA" and returns 120 TRUEs and FALSEs, exactly the logical vector of Lesson 3. Put it before the comma and R keeps the rows where it is TRUE.
aapl <- p[p$Ticker =="AAA", ] # rows before the comma, blank after itprint(dim(aapl)) # -> [1] 40 4
[1] 40 4
print(head(aapl, 3))
Date Ticker Close Volume
1 2026-01-02 AAA 186.66 2183494
4 2026-01-05 AAA 186.38 8463199
7 2026-01-06 AAA 188.51 5299573
Forty rows out of 120. The numbers on the left are row names, and they are 1, 4, 7 because those were the AAA rows in the full table. Selecting rows keeps the old labels.
The comma is not optional. p[p$Ticker == "AAA"] without it asks for columns, not rows, and errors.
Step 3. Columns, and both at once
Names after the comma pick columns. Leave the row side blank to keep every row.
two <- p[, c("Date", "Close")] # all rows, two columnsprint(head(two, 3))
Date Close
1 2026-01-02 186.66
2 2026-01-02 416.98
3 2026-01-02 125.18
Use &, not &&. && looks at the first element only and gives a single TRUE or FALSE, which is what if wants, not what a row index wants.
Step 5. The dplyr spellings
dplyr gives each job a verb. filter() takes rows, select() takes columns. Both take the data frame as their first argument, and both let you write column names bare, without p$ and without quotes.
library(dplyr) # filter, select, arrange, slice_headrows <-filter(p, Ticker =="AAA", Close >190) # commas mean andout <-select(rows, Date, Close) # two columns by bare nameprint(out)
Date Close
1 2026-01-19 190.92
2 2026-01-21 190.60
3 2026-01-27 193.08
4 2026-01-28 191.35
5 2026-01-29 192.76
6 2026-01-30 191.04
The same six rows as Step 4. The one difference is the row names: dplyr renumbers from 1, square brackets kept 34, 40, 52. Reset the names on the base R result and the two objects match exactly.
rownames(big) <-NULL# drop the old labelsprint(identical(big, out)) # -> [1] TRUE
|> passes the value on its left into the function on its right as the first argument. So p |> filter(Ticker == "AAA") is filter(p, Ticker == "AAA"). Because every dplyr verb takes the data frame first, they chain.
arrange() sorts, desc() sorts downward, and slice_head(n = 5) keeps the first five rows of whatever it is handed.
top <- p |>filter(Ticker =="AAA", Close >190) |># six rows surviveselect(Date, Close) |># two columnsarrange(desc(Close)) |># highest close firstslice_head(n =5) # keep the top fiveprint(top)
Date Close
1 2026-01-27 193.08
2 2026-01-29 192.76
3 2026-01-28 191.35
4 2026-01-30 191.04
5 2026-01-19 190.92
Read it top to bottom: take p, keep AAA above 190, keep two columns, sort by close, show five. arrange() on its own sorts upward.
print(p |>filter(Ticker =="DDD") |># one tickerselect(Date, Close) |># drop Ticker and Volumearrange(Close) |># lowest close firstslice_head(n =3)) # three cheapest days
Date Close
1 2026-02-23 101.43
2 2026-02-06 103.00
3 2026-02-09 103.26