PSet 2
Who benefits when kangaroo rats are excluded?
In 1977, ecologists in the Arizona desert built fences with doors too small for one animal, the kangaroo rat, and then counted who showed up every month for decades.

Your question: when the biggest seed-eater is fenced out, are other rodents caught more often? The answer is in 35,606 rows of data, but only if you count fairly.
- Due: Sunday, November 15, 2026, 11:59 PM PT, on Bruin Learn. (Released Tuesday, November 3.)
- Chapters: 1–19.
- Download: starter project and data. The report template has one section per task.
- Already downloaded the zip earlier? The data are unchanged. Just save the updated report template and
references.bibinto your project folder, next to the.Rprojfile. - Submit: your zipped project folder,
PSet2_LastName.zip(see the checklist). - Grading: 15 items × 2 pts = 30. Each item is scored 2 / 1 / 0 (complete / partial / missing).
- AI: not allowed (per the syllabus). Discussing ideas is fine; your code and words must be your own.
What you need to know
| Term | Meaning |
|---|---|
| plot | A fenced 50 × 50 m patch. 16 plots: 8 control (all rodents enter), 8 exclosure (kangaroo rats DM, DO, DS kept out). |
| census | One monthly visit. Each sampled plot gets 49 traps for one night = 49 trap-nights. |
| capture | One row of captures.csv: one animal caught once. |
| capture rate | 100 * captures / trap_nights. We compare rates, not counts. |
Why rates? A plot visited twice as often catches twice as many animals:
| Plot | Visits | Trap-nights | Captures | Rate |
|---|---|---|---|---|
| A | 10 | 490 | 10 | 2.0 |
| B | 5 | 245 | 5 | 2.0 |
Same rate, different counts. Real plots get 8–13 visits a year.
Why zeros matter: a visit that caught nothing is a real 0. A visit that never happened is not a 0.
| File | One row is… | Key columns |
|---|---|---|
captures.csv |
one capture | census, plot, species, mass_g, hindfoot_mm |
species.csv |
one species | species, common_name |
plots.csv |
one plot | plot, treatment |
surveys.csv |
one plot × visit (incl. unsampled) | census, plot, year, sampled, trap_nights, protocol_ok |
Start: open PSet2_Portal.Rproj → rename the .qmd → Render once → write a 2–3 sentence prediction at the top.
Task 1 — Join the tables (4 pts)
- (2 pts) Make a table of the number of rows and columns in each file, and count the missing values in
mass_g. [nrow(),ncol(),knitr::kable(),is.na()]
Write: why not replace a missing mass with 0? - (2 pts) Build
captures_joined: add each capture’s species name, treatment, and year (matching surveys on bothcensusandplot). Add a columnrodent_group:"Kangaroo rat"forDM/DO/DS,"Other rodent"otherwise. [left_join(),mutate(),if_else()]
Write: match surveys onplotonly instead. How many rows do you get? Why?
Check: captures 35,606 · surveys 5,056 · species 21 · plots 16 · missing mass 810 · captures_joined 35,606.
Species code "NA" is a real species. The starter reads files with na = "" so it isn’t treated as missing.
An audit table is just a small tibble you build by hand:
tibble(table = c("captures", "species"),
rows = c(nrow(captures), nrow(species)))Extend it to all four files and add a columns column.
For the join, the first step looks like this; add the other two yourself:
captures_joined <- captures |>
left_join(species, by = "species")Keep only the survey columns you need (census, plot, year) before joining. Always check nrow() after a join: duplicate keys in the right table add rows (Chapter 10).
Task 2 — Count captures, keeping the zeros (4 pts)
Goal: one row per sampled visit × rodent group, with zero-capture visits kept.
- (2 pts) Build
survey_baseyourself: keep only visits that were sampled, passed the protocol check, and had traps set (see thesampled,protocol_ok, andtrap_nightscolumns; 1 means yes); then add each plot’s treatment. [filter(),left_join()]
Then run the rest of the pipeline from the box below.
Write: why must an unsampled visit be removed rather than counted as zero? Explain the 3 numbered comments in the box code in your own words. - (2 pts) Combine
capture_countswithsurvey_baseusing three different join types and report the row count of each. [left_join(),inner_join(),full_join(),nrow()]
Write: which visits does the inner join drop? Why is the left join the right choice?
Check: survey_base 4,893 rows · census_rates 9,786 rows · 2,887 zeros.
Write survey_base yourself first (check: 4,893 rows). Then:
capture_counts <- captures_joined |>
group_by(census, plot) |>
summarise(kangaroo = sum(rodent_group == "Kangaroo rat"),
other = sum(rodent_group == "Other rodent"),
.groups = "drop")
census_rates <- survey_base |> # 1. Start from visits
left_join(capture_counts, by = c("census", "plot")) |>
mutate(kangaroo = replace_na(kangaroo, 0), # 2. Fill counts with 0
other = replace_na(other, 0)) |>
pivot_longer(c(kangaroo, other),
names_to = "rodent_group", values_to = "n_captures") |>
mutate(capture_rate = 100 * n_captures / trap_nights) # 3. Rate, not countTask 3 — Compare treatments by year (4 pts)
- (2 pts) Build three tables:
plot_year: the meancapture_ratefor each year, plot, treatment, and rodent group. [group_by(),summarise(),mean()]annual_rates: the mean of those across plots, for each year, treatment, and rodent group. [group_by(),summarise()]difference_table: other rodents only, with columnsyear,control,exclosure, anddifference(exclosure minus control). Display it in the chunk labelledtbl-difference. [filter(),select(),pivot_wider(),mutate(),knitr::kable()]
- (2 pts) Write: what does a positive difference mean (units)? Which years are largest and smallest?
Check: annual_rates 108 rows · difference_table 27 rows.
group_by()the columns that define one row of the result, thensummarise(). Add.groups = "drop"to avoid leftover grouping.annual_ratesstarts fromplot_year, not fromcensus_rates.- For
difference_table, keep onlyyear,treatment, and the rate before widening;treatmentsupplies the new column names.
Skip the select() and pivot_wider() gives a table full of NAs (Chapter 9).
Task 4 — Explore (6 pts)
- (2 pts)
p_trend: plotannual_ratesover time as lines, colored by treatment, with one panel per rodent group. Then make an interactive version. [ggplot(),geom_line(),facet_wrap(),plotly::ggplotly()]
Write: did the fences keep kangaroo rats out? What happened to other rodents? - (2 pts) Make two of: (a) a sorted bar chart of captures per species; (b) the body-mass distribution of
DM,PB,PP; (c) hindfoot length vs. mass for those species. [count(),geom_col(),geom_boxplot(),geom_point(),drop_na()]
Write: one sentence under each. - (2 pts) Ask one follow-up question from your trend plot; answer it with a table or plot.
Write: Question / Observation (with a number) / What I’d check next.
p_trend <- ggplot(annual_rates, aes(x = ___, y = ___, color = ___)) +
geom_line() +
facet_wrap(~ ___)count(common_name), thenreorder(common_name, n)to sort bars.
- (b, c)
filter(species %in% c("DM", "PB", "PP"))anddrop_na(mass_g). - Follow-up idea: in the year with the biggest gap, which species were caught most in each treatment?
filter()+count(treatment, common_name).
Task 5 — Final figure (4 pts)
Chunk label: fig-main. Other-rodent capture rate over time, both treatments.
- (2 pts) Use the same two treatment colors as before, plus a second cue (linetype or shape) so it works without color. [
scale_color_manual(),aes(linetype = )] - (2 pts) Add a title that states the finding, axis labels with units, and a caption with the years and data source. Export it as PDF and PNG with a set width and height. [
labs(),#| fig-cap:,ggsave()]
scale_color_manual(values = c(control = "#0072B2", exclosure = "#D55E00"))
ggsave(here::here("figures", "figure_1.png"), plot = figure_1,
width = 7, height = 4.5, dpi = 300)Task 6 — Write it up (4 pts)
- (2 pts) Methods: in a short paragraph, say which data you used and how you calculated the capture rate (in words). Cite the two references in
references.bib, and refer to your table and figure in the text. [[@ernest2009; @portaldata],@tbl-difference,@fig-main] - (2 pts) Conclusion: 100–150 words. Answer the question with numbers + one limitation.
Check: HTML shows “Table 1”, “Figure 1”, and a reference list (no ?@).
Citations and cross-references look like this (a different example, so adapt it):
Penguin data come from @gorman2014. Bill length differs
among species (@tbl-bills, @fig-bills).Chapter 19 covers citations and cross-references.
Task 7 — Reproducible (4 pts)
- (2 pts) Restart R and render. The report runs top to bottom and recreates
figures/figure_1.pdfand.png. - (2 pts) In the Console, set up a package record for the project, then save it after a successful render. [
renv::init(),renv::snapshot(); see Chapter 18]