four_star <- only_hotels |>
filter(star_tier == "4 stars") |>
group_by(band, .drop = FALSE) |>
summarise(n = n(), median_price = median(price), .groups = "drop")Exercise: the board briefing
Session 3 — Applied Data Science 2026
The analysis you did last week convinced the board. Now they want it as a document — something they can read in the meeting, forward to a colleague, and come back to in six months.
They have asked one question: where should we build our four-star hotel, and how much more can we charge our guests per night for a central one?
You already know the answer. This week is about producing a document that still gives the right answer after somebody changes the data.
Alone or in pairs.
Your task: Build one Quarto document,
session03.qmd, in your project. It has to satisfy every requirement in Section 8. Work through the questions below in order — each one adds a piece.
In your applied-data-science project, create session03.qmd and give it a setup chunk. Note the three new things: library(here), the i_am() line, and here() in the path.
```{r}
#| label: setup
#| include: false
library(tidyverse) # Standard packages for data transformation
library(flextable) # For printing nice tables in Quarto
library(here) # Paths that work on any machine
i_am("session03.qmd") # where this document sits in the project
hotels <- read_csv(here("data", "hotels-vienna.csv"))
hotels_clean <- hotels |>
filter(city_actual == "Vienna", price < 1000) |>
mutate(
band = cut(distance, c(-0.1, 1, 2, 3, 100),
labels = c("<1 km", "1-2 km", "2-3 km", ">3 km")),
star_tier = factor(floor(stars + 0.5), levels = 1:5,
labels = paste(1:5, "stars"))
)
only_hotels <- hotels_clean |>
filter(accommodation_type == "Hotel")
```If a path fails, the problem is almost never here() — it is that you opened the .qmd directly instead of opening the project. Close everything and open applied-data-science.Rproj first. i_am() will say so in as many words.
Every path in this document goes through here(). Not one read_csv("data/…") anywhere, and no setwd().
1 Q1 — The header
Write the YAML header. It must produce:
- a title, your name as author, and a date
- a table of contents
- numbered sections
- HTML output
- no warnings or package messages anywhere in the finished document
Four top-level keys: title, author, date, format. Then execute: with warning: false and message: false.
Under format:, html: goes one level in, and the options for it one level in again. Count your spaces — and use spaces, never tabs.
2 Q2 — Give it a structure
A briefing the board can navigate. Using Markdown only — no R in this question:
- at least three
##sections, in an order that makes sense to a reader - the key finding marked with bold
- one bulleted list somewhere it earns its place
Then render and check that the table of contents you asked for in Q1 actually lists your sections.
## Section name on its own line. **bold** around the words you want emphasised. A list is lines starting with - — with a blank line before the first one, or Markdown runs it into the paragraph above.
Start at ##, not #: the # level is the document title, and the YAML already supplies that.
3 Q3 — The answer, with nothing typed
Write the opening paragraph of the briefing. It answers the board’s question and it contains at least four numbers: how many four-star hotels there are, what the central ones charge, what the ones one to two kilometres out charge, and the difference.
Every one of those numbers is inline R code. If you have typed a digit into a sentence, that is the mistake this question is about.
First a chunk that computes the summary table and stores it:
Then pull single values out of it in the text, with `r four_star$median_price[four_star$band == "<1 km"]`.
A small helper in your setup chunk makes the prose readable:
eur <- function(x) {
digits <- if (isTRUE(all.equal(x, round(x)))) 0 else 2
paste0("€", formatC(x, format = "f", digits = digits))
}It gives €138 for a whole number and €88.50 for one that is not, so your prose never says €138.00 — or, worse, rounds €88.50 down to €88.
Be careful how you phrase the conclusion — the same trap as last week. You are describing what existing hotels charge, not what a location is worth.
4 Q4 — A table you can refer to
Show the summary table properly, with a caption, and refer to it from your text by name.
Two chunk options, then @tbl- plus your label in the prose:
four_star |> flextable() |> theme_vanilla() |> autofit()The label has to start with tbl-. Without the caption there is nothing to number, and your reference will render as ?tbl-fourstar.
5 Q5 — A figure you can refer to
Add one chart that supports the paragraph you wrote in Q3, with a caption, and refer to it by name as well.
Same pattern, fig- instead of tbl-:
ggplot(four_star, aes(x = band, y = median_price)) +
geom_col(fill = "#69AACD") +
labs(x = "Distance to city centre", y = "Median price per night (EUR)") +
theme_classic()The caption says what the reader is looking at. The title, if you add one, says what they should conclude from it. “Median price by distance band” is a caption. “The location premium is concentrated in the first kilometre” is a title.
Both are useful. They are not the same sentence.
6 Q6 — Cite the data
The Vienna hotels data comes from Békés and Kézdi (2021). Cite it properly.
Create a file references.bib in your project folder containing:
@book{bekes2021data,
title = {Data Analysis for Business, Economics, and Policy},
author = {Békés, Gábor and Kézdi, Gábor},
year = {2021},
publisher = {Cambridge University Press},
address = {Cambridge},
url = {https://gabors-data-analysis.com/}
}Add bibliography: references.bib to your YAML, write [@bekes2021data] in the text, and end the document with:
## References {.unnumbered}
::: {#refs}
:::7 Q7 — Prove it reproduces
- Session → Restart R (or Ctrl/Cmd + Shift + F10)
- Render, without running anything in the console first
- If it fails, the error tells you what was living in your environment rather than in your document. Fix it and repeat.
Then the real test. Swap with the person next to you: copy your whole project folder to them, take theirs, and render it. Anything that only worked on the author’s laptop shows up in the next ninety seconds.
- An absolute path, or
setwd()somewhere near the top - A package used but never
library()-ed in the document - An object created in the console three hours ago and never in a chunk
- The data file not actually being in the project’s
data/folder View()orinstall.packages()left in a chunk — the render has no panes and nobody to click, soView()shows nothing and says nothing
If your R is set up in German the messages come in German — Objekt 'four_star' nicht gefunden is case 3. Same error, same fix.
8 The checklist
Before you say you are done, your document must:
Report 1, issued at the end of today, is a longer version of exactly this document. Everything on the list above is a requirement there too — so getting it right now is the work, not extra work.
9 If you finish early
9.1 a) The messy import
data/hotels-vienna-messy.csv holds nine columns of the same data, exported from a German Excel. Everything that can be wrong with a delivered file is wrong with it.
Read it into R so that those nine columns come out right — split apart properly, numbers stored as numbers, umlauts intact, missing ratings actually missing — and prove it by joining it to the clean file and comparing.
Start by opening it in a text editor and looking at it.
- What separates the columns? (
read_csv2()rather thanread_csv()) - What separates the decimals?
- What has happened to the letter ä? (
locale(encoding = ...), and the file is ISO-8859-1) - What is written in the rating column where a rating is missing? (
na = ...)
Then janitor::clean_names() for the column names, which contain spaces, brackets and an umlaut. Install janitor if you have not already.
9.2 b) The boundary you chose
You defined “central” as within one kilometre. Re-run your analysis with the first band split at half a kilometre — c(-0.1, 0.5, 1, 2, 3, 100).
Re-render, and report what happened to the numbers in your prose. How much did you have to edit by hand?
Then write two sentences on which of the two answers you would give the board, and why.
9.3 c) An appendix that says what you ran
Add a final section to your report:
sessionInfo()Then answer, in two or three sentences: what does this actually guarantee about somebody else reproducing your result — and what does it not?
9.4 d) The same document as a PDF
Last, and only if the rest is done. Everything above renders to HTML, which is all this course requires. Getting the same document out as a PDF costs one YAML entry and one installation — and the installation is why this is optional rather than part of Q1.
- In the Terminal tab, next to the Console, run
quarto install tinytexand let it finish. It is a self-contained LaTeX installation, it takes a few minutes, and you never think about it again. - Add a
pdf:entry alongsidehtml:underformat:in your YAML. - Render. The Render button now has a dropdown; the Terminal command
quarto render session03.qmdbuilds both at once.
Then compare the two files side by side and write down two things that came out differently. There are more than two.
Indentation, again:
format:
html:
toc: true
pdf:
toc: false
geometry: margin=1inIf the render fails complaining about LaTeX, the install in step 1 has not finished. Wait for it.
10 Debrief
We look at two or three of your documents on the projector — including, if anybody is brave, one that failed the neighbour test. That failure is more useful than a document that worked.
11 Report 1
Individual, pass/fail, and required for exam admission. The task sheet and its data are on Moodle. You submit two files there: the rendered report and a zip of your project folder, because it has to reproduce on my machine.
Due Sunday 18 October, 23:59. There is no class on 5 and 12 October, so the three weeks are genuinely free — but the first render on somebody else’s machine is always the one that fails. Start this week.
12 Take-home until session 4
- Report 1
- Read R for Data Science, ch. 28 “Quarto”
- Optional but genuinely useful: the messy import in Section 9.1. Real data arrives in that state more often than not. And if you did not get to Section 9.4 in the session, the LaTeX installation is a good thing to have running in the background one evening.
- If anything from the first three sessions felt shaky, the two free weeks are the last comfortable moment to fix it
Session 4 is Git and GitHub — version control, entirely through the RStudio interface, with no command line.