install.packages("dplyr")AEM 6850
Empirical Methods for Applied Economists
Prof. Ariel Ortiz-Bobea
Optional · not taught in class
This page is not on the calendar. It is written, runnable, and about one class meeting of material.
By the end of this page you can:
group_by() and summarize().left_join(), and check the row count before and after.A package is R code somebody else wrote, tested, and shared. dplyr is the standard one for wrangling data frames. In the console:
Installing put dplyr on the machine. library() puts it into the current R session:
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
filter() and lag() now stand in front of the base ones.library() at the top of every script.Reading stays base R: the same read, the same repairs, the same checks.
pm <- read.csv("data/epa_pm25_compton_2025.csv",
colClasses = c("Site.ID" = "character",
"State.FIPS.Code" = "character",
"County.FIPS.Code" = "character"))
names(pm)[names(pm) == "Daily.Mean.PM2.5.Concentration"] <- "pm25"
pm$date <- as.Date(pm$Date, format = "%m/%d/%Y")
nrow(pm) # the whole year, both instruments: 662 rows#> [1] 662
filter(): keep rowsBase R cuts the winter window with brackets. Same subset, first verb:
#> [1] 59
pm$.| still spells or, inside one condition.select(): keep columnsBase needed two coordinates and quoted names. select() takes bare names, in the order you want them:
mutate(): add columns#> date pm25 above35 month
#> 1 2025-01-01 53.2 TRUE Jan
#> 2 2025-01-02 33.6 FALSE Jan
#> 3 2025-01-03 27.7 FALSE Jan
#> [1] 4
name = expression, computed inside the frame: bare column names again."%b" is the short month name: format strings, in reverse.arrange(): sort rowsarrange() sorts rows by a column. desc() flips it to descending:
#> date pm25
#> 1 2025-01-01 53.2
#> 2 2025-01-09 47.7
#> 3 2025-01-08 44.6
worst, or nested calls you read inside out. The pipe fixes this.|> passes whatever is on its left to the function on its right, as that function’s first argument:
#> [1] 301
#> [1] 301
|> as “then”. Verbs take the data frame first so chains work.%>%. Read it the same way; write |>.#> date pm25
#> 1 2025-01-01 53.2
#> 2 2025-01-09 47.7
#> 3 2025-01-08 44.6
#> 4 2025-01-10 42.2
#> 5 2025-01-02 33.6
group_by() and summarize()Write down what this should print. Evidence: no gaps in the window, so 31 and 28 days. January has all four event days, so its mean should clear its median with room.
n() counts the rows of its group; it works only inside summarize().#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
#> # A tibble: 2 x 3
#> month days median_pm25
#> <fct> <int> <dbl>
#> 1 Jan 31 16.9
#> 2 Feb 28 12.6
count(): the counting shorthand#> POC AQS.Parameter.Code n
#> 1 1 88101 301
#> 2 3 88101 7
#> 3 3 88502 354
count(x) = group_by(x) + summarize(n = n()).table(): 301, 7, 354. Sum: 662, the whole file.The regulatory series has 301 rows where a full year has 365. A chain you can now read says which months the missing days fall in:
Write down the twelve counts this should print. The evidence: the count() above says the regulatory series has 301 rows, and a full month has 28 to 31 days, so 64 days are missing somewhere. Your twelve numbers must sum to 301. Decide where you think the gap falls before you look.
#> month n
#> 1 Jan 31
#> 2 Feb 28
#> 3 Mar 30
#> 4 Apr 2
#> 5 May 1
#> 6 Jun 28
#> 7 Jul 31
#> 8 Aug 31
#> 9 Sep 27
#> 10 Oct 31
#> 11 Nov 30
#> 12 Dec 31
Readings in one table, the weather in another. The weather file carries three lines of station metadata before its header, so skip = 3:
#> [1] 59
#> [1] 90
skip = 3 steps over the station metadata. Rename, convert the date.date column in each.left_join(): joining two tables#> [1] 59
#> [1] 0
by names the shared column. The left table’s rows, both tables’ columns.NA: every day found its weather.The join is what makes the next question one chain:
#> # A tibble: 2 x 3
#> wet days mean_pm25
#> <lgl> <int> <dbl>
#> 1 FALSE 48 20.1
#> 2 TRUE 11 6.43
#> [1] 58
#> [1] 59
inner_join() keeps only keys in both tables: rows vanish, silently.left_join() keeps the left table and fills with NA.One more habit. Wrap any line in system.time() and R reports how long it took:
elapsed: the seconds you waited. Here, effectively zero.On the page: the verb table with base translations, the pipe and its shortcut, the factor recipe, and the things that bite.
AEM 6850 · Wrangling with dplyr