12  Wrap-up and Final-Exam Preparation

NoteData for this chapter

The end-to-end case study uses the BC silviculture workbook (bc_disturbance_reforestation.xlsx). Click the file name to download it directly, then save it in your data/ folder. For the source and details, see the Datasets Used in This Book page.

Chapter goals

By the end of this chapter you will be able to:

  • Trace the full reproducible workflow taught in this book — import → clean → summarize → reshape → join → visualize → render → explain — on a single forestry dataset.
  • Reproduce a complete analysis from a clean project, from a raw file to a rendered Quarto report.
  • Apply a repeatable debugging ladder to any error you meet in the final exam (and on the job).
  • Name concrete next steps for learning beyond this course.

Expected output for this chapter

TipWhat you will produce

A complete end-to-end case-study report: take one of the course datasets, start from the raw file, document your cleaning, summarize by group, produce one figure (and an optional map only if you used the spatial extension), and render it as a single Quarto HTML report. This is both your final review and the best possible exam practice.

12.1 Where we are

You started in Excel (Chapters 1–3), crossed the bridge into R (Chapter 4), and built — one chapter at a time — every stage of a reproducible analysis:

Stage You learned it in Key functions
Import Ch 4–5 read_excel(), read_csv(), here()
Inspect Ch 5 glimpse(), summary(), dim(), colSums(is.na())
Clean Ch 6 select(), filter(), rename(), mutate(), case_when()
Summarize Ch 7 group_by(), summarise(), count(), n_distinct()
Reshape Ch 8 pivot_longer(), pivot_wider()
Join Ch 8 left_join(), the filtering joins, pre/post-join checks
Visualize Ch 9 ggplot2, geoms, facets, themes, fig-alt
Render Ch 5–12 Quarto: code + prose + output in one document

This chapter threads them all together.

12.2 End-to-end case study: BC silviculture

We use one dataset — the BC silviculture file you have known since Chapter 1 — and run it through the whole pipeline.

12.2.1 1. Import

bc <- read_excel(here("data", "bc_disturbance_reforestation.xlsx"),
                 sheet = "Data")
dim(bc)
[1] 37  8

12.2.2 2. Inspect

glimpse(bc)
Rows: 37
Columns: 8
$ Fiscal_Year                   <dbl> 1987, 1988, 1989, 1990, 1991, 1992, 1993…
$ Clearcutting_ha               <dbl> 216304.92, 217836.07, 190635.52, 172360.…
$ Clearcutting_with_reserves_ha <dbl> 5800.400, 1842.952, 1456.666, 1400.013, …
$ Partial_cutting_ha            <dbl> 20076.681, 31878.294, 23785.954, 18930.8…
$ Harvested_ha                  <dbl> 242182.0, 251557.3, 215878.1, 192691.6, …
$ Natural_Disturbance_ha        <dbl> 20875.00, 9641.20, 10711.80, 28300.70, 1…
$ Total_Disturbance_ha          <dbl> 263057.0, 261198.5, 226589.9, 220992.3, …
$ Reforestation_ha              <dbl> 201972.5, 221296.3, 237275.9, 260370.8, …
colSums(is.na(bc))   # missing values per column
                  Fiscal_Year               Clearcutting_ha 
                            0                             0 
Clearcutting_with_reserves_ha            Partial_cutting_ha 
                            0                             0 
                 Harvested_ha        Natural_Disturbance_ha 
                            0                             0 
         Total_Disturbance_ha              Reforestation_ha 
                            0                             0 

12.2.3 3. Clean and derive

We add a decade grouping variable and a reforestation ratio (reforested area per hectare harvested), documenting each step as we go:

bc_clean <- bc |>
  mutate(
    decade          = (Fiscal_Year %/% 10) * 10,
    reforest_ratio  = Reforestation_ha / Harvested_ha
  )
bc_clean |> select(Fiscal_Year, decade, Harvested_ha,
                   Reforestation_ha, reforest_ratio) |> head()
# A tibble: 6 × 5
  Fiscal_Year decade Harvested_ha Reforestation_ha reforest_ratio
        <dbl>  <dbl>        <dbl>            <dbl>          <dbl>
1        1987   1980      242182           201972.          0.834
2        1988   1980      251557.          221296.          0.880
3        1989   1980      215878.          237276.          1.10 
4        1990   1990      192692.          260371.          1.35 
5        1991   1990      213217.          245370.          1.15 
6        1992   1990      234086.          224983.          0.961

12.2.4 4. Summarize by group

by_decade <- bc_clean |>
  group_by(decade) |>
  summarise(
    n_years           = n(),
    mean_harvest_ha   = round(mean(Harvested_ha, na.rm = TRUE)),
    mean_reforest_ha  = round(mean(Reforestation_ha, na.rm = TRUE)),
    mean_ratio        = round(mean(reforest_ratio, na.rm = TRUE), 2),
    .groups = "drop"
  )
by_decade |> kable()
decade n_years mean_harvest_ha mean_reforest_ha mean_ratio
1980 3 236539 220182 0.94
1990 10 203694 236134 1.17
2000 10 207063 221916 1.09
2010 10 204947 218683 1.08
2020 4 124579 209047 1.76

Note the discipline from Chapter 7: n_years is reported beside every mean, so a reader can see that the 1980s and 2020s decades are partial.

12.2.5 5. Visualize — one figure

bc_long <- bc_clean |>
  select(Fiscal_Year, Harvested_ha, Reforestation_ha) |>
  pivot_longer(-Fiscal_Year, names_to = "series", values_to = "area_ha")

ggplot(bc_long, aes(Fiscal_Year, area_ha, colour = series, linetype = series)) +
  geom_line(linewidth = 1) +
  scale_colour_manual(values = c("#B7791F", "#2D6A4F"),
                      labels = c("Harvested", "Reforested")) +
  scale_linetype_manual(values = c("solid", "dashed"),
                        labels = c("Harvested", "Reforested")) +
  labs(title = "BC harvest and reforestation, 1987–2023",
       x = "Fiscal year", y = "Area (hectares)",
       colour = NULL, linetype = NULL,
       caption = "Source: Environmental Reporting BC") +
  theme_minimal()

Line chart of BC harvested area (solid amber line) and reforested area (dashed green line) by fiscal year, 1987 to 2023, with the two series tracking each other and reforestation generally following harvest.

BC harvested and reforested area (ha) by fiscal year, 1987–2023.

12.2.6 6. Render and explain

Everything above lives in one .qmd file. Pressing Render produces a single HTML report that another analyst can reproduce from the raw file — no hidden clicks, no copy-paste. That reproducibility is the whole point of this course.

12.3 Final-exam preparation: the debugging ladder

The exam is not about memorizing function names — it is about recovering when something breaks. When you hit an error, climb this ladder one rung at a time:

  1. Read the error message. R errors are more helpful than they look. Note the function named and the object named.
  2. Identify the step. Which is wrong — the object, the function, or the data? (object not found → object; could not find function → package not loaded; an unexpected number of rows → data.)
  3. Inspect the data. glimpse(), summary(), dim(), names(). Most “code” bugs are really data surprises.
  4. Reduce to a minimal example. Strip the pipeline back to the smallest piece that still fails.
  5. Test one fix. Change one thing, re-run, observe. Not three things at once.
  6. Document the verification. Note what the fix was and how you confirmed it — a row count, a spot check, a re-render.
NoteUsing AI during study (not during a closed exam)

When studying, AI is excellent for explaining an error or generating practice variants. Paste the exact error, your R and package versions, and the code you tried. Then verify the explanation against ?function_name and a small test — the same discipline the rest of the book taught. In a closed-book exam, the ladder above is what you carry in your head.

12.4 Active learning — exam practice

12.4.1 Activity 1 — Full pipeline, new dataset

Run the seven-step pipeline above on a different course dataset (Ontario forest stats, or the cleaned air-quality file). Produce one summary table and one figure.

12.4.2 Activity 2 — Break and fix

Introduce a deliberate bug (misspell a column, drop a library(), swap lat/lon). Use the debugging ladder to find and fix it. Write one sentence per rung.

12.4.3 Activity 3 — Reproduce from clean

Move your .qmd and data/ to a fresh folder and render. Does it work with no manual steps? If not, find the hidden dependency.

12.5 Where to go next

This course is a foundation. Natural next steps:

  • Statistical modelling — regression, mixed models, and growth-and-yield models in R (FRST 231 / 430 build directly on this).
  • Spatial data science — deeper sf, raster data with terra, and remote sensing for forest inventory.
  • Time series — trends, seasonality, and forecasting for climate and disturbance data.
  • Reproducible researchrenv, Git/GitHub, and parameterized Quarto reports for production work.

Recommended reading mixes canonical and diverse voices:

AI as a debugging companion

Across the book’s chapters, AI has been a verification companion, not an author: it explains errors, drafts starting code, and suggests alternatives — and you check every line against the data and the documentation. That habit, more than any single function, is what makes you employable in a world where code is cheap and judgment is scarce.

Instructor notes

  • Pacing. One session of review, one of Q&A / sample-exam walkthrough, one wrap-up. No graded lab this week.
  • Final exam covers import, clean, summarize, reshape, join, and visualize with R, tidyverse, and ggplot2 — weighted 30% per the syllabus. (Spatial mapping with sf is an optional extension and is not required on the exam.)
  • Accommodations. Confirm Centre for Accessibility arrangements before the exam (extended time, alternative formats, rest breaks). Where the timed format raises equity concerns, consider a take-home practical for the whole cohort, not just students with formal accommodations — see the Accessibility statement in the front matter.
  • Common stumbling points. Forgotten library() calls; data paths that work locally but not from a clean project; reading NA as zero.

Working with AI on this chapter

Use AI as a verification and debugging partner, not an answer machine. The goal is not to make AI do the work — it is to use AI to help you understand, check, and improve your own work.

Fill in the brackets and paste this into your AI assistant. It should explain and check, not hand you a final assignment answer:

“I am working on FRST 232, this chapter. My dataset is [file name] with columns [columns]. My task is [what you are trying to do]. I tried [paste your code or steps] and got [paste the exact output or error]. Explain what this does line by line, tell me whether it answers the question, and list the checks I should run to verify it. Do not give me the final answer.”

Verify it yourself — before trusting any result (yours or an AI’s):

  • the rows, columns, and units are what you expect;
  • the row count changes sensibly after a filter() or a join;
  • group sizes and summary values are plausible;
  • the figure answers the question and axis labels include units.

Common AI mistakes to watch for: inventing a column, function, or value that is not in your data; choosing a tool before reading the question; and (in Excel) suggesting the wrong cell range or forgetting to refresh a PivotTable.

When a reference solution is available, do not just copy it — use it to check your own work:

  • Expected result — does your output match the target shape and values?
  • Required components — did you include every step the task asked for?
  • If your result differs — say where it matched, where it differed, what caused the difference, how you fixed it, and what you learned.

Write a prompt that would guide an AI toward the reference solution without handing over the final answer. Include the dataset, the columns, the task goal, the output format you need, and the verification checks. Then paste (or summarise) the AI’s response and answer: did its approach match the book’s? did it miss a verification step? did it invent a column, function, or value? what did your group change after checking it?