Exercise: explain it, then predict it

Session 8 — Applied Data Science 2026

Published

16 11 2026

NoteThe setting

The board has two questions about Vienna. Does guest rating change the picture of the location premium? And what price could a new four-star hotel, two kilometres from the centre and rated 4.2 by its guests, charge?

Alone or in pairs. Two parts: Part A and Part B.

ImportantStart here

Create session08.qmd in your project. Nothing new to install: broom comes with the tidyverse.

```{r}
#| label: setup
#| include: false
library(tidyverse)
library(broom)
library(here)

i_am("session08.qmd")

prices   <- read_csv(here("data", "hotels-europe_price.csv"))
features <- read_csv(here("data", "hotels-europe_features.csv"))

vienna <- prices |>
  filter(year == 2017, month == 11, weekend == 0) |>
  inner_join(features, by = "hotel_id") |>
  filter(city == "Vienna", city_actual == "Vienna",
         accommodation_type == "Hotel",
         price < 1000, stars >= 1.5,
         !is.na(rating)) |>
  mutate(
    star_tier = factor(paste(floor(stars + 0.5), "stars")),
    star_tier = relevel(star_tier, ref = "3 stars"),
    central   = distance <= 1,
    ln_price  = log(price)
  )

rmse <- function(actual, predicted) {
  sqrt(mean((actual - predicted)^2))
}
```

One difference from the live coding: hotels without a guest rating are dropped, so that every model in Part B is fitted on the same hotels.

Part A — Explaining

A1 · Add the rating

Fit ln_price ~ distance + star_tier + rating. Then write two sentences, one for rating and one for distance, in this form:

Holding … constant, the model estimates an average association of about …% higher/lower prices per additional …

Each part of the sentence matters: holding … constant (the other variables in the model — ceteris paribus), the model estimates (it is a property of this model, not a law of nature), on average (individual hotels deviate), association (not an effect).

Did the distance coefficient change compared with the model without rating? In which direction, and why might that be?

tidy(model) gives the coefficients. On the log scale, a coefficient b multiplies the price by exp(b): exp(b) - 1 is the percentage change.

A2 · The location premium, once more

Fit ln_price ~ central, then ln_price ~ central + star_tier, then ln_price ~ central + star_tier + rating. Turn each centralTRUE coefficient into a percentage, and write the last one as a sentence in the form from A1. What happens to the premium as you compare more like with like? Is the last number the effect of a central location?

Part B — Predicting

B1 · Four models, one test

Split the data:

set.seed(1)
train <- vienna |> slice_sample(prop = 0.8)
test  <- vienna |> anti_join(train, by = "hotel_id")

Fit these four models on train and compute the RMSE on train and on test for each:

Model Formula
m1 ln_price ~ distance
m2 ln_price ~ distance + star_tier
m3 ln_price ~ distance + star_tier + rating
m4 ln_price ~ poly(distance, 5) + star_tier + poly(rating, 3)

Put the results in one table. Which model fits the training data best? Which predicts the test data best?

For one model:

m1 <- lm(ln_price ~ distance, data = train)
rmse(train$ln_price, predict(m1, newdata = train))
rmse(test$ln_price,  predict(m1, newdata = test))

Four times — or write a small function that takes a formula and returns both numbers, and apply it to all four formulas with purrr::map(), as in the live coding.

B2 · Price the new hotel

Use the model that predicts best to price the board’s hotel: two kilometres, four stars, rating 4.2.

new_hotel <- tibble(distance = 2, rating = 4.2,
                    star_tier = factor("4 stars", levels = levels(vienna$star_tier)))

Give the predicted median price in euros, and say how far off a prediction from this model typically is, using its test RMSE: between which two prices does the actual price typically lie?

exp(predict(model, newdata = new_hotel)) gives the median price. A test RMSE of r on the log scale means a typical error by a factor of exp(r) — up and down: the typical range runs from the prediction divided by exp(r) to the prediction multiplied by exp(r).

B3 · Change one number

Run B1 again with set.seed(2026) instead of set.seed(1). Does the same model win? What does that tell you about choosing a model with one train/test split?

If you finish early

a) Overfit on purpose

Repeat the polynomial experiment from the live coding for rating instead of distance: ln_price ~ distance + star_tier + poly(rating, d) for d from 1 to 8. Plot training and test RMSE.

b) Explain or predict?

For each, decide whether it is an explaining or a predicting question, and which number you would look at:

  1. What should the group charge for its new hotel?
  2. Do guests pay for good reviews, or only for stars?
  3. Which hotels in the data are priced unusually high for what they offer?