Exercise: pricing the Vienna hotel market

Session 2 — Applied Data Science 2026

Published

21 09 2026

NoteThe setting

You are on the analytics team of a hotel group planning its first property in Vienna. The board wants four questions answered before the site meeting, and they want the reasoning written down.

Alone or in pairs.

Your task: Create a project with a quarto file session02.qmd. Make sure your directory structure is as discussed earlier. Answer each question with code plus one or two sentences of interpretation. The sentences matter as much as the code.

library(tidyverse) # Standard packages for data transformation
library(flextable) # For printing nice tables in Quarto
hotels <- read_csv("data/hotels-vienna.csv")
ImportantStart by cleaning and tiering

Use this version of the data throughout, exactly as in the live coding:

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"))
  )

Say in one sentence what you removed and why.

Use star_tier, not stars, everywhere below. The raw column has half stars and eight ragged groups; star_tier has five usable ones and is a proper factor.

R’s round() uses banker’s rounding — it sends halves to the nearest even number:

round(2.5)   # 2, not 3
round(3.5)   # 4
round(4.5)   # 4, not 5

So round() would push 2.5 and 4.5 down but 3.5 up. Inconsistent, and it gives you no warning. floor(x + 0.5) rounds halves up every time, which is what we want when binning a rating into tiers.

Banker’s rounding exists for a good reason — it stops rounding errors piling up in one direction when you sum thousands of values. It is just the wrong tool here.

1 Q1 — What does the market look like?

How many properties are there of each accommodation_type, and what is the median price of each? Sort from most to least expensive.

group_by(), then summarise() with n() and median(), then arrange(desc(...)).

2 Q2 — Is the upper segment worth entering?

The board defines the upscale segment as hotels — not apartments — in the 4 stars or 5 stars tier.

How many are there, what is their median price, and how does that compare with the rest of the hotel market?

star_tier %in% c("4 stars", "5 stars") picks the segment. Then either two pipelines with different filter() conditions, or one with a mutate() that creates a segment variable and a group_by() on it.

3 Q3 — Does a good guest rating pay?

Among hotels only, compute the median price for each band of guest rating: 4 and below, above 4 up to 4.5, and above 4.5.

State how many hotels you dropped because rating is missing.

mutate() with cut(rating, breaks = c(0, 4, 4.5, 5)), then group and summarise. sum(is.na(...)) for the dropped count.

cut() closes each interval on the right, so a hotel rated exactly 4.5 lands in the middle band. Label your bands accordingly.

4 Q4 — Where should we build?

Using band, and looking only at hotels in the 4 stars tier, work out the median price in each band. Report n alongside the median.

Then answer the board’s actual question in one sentence: if we are building a four-star hotel, how much more per night do central hotels charge their guests?

Warning

Be careful how you phrase it — in two ways.

You are describing what existing hotels charge their guests, not what would happen if you moved a building. And you are on the revenue side: a central site also costs the group more to acquire, and nothing in this data says anything about that.

You will not be able to answer it. Work out why from the n column, and write one sentence saying what you would need in order to answer it.

5 Q5 — Your own question

Now you decide. Ask something of this data the board has not asked, answer it with a pipeline, and make one chart that supports your answer.

Some directions, if you want a nudge:

  • Do discounted hotels (offer == 1) differ systematically from the rest?
  • Is the neighbourhood variable telling you anything distance does not?
  • Do the hotels with missing ratings look different from the rated ones?
  • Does distance_alter (distance to the second landmark) change the picture?
NoteThis is the part that matters

Questions 1–4 test whether you can use the verbs. Question 5 tests whether you can think with them. That is the actual skill, and it is what the reports will ask for.

6 If you finish early

  1. In the live coding we found that the location premium was about €16 for three-star hotels and about €43 for four-star ones. Check whether the same holds for guest rating instead of stars: does a good rating command a bigger premium in the centre or on the outskirts?

  2. Rewrite one of your pipelines without the pipe, using nested function calls. Then decide which version you would rather read in six months.

  3. Rebuild star_tier using round(stars) instead of floor(stars + 0.5) and compare the two with count(). Which hotels move tier, and how many?

7 Take-home until session 3

Important
  1. Finish this sheet if you did not get through it — these pipelines come back
  2. Read R for Data Science, ch. 3 — about 30 minutes
  3. Print the dplyr cheat sheet and keep it next to your laptop

Session 3 is Quarto end to end, and Report 1 is issued at the end of it.