Exercise: the board briefing

Session 3 — Applied Data Science 2026

Published

28 09 2026

NoteThe setting

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.

ImportantStart here

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:

four_star <- only_hotels |>
  filter(star_tier == "4 stars") |>
  group_by(band, .drop = FALSE) |>
  summarise(n = n(), median_price = median(price), .groups = "drop")

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.

Warning

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:

Table 1: Four-star hotels by distance to the city centre.
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

ImportantDo not skip this one
  1. Session → Restart R (or Ctrl/Cmd + Shift + F10)
  2. Render, without running anything in the console first
  3. 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.

  1. An absolute path, or setwd() somewhere near the top
  2. A package used but never library()-ed in the document
  3. An object created in the console three hours ago and never in a chunk
  4. The data file not actually being in the project’s data/ folder
  5. View() or install.packages() left in a chunk — the render has no panes and nobody to click, so View() 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:

NoteThis is also the Report 1 checklist

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.

  1. What separates the columns? (read_csv2() rather than read_csv())
  2. What separates the decimals?
  3. What has happened to the letter ä? (locale(encoding = ...), and the file is ISO-8859-1)
  4. 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.

  1. In the Terminal tab, next to the Console, run quarto install tinytex and let it finish. It is a self-contained LaTeX installation, it takes a few minutes, and you never think about it again.
  2. Add a pdf: entry alongside html: under format: in your YAML.
  3. Render. The Render button now has a dropdown; the Terminal command quarto render session03.qmd builds 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=1in

If 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

ImportantIssued at the end of this session

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

Important
  1. Report 1
  2. Read R for Data Science, ch. 28 “Quarto”
  3. 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.
  4. 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.