Import an Excel file with readxl::read_excel() and a CSV file with readr::read_csv().
Inspect a tibble with dim(), names(), glimpse(), summary(), head(), and tail().
Count missing values per column with colSums(is.na(...)) and distinguish NA from 0.
Write a small data dictionary in Quarto next to the code that produced it.
Render a complete Quarto HTML report that another reader could open without R.
Expected output for this chapter
TipWhat you will hand in
A rendered Quarto HTML report named frst232_ch05_learner.html, containing:
The result of importing both bc_disturbance_reforestation.xlsx and on_forest_statistics_1.xlsx.
The inspection output — dim(), names(), glimpse(), summary(), head(), and tail() — for each file (the inspection moves taught in this chapter).
A missing-value summary for each file (one cell per column showing how many NA values it has).
A data dictionary table for the BC file, written by you in Markdown.
A short prose paragraph (two or three sentences) describing what is in each file.
The corresponding .qmd source file.
Notification that you have completed the Excel midterm in the lab session for this chapter (see Lab session — Excel midterm below).
A short group peer-review worksheet of another learner’s rendered HTML.
This list is the same as the rubric for the chapter assignment. Keep it in view while you work.
5.1 Where we are in the course
In Chapter 4 you:
Installed R, RStudio, and Quarto (or set up Posit Cloud).
Created the frst232/ project with data/, outputs/, R/, notes/.
Imported bc_disturbance_reforestation.xlsx with readxl::read_excel().
Computed five statistics (sum, mean, min, max, median) on bc$Reforestation_ha.
Confirmed those numbers match the values you saw in Excel for Chapter 1.
Chapter 5 deepens the R fundamentals: vectors, tibbles, column types, missing values, and your first full Quarto report.
5.2 Excel-to-R bridge — the inspection moves
From Chapter 4 you already know:
Excel inspection
R command
Returns
Status-bar Count
dim(bc) / nrow(bc)
Row and column count
Status-bar Sum
sum(bc$ColumnName)
Sum of one column
Status-bar Average
mean(bc$ColumnName)
Mean of one column
This chapter adds three more — the missing-value and type moves that Excel cannot do cleanly:
Excel pattern
R command
Returns
“Cells: blank or text? Hard to tell.”
glimpse(bc)
Each column with type and first values
“Count = 35 but 37 rows? Two cells are text.”
colSums(is.na(bc))
Number of missing values per column
“Format Cells → Number / Text”
class(bc$ColumnName)
The type (“numeric”, “character”, etc.)
By the end of this chapter, every move you used to do by looking at Excel is now a scripted R call.
5.3 Vectors — the building block
Almost everything in R is a vector: an ordered sequence of values, all of the same type.
# A vector of numbersyears <-c(1987, 1988, 1989, 1990, 1991)years
[1] 1987 1988 1989 1990 1991
# A vector of textspecies <-c("Pl", "Fd", "Sx")species
[1] "Pl" "Fd" "Sx"
# A vector of TRUE/FALSE valuesis_old <-c(TRUE, FALSE, TRUE, TRUE, FALSE)is_old
[1] TRUE FALSE TRUE TRUE FALSE
The c() function (c for combine) is how you build a vector. Type the values, comma-separated, inside c(...).
5.3.1 The four atomic types
Every value in R has a type. The four most common:
Type
Examples
R shorthand
logical
TRUE, FALSE, NA
<lgl>
integer
1L, 2L, 2026L
<int>
double
1, 3.14, -2.5
<dbl>
character
"Vancouver", "Pl"
<chr>
Numbers without an L suffix are <dbl> (double-precision) by default. Most numeric R work uses doubles.
5.3.2 Vectorized operations
Most R functions work on a whole vector at once, not one element at a time:
ha <-c(220000, 180000, 250000, 190000)# Each of these acts on the whole vectorsum(ha)
[1] 840000
mean(ha)
[1] 210000
min(ha)
[1] 180000
max(ha)
[1] 250000
length(ha)
[1] 4
# Arithmetic is vectorized too — element-by-elementha /1000# convert each value to thousands of hectares
[1] 220 180 250 190
ha +1000# add 1000 to every value
[1] 221000 181000 251000 191000
ha >200000# comparison returns a logical vector
[1] TRUE FALSE TRUE FALSE
That last result — ha > 200000 returns a vector of TRUE/FALSE — is the bedrock of every filter and condition you will write in R.
5.3.3 Subsetting with [
You can pull out specific positions from a vector with square brackets:
ha[1] # first value
[1] 220000
ha[c(1, 3)] # first and third values
[1] 220000 250000
ha[ha >200000] # only values greater than 200,000
[1] 220000 250000
That third example reads: “return the elements of ha where ha > 200000 is TRUE”. This is the logical-vector subsetting pattern — the conceptual ancestor of dplyr::filter() which you will meet in Chapter 6.
5.4 Tibbles — vectors arranged in columns
A tibble is a tidyverse-flavored data.frame: a rectangular table where every column is a vector and all columns have the same length.
When you ran:
bc <-read_excel(here("data", "bc_disturbance_reforestation.xlsx"),sheet ="Data")
…in Chapter 4, R built a tibble called bc. Each column of bc is a vector. The whole tibble is a list of vectors that R prints as a rectangle.
Look at the top of the printout. Just below the column names, you see things like <dbl>, <dbl>, … — the column types. A tibble always shows its column types when printed. That alone is reason enough to use tibbles over base R data.frames.
5.4.1 Tibble vs data.frame
For most purposes, a tibble is a data.frame. The differences that matter for this course:
Feature
data.frame
tibble
Prints
All rows by default (slow on big data)
First 10 rows only
Column types
Hidden until you ask
Always shown
Subset with [, "col"]
Returns a vector
Returns a tibble
Created by
read.csv(), data.frame()
read_excel(), read_csv(), tibble()
You can always convert in either direction: as_tibble(df) turns a data.frame into a tibble, and as.data.frame(tib) turns a tibble back into a data.frame. For tidyverse work, stay with tibbles.
5.5 Importing files
NoteAI prompt to check your work
Use this after your own attempt. It should check your reasoning, not hand you the answer:
“I imported [file] and got a tibble of [rows] x [cols]. Check whether the column types look right, whether any column that should be numeric came in as text (or a date as character), and which inspection commands I should run next. Do not hand me a finished import script.”
Two import functions cover almost every real-world file:
5.5.1readxl::read_excel()
bc <-read_excel(here("data", "bc_disturbance_reforestation.xlsx"),sheet ="Data")
Arguments to remember:
The first argument is the file path — always build it with here().
sheet = names the sheet to read. If you skip it, read_excel reads the first sheet.
skip = 3 skips the first 3 rows (useful when the file has a banner or title above the headers).
na = c("", "NA", "n/a") tells read_excel which strings to treat as missing.
5.5.2readr::read_csv()
# Example (you do not need to run this; we use it in Chapter 6).airdata <-read_csv(here("data", "airdata3.csv"))
Arguments to remember:
The first argument is the file path.
col_types = can lock in column types if read_csv guesses wrong (advanced — leave it off for now).
na = c("", "NA", "-9999") is useful for fields that use sentinel values for missing.
5.5.3 Other readr siblings (briefly)
read_tsv() — tab-separated.
read_delim() — generic, any delimiter.
All readr functions return a tibble. All accept a file path as the first argument.
5.6 Inspecting a tibble — the eight moves
Whenever you load a new file, run these eight commands. They take seconds and catch most problems.
summary(bc) # min / quartiles / mean / max for each numeric column
Fiscal_Year Clearcutting_ha Clearcutting_with_reserves_ha
Min. :1987 Min. : 7300 Min. : 923.9
1st Qu.:1996 1st Qu.: 28099 1st Qu.: 14044.9
Median :2005 Median : 76531 Median :121658.2
Mean :2005 Mean : 89774 Mean : 97152.6
3rd Qu.:2014 3rd Qu.:166369 3rd Qu.:155763.9
Max. :2023 Max. :217836 Max. :200699.1
Partial_cutting_ha Harvested_ha Natural_Disturbance_ha
Min. : 2502 Min. : 78961 Min. : 1448
1st Qu.: 5603 1st Qu.:190393 1st Qu.: 7562
Median : 9273 Median :204791 Median : 13908
Mean :12127 Mean :199053 Mean : 29035
3rd Qu.:18931 3rd Qu.:216302 3rd Qu.: 28733
Max. :31878 Max. :251557 Max. :218275
Total_Disturbance_ha Reforestation_ha
Min. :116942 Min. :189394
1st Qu.:207574 1st Qu.:207668
Median :224147 Median :219925
Mean :228089 Mean :223353
3rd Qu.:254544 3rd Qu.:237276
Max. :422092 Max. :274616
Each one answers a different question:
What you want to know
Use
How many rows × columns?
dim()
Just rows
nrow()
Just columns
ncol()
The column names
names()
What the first / last rows look like
head() / tail()
Column types + a preview of values
glimpse()
Distributions of numeric columns
summary()
glimpse() is the single most valuable function in this list. Make it your default first move on any tibble.
5.7 Column types — and why they matter
In the glimpse(bc) output above, you see types like <dbl> next to each column name. The type tells you what R thinks this column contains.
Type
Means
Example
<int>
integer (whole numbers, no decimals)
1987L, 2L
<dbl>
double (any real number)
216304.919
<chr>
character (text)
"Vancouver"
<lgl>
logical
TRUE / FALSE
<fct>
factor (categorical with a fixed set of levels)
rare on imported files
<date>
date
2024-09-15
If read_excel reads Fiscal_Year as <dbl> but you want it as <int>, you can coerce after import:
Most of the time the inferred types are fine. The only case where it matters is when something looks numeric but arrives as text (often because of an extra space or a stray character).
5.7.1 How to spot a type bug
If you compute sum(bc$Reforestation_ha) and get the wrong answer — or an error message like “non-numeric argument to binary operator” — the column is probably character. Check:
Use this after your own attempt. It should check your reasoning, not hand you the answer:
“colSums(is.na(df)) shows [which columns] have missing values. Check whether my plan to handle them ([drop / keep / na.rm = TRUE]) is appropriate for this dataset, and explain what could go wrong if I treated NA as zero. Do not decide the whole cleaning strategy for me.”
In R, missing values are represented by NA. NA is not zero. It is not an empty string. It is its own thing: a flag meaning “we do not know this value”. If that distinction is new to you, skim NA vs 0 (below) first — treating an unknown value as 0 silently corrupts every sum and mean.
5.8.1 Counting NAs in one column
is.na(bc$Reforestation_ha) # logical vector — TRUE for each NA
sum(is.na(bc$Reforestation_ha)) # how many NAs in this column
[1] 0
is.na() returns a logical vector of the same length as its input — TRUE for each NA, FALSE for each real value. Sum that vector to count the missing values (TRUE is treated as 1, FALSE as 0).
This is the fastest missing-value audit in R. One line. Every column. Always run it after import.
column
n_missing
Fiscal_Year
0
Clearcutting_ha
0
Clearcutting_with_reserves_ha
0
Partial_cutting_ha
0
Harvested_ha
0
Natural_Disturbance_ha
0
Total_Disturbance_ha
0
Reforestation_ha
0
For the BC silviculture file, every column shows 0 — there are no missing values, which matches what you saw in Excel for Chapter 1. (The Ontario file is the same — we will check in the exercises.)
5.8.3NA vs 0 — a habit worth forming
Situation
What to record
The variable was measured and the value really is zero
0
The variable was not measured
NA
The plot did not exist that year
NA
The measurement was below the detection limit
depends — see your data dictionary
Mixing these up is a silent bug class. “Mean reforestation” including 12 unmeasured years coded as 0 will be 12 zeros pulled into the average. Coded as NA, they would be skipped (if you use na.rm = TRUE).
5.9 Skipping NA in calculations
# If a column had NAs, the default behaviour is:mean(c(1, 2, NA, 4)) # returns NA
na.rm = TRUE is available on every aggregation function (sum, mean, min, max, median, sd). Add it whenever your data might have missing values. Forgetting it is the most common silent bug in first-year R.
5.10 A worked inspection — the BC file end-to-end
Here is the canonical inspection sequence on a freshly imported file:
Fiscal_Year Clearcutting_ha Clearcutting_with_reserves_ha
Min. :1987 Min. : 7300 Min. : 923.9
1st Qu.:1996 1st Qu.: 28099 1st Qu.: 14044.9
Median :2005 Median : 76531 Median :121658.2
Mean :2005 Mean : 89774 Mean : 97152.6
3rd Qu.:2014 3rd Qu.:166369 3rd Qu.:155763.9
Max. :2023 Max. :217836 Max. :200699.1
Partial_cutting_ha Harvested_ha Natural_Disturbance_ha
Min. : 2502 Min. : 78961 Min. : 1448
1st Qu.: 5603 1st Qu.:190393 1st Qu.: 7562
Median : 9273 Median :204791 Median : 13908
Mean :12127 Mean :199053 Mean : 29035
3rd Qu.:18931 3rd Qu.:216302 3rd Qu.: 28733
Max. :31878 Max. :251557 Max. :218275
Total_Disturbance_ha Reforestation_ha
Min. :116942 Min. :189394
1st Qu.:207574 1st Qu.:207668
Median :224147 Median :219925
Mean :228089 Mean :223353
3rd Qu.:254544 3rd Qu.:237276
Max. :422092 Max. :274616
# 6. Spot-check the verification identity from Chapter 1identical_check <-all.equal( bc$Harvested_ha + bc$Natural_Disturbance_ha, bc$Total_Disturbance_ha)identical_check
[1] TRUE
Six lines. Six confirmations. You now know:
The file has 37 rows and 8 columns (matches Chapter 1).
Column names match the ReadMe (you can compare them by eye).
Every column is <dbl> — no surprise text-as-number issues.
The summary() table tells you each column’s range.
No missing values anywhere.
The arithmetic identity from Chapter 1 still holds: Harvested + Natural = Total for every row.
That sequence is what professional R analysts do every time they open a new file. Do it on every dataset you touch.
5.11 First Quarto report
You have been reading Quarto documents through this whole book. Now you write one.
5.11.1 The four parts of a .qmd file
A Quarto file has four ingredients:
Part
Looks like
What it does
YAML header
--- block at the top
Tells Quarto how to render
Markdown text
Plain prose, headings, bullets
The reading content
Code chunks
```{r} ... ```
R code that gets executed
Inline code
`r expression`
One-line R inside prose
A minimal .qmd file:
---title: "My first Quarto report"format: html---# IntroductionThis report inspects the BC silviculture file.::: {.cell}```{.r .cell-code}library(readxl)library(here)bc <-read_excel(here("data", "bc_disturbance_reforestation.xlsx"),sheet ="Data")dim(bc)```::: {.cell-output .cell-output-stdout}```[1] 37 8```::::::The file has 37 rows.
Save as my_first_report.qmd. Click Render (or Ctrl+Shift+K / Cmd+Shift+K). You get a single self-contained HTML file.
5.11.2 Common chunk options
Chunk options go right after the ```{r} line, prefixed with #|:
::: {.cell}:::
Option
What it does
label:
Names the chunk (helpful when one errors)
echo: false
Hide the code in the rendered output (just show the result)
include: false
Run the code but hide both code and output (useful for setup)
message: false
Hide messages from R (e.g., the tidyverse load message)
warning: false
Hide warnings
eval: false
Show the code but do not run it
fig.width: 6
Plot width in inches (Chapter 9)
fig.cap: "..."
Caption for figures
5.11.3 What rendering does
When you click Render, Quarto:
Reads your .qmd file.
Runs every R chunk in order, from top to bottom, in a fresh R session.
Captures the output (text, tables, charts).
Stitches code + text + output into a single HTML file.
Saves the HTML alongside the .qmd.
The fresh session part is important: rendering does not use your interactive console. If a chunk depends on a variable that was only created in your console (and not in an earlier chunk), rendering will fail.
TipA reproducibility test
If your .qmd renders cleanly from a fresh R session, your work is reproducible. If it does not, your work depends on state that no one else can see — and that is the bug.
Restart R often. In RStudio: Session → Restart R (Ctrl+Shift+F10 / Cmd+Shift+F10). Then render. If it still works, you are good.
5.12 Writing a data dictionary in Quarto
A data dictionary lives next to your code. In Quarto, it is just a Markdown table:
| Column | Type | Unit | Description ||---|---|---|---||`Fiscal_Year`| integer | year | Fiscal year (April–March) ||`Harvested_ha`| double | hectares | Total area harvested || ... ||||
That renders as:
Column
Type
Unit
Description
Fiscal_Year
integer
year
Fiscal year (April–March)
Harvested_ha
double
hectares
Total area harvested
…
The point: the dictionary travels with the analysis. When you hand your .qmd to a colleague, they see the table and the code that built the numbers, in the same file.
TipStarter for the BC file
The BC silviculture file has these eight columns. Copy this table into your report and fill in the Type, Unit, and Description yourself (use glimpse(bc) for the types):
Column
Type
Unit
Description
Fiscal_Year
Clearcutting_ha
Clearcutting_with_reserves_ha
Partial_cutting_ha
Harvested_ha
Natural_Disturbance_ha
Total_Disturbance_ha
Reforestation_ha
File-summary template. After the dictionary, add two or three sentences that turn your inspection output into prose, e.g.:
“The BC silviculture file has 37 rows and 8 columns. The variables are numeric areas in hectares plus a fiscal-year integer, and the missing-value audit shows no missing values. The main variables describe harvested, natural-disturbance, total-disturbance, and reforested area by fiscal year.”
5.13 Active learning
5.13.1 Activity 1 — Vectors
ha <-c(220000, 180000, 250000, 190000, 210000)
In the console, compute:
the sum,
the mean,
the count of values greater than 200,000 (hint: sum(ha > 200000)),
the values greater than 200,000 themselves.
5.13.2 Activity 2 — Inspect two files
Run the six-move inspection on the BC silviculture file, then on the Ontario forest stats file. Note the differences:
How are the column types different from the BC file? Which file has any character columns?
5.13.3 Activity 3 — Missing-value audit
Run colSums(is.na(bc)) and colSums(is.na(ontario)). Are there any missing values? Confirm against what you saw in Excel for Chapters 1 and 2.
5.13.4 Activity 4 — Render your first Quarto report
Open the chapter learner file frst232_ch05_learner.qmd. Render it. Confirm the HTML opens and shows all expected output.
5.13.5 Activity 5 — Break and fix
In frst232_ch05_learner.qmd, deliberately misspell Reforestation_ha somewhere. Render. Read the error message carefully. Fix it. Re-render. The error message taught you something useful — what?
5.14 Lab session — Excel midterm
ImportantThe lab session for Chapter 5 is the Excel midterm
There is no separate practice lab this chapter. The lab session is given over to the Excel midterm, covering material from Chapters 1–3:
Importing and inspecting an Excel workbook (Chapter 1).
Cleaning, summarizing, and writing formulas (Chapter 2).
Visualization and PivotTables (Chapter 3).
The midterm is closed-AI but open-notes. Bring a laptop with Excel installed (or use the lab computers). The teaching team will provide a midterm-specific workbook at the start of the session.
What to do before the lab:
Re-read the Chapter 1, 2, and 3 Expected output callouts.
Make sure your laptop has Excel working.
Bring your bc_disturbance_reforestation.xlsx and on_forest_statistics_1.xlsx files in case the midterm references them.
What is NOT on the midterm:
R code.
Quarto rendering.
Anything from Chapter 4 or Chapter 5.
The R material is assessed through homework exercises, the chapter quizzes, and the final exam at the end of the term.
5.15 Group activity — Peer review of Quarto reports
Outside the midterm session, run this peer-review activity at any time during the chapter (e.g., during a lecture session).
NoteGroup: Peer review a rendered HTML report
Work in pairs (or threes). Each member should have a rendered frst232_ch05_learner.html to share.
Task 1 — Swap. Each person sends their rendered HTML to the person on their left. Open it in a browser.
Task 2 — Score each other’s report using this rubric:
Criterion
0
1
2
Renders without error
Did not render
Renders with warnings
Renders cleanly
dim(), names(), glimpse() visible
Missing
Present but unclear
Clearly visible
Missing-value summary present
Missing
Present but unexplained
Present with note
Data dictionary table
Missing
Incomplete
Complete and readable
Two-sentence prose summary
Missing
Vague
Specific to the file
Task 3 — Give one piece of constructive feedback in writing. What is the strongest part of the report? What would you change?
Task 4 — Submit. Submit your peer-review rubric (with your own name and the author’s name) to the course site.
This activity is designed to fit inside a single lecture session.
5.16 Exercises
These are the take-home exercises that go with this chapter.
Run the six-move inspection sequence on the BC silviculture file. Copy the output of each function into your .qmd file inside R chunks.
Run the same six-move inspection on the Ontario file. Compare the two outputs. Which file has character columns? How many?
Compute the missing-value summary for both files using colSums(is.na(...)). Are there any missing values?
Build a data dictionary table for the BC silviculture file in your .qmd, in the format shown in this chapter. Each row should have Column, Type, Unit, Description.
Use vectorized arithmetic to compute the ratio Reforestation_ha / Total_Disturbance_ha for every year. Save the result as a new vector. How many years had a ratio greater than 1?
Use summary(bc) to find the median of Total_Disturbance_ha. Confirm it matches the value you would compute with median(bc$Total_Disturbance_ha).
Restart R (Session → Restart R). Re-render your frst232_ch05_learner.qmd from scratch. Confirm it renders without errors. (If it does not, fix what depends on hidden console state.)
Optional, harder. Write a tiny R function called inspect_file() that takes a tibble and prints dim, names, glimpse, and colSums(is.na(...)) all at once. Apply it to bc and ontario.
5.17 Optional, advanced
5.17.1 Lists — the catch-all data structure
A list in R is like a vector except its elements can be of different types. A tibble is technically a list of equal-length vectors.
You will use lists in the optional spatial-data material, where each “row” is a complex geometry.
5.17.2 Factors — categorical variables with order
A factor is a vector with a fixed set of allowed values (levels). Useful when you want ordered categories (small < medium < large) or when a categorical variable has a known set of valid values.
This is overkill for clean BC and Ontario files. It becomes useful when you import messy real-world CSVs in Chapter 6.
5.18 A glimpse ahead: From inspection to cleaning
In Chapter 6 you move from inspecting to cleaning. The core moves of dplyr:
What you want to do
Excel (Ch 2)
dplyr verb (Ch 6)
Keep some rows
AutoFilter
filter()
Add a new column
=A2+B2 in a new column
mutate()
Sort
Data → Sort
arrange()
Keep only some columns
Delete columns
select()
Group and summarise
PivotTable
group_by() |> summarise()
Look up from a reference
VLOOKUP
left_join()
Recode values
IFS
case_when()
Every Excel cleaning move you learned in Chapter 2 has a one-line dplyr equivalent. The names are short, the pattern is consistent, and once you know the six verbs above, you can clean almost any real-world dataset.
Chapter 6 walks through each verb on the BC air-quality dataset (airdata3.csv) — a messier file than what you have seen so far, with actual missing values, mixed date formats, and the kind of problems that real forestry data presents.
5.19 Self-assessment quiz
Click the answer you think is correct. The right answer turns green; wrong answers turn red and the correct one is revealed. Explanations appear below each question.
1. What is a vector in R?
A graphical chart
An ordered sequence of values, all of the same type
A file format
The same as a list
Vectors are the building block of R. A vector has a single type — logical, integer, double, or character — and a length.
2. c(1987, 1988, 1989) is a vector of which type?
integer ()
double ()
character ()
logical ()
Numbers without an L suffix are doubles by default. c(1987L, 1988L, 1989L) would be integer.
3. A tibble is —
A new programming language
A tidyverse-flavored data.frame — a rectangular table where each column is a vector
A type of chart
A statistical model
Tibbles are the tidyverse's default rectangular structure. They print better than data.frames and show column types.
4. In a tibble printout, you see <dbl> under a column name. What does that mean?
read_csv() from the readr package (part of tidyverse) reads comma-separated files into a tibble. read_excel() is for .xlsx files only.
6. colSums(is.na(bc)) returns —
The sum of every column
A named vector giving the number of missing (NA) values in each column
Whether the file is complete
An error
is.na(bc) returns a logical matrix (TRUE for each NA). colSums() adds the TRUEs per column. One line, complete missing-value audit.
7. NA in R means —
Zero
An empty string
Missing — the value is unknown
North America
NA is its own value type. Mixing it up with 0 is a silent bug: an unmeasured year recorded as 0 will be pulled into averages; recorded as NA, it will be skipped if you use na.rm = TRUE.
8. mean(c(1, 2, NA, 4)) returns —
2.33
NA
An error
7
By default, R refuses to silently ignore missing values. Add na.rm = TRUE to skip them: mean(c(1,2,NA,4), na.rm = TRUE) returns 2.33.
9. Which function gives the most useful one-line preview of every column in a tibble (name, type, and first values)?
dim()
summary()
glimpse()
head()
glimpse() shows every column, its type, and a preview of the first values — like a richer version of Excel's status bar.
10. summary(bc) on a tibble of numeric columns returns —
The R version
For each column: Min, 1st Quartile, Median, Mean, 3rd Quartile, Max (and NA count if any)
Only the column names
A chart
summary() produces the five-number summary plus the mean for each numeric column. For character columns it just notes the type.
11. A Quarto .qmd file has four main parts. Which is NOT one of them?
YAML header
Markdown text
Code chunks
A standalone CSS file
The four parts: YAML header, Markdown text, code chunks (```` ```{r} ... ``` ````), and inline R code (`r ...`). CSS can be customized but is not a required part.
12. Inside a Quarto code chunk, what does #| echo: false do?
Skips the chunk entirely
Hides the code in the rendered HTML; still shows the output
Hides the output; still shows the code
Stops the chunk after one second
echo: false shows the output but hides the source code. Use it for the setup chunk and for any code your reader does not need to see.
13. When you click Render in RStudio, Quarto —
Just exports the .qmd file as text
Runs every code chunk in order in a fresh R session and produces a self-contained HTML (or PDF) file
Asks an AI to render the document
Saves the file to the cloud
Render = run every chunk top-to-bottom in a fresh session + assemble code, text, and output into one HTML. The fresh session is what makes rendering a reproducibility test.
14. bc$Fiscal_Year is —
A tibble
A vector — the Fiscal_Year column of the bc tibble
An error
A function
Using $ on a tibble returns a single column as a vector. bc[, "Fiscal_Year"] on a tibble returns a one-column tibble, not a vector.
15. ha > 200000 returns —
A single number
A logical vector — TRUE or FALSE for each element of ha
An error
The number of values greater than 200,000
Comparison in R is vectorized: each element of ha is compared to 200000, returning a parallel logical vector. To count how many are TRUE, wrap with sum().
16. Inside a Quarto chunk, you write code that uses a variable created only in your interactive console. When you Render, what happens?
It works fine — Quarto sees the console
It fails — Render runs in a fresh R session that does not see console state
It works only on Posit Cloud
It runs the chunk twice
Render uses a fresh session. Everything the document depends on must be created inside a chunk. This is the whole point of Quarto being reproducible.
17. The Excel midterm is held —
Online, asynchronously
During the Chapter 5 lab session
At the end of the term
There is no midterm
The Chapter 5 lab session is given over to the midterm. The midterm covers Chapter 1, 2, and 3 (Excel) material.
18. Which is the closest equivalent of Excel's status-bar "Count = 35 but I selected 37 cells" warning?
summary(bc)
dim(bc)
colSums(is.na(bc)) — a non-zero entry means that column has missing or non-numeric cells
head(bc)
In Excel, status-bar Count < selection size meant some cells were text-not-number. In R, colSums(is.na(...)) per column tells you the same thing more precisely.
19. What is the main difference between a tibble and a data.frame in this course?
Tibbles cannot hold text
Tibbles print better (first 10 rows, column types shown) and are the default in tidyverse imports
data.frames are faster
There is no difference
For most operations, a tibble IS a data.frame (it inherits from it). The differences matter for printing, subsetting (tibble[, "col"] returns a tibble, not a vector), and consistency with tidyverse functions.
20. Reflection. Now that you have rendered a Quarto report, how does the experience compare to submitting an Excel workbook? What is easier? What is harder?
Common reflections: easier — every number in the report is traceable to the code that produced it · harder — getting the YAML header right the first time · easier — re-running with a new dataset doesn't require redoing every chart and table · harder — error messages on render are scary at first · aha moment — the Quarto report carries its own data dictionary AND its own analysis, in one file.
5.20 AI as a debugging companion
TipUseful prompts for this chapter
“My Quarto document fails to render with the error object 'bc' not found. The variable works fine in my console. What might be wrong?” (Hint: fresh-session rendering.)
“Explain this output of glimpse(bc) line by line:” (paste the glimpse output)
“I imported a CSV with read_csv and one of my numeric columns was read as character. What’s the most common cause and how do I fix it?”
5.20.1 A prompt template for the rest of the course
When you ask AI for help with R code, include four things:
Your R version (R.version.string).
The packages you have loaded (the top of your .qmd).
The exact code you ran.
The exact error message (copy-paste, do not paraphrase).
Without these four, the answer will be too generic to use.
5.20.2 Verifying AI’s answers
After AI suggests a fix, check:
Run the suggested code in your console. Does it work?
Does the result match what you expect from glimpse(), summary(), or a manual count?
If AI rewrites a chunk for you, render the whole document afterwards to confirm nothing else broke.
5.20.3 When not to use AI
Do not let AI write your data dictionary. The dictionary describes your analysis decisions.
Do not let AI hide an unexplained behaviour with a try() or suppressWarnings(). Warnings exist for a reason — read them.
5.21 Reading
R for Data Science — chapter 2 Workflow: basics and chapter 8 Data import. Free at https://r4ds.hadley.nz/.
The chapter has two big new ideas: vectors / tibbles (the R type system) and Quarto (the new deliverable format). Spend time on both; do not rush past the YAML header.
The missing-value audit (colSums(is.na(bc))) is the single most useful one-line R command in this chapter. Use it in your live demo.
Quarto rendering must succeed before submission. Many learners will hand in a .qmd that does not render. Build the “render from a fresh session” check into the grading rubric.
5.21.1 Excel midterm logistics
The lab session for this chapter is the midterm.
Material is Chapter 1, 2, 3 Excel only.
Recommended midterm structure: one 90-minute Excel deliverable (single workbook) with sections on importing, cleaning, summarizing, and a PivotTable. Provide a starter file at the start of the session.
Closed-AI, open-notes. Learners may use the chapter materials but not generative-AI tools.
TAs should be prepared for “my Excel won’t open” support questions at the start of the session — have a backup computer ready.
5.21.2 Expected output checklist (matches the top-of-chapter callout)
frst232_ch05_learner.html (rendered) with imports for both BC and Ontario files.
frst232_ch05_learner.qmd (source).
Excel midterm completed in the lab session.
Peer-review worksheet (during the lecture peer-review activity).
5.21.3 Materials provided alongside this chapter
File
Purpose
bc_disturbance_reforestation.xlsx
Raw BC silviculture (from Ch 1, unchanged).
on_forest_statistics_1.xlsx
Raw Ontario forest stats (from Ch 2, unchanged).
frst232_ch05_learner.qmd
Quarto starter with TODO chunks for each exercise.
frst232_ch05_solutions.qmd
Completed Quarto document. Do not distribute to learners.
quiz_ch05.html
Standalone interactive quiz.
5.21.4 Common stumbling points
“Object not found on render.” Almost always: the variable was created in the console, not in a chunk. Solution: Restart R, then re-render.
YAML errors. Quarto is fussy about the YAML header. Indentation must use spaces (no tabs); the --- fences must be on their own lines.
library(tidyverse) not run in the setup chunk. Then the first chunk that uses a tidyverse function errors. Make sure the setup chunk has include: false (runs but is hidden) and loads every package the document needs.
NA confusion. Some learners conflate NA with 0 when they see a “missing value count of 0” — they think “so there are zero values?”. Walk through the distinction explicitly.
Excel-to-R bridge: The most important sentence in this chapter is “every move you used to do by looking at Excel is now a scripted R call”. Refer back to it whenever a learner asks why a particular function exists.
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.
NoteAI prompt to check your 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.
TipCompare your solution with the reference
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.
TipAI replication challenge
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?