Capstone dossier · the hedonic location premium in Amsterdam menu prices
Author
Dei Martinez Elurbe
Published
July 1, 2026
Dossier, not the essay. This is the backstage layer behind one act of the capstone: written as the investigation it was, numbers from the canonical artifacts, every statistical term glossed on first use. It is meant for technical readers; the capstone essay is the artifact aimed at a general audience. Corpus: Snapshot · June 2026, ~900 priced venues, roughly 4 in 10 of Amsterdam’s ~2,028 restaurants.
The verdict
Amsterdam restaurants in tourist territory charge a real, measurable location premium, and it is smaller than everyone believes. Holding the kind of dish and the kind of restaurant constant, one big step deeper into tourist Amsterdam adds +4.2% to a dish’s price (90% credible interval +1.8% to +6.6%; the probability the premium is positive at all is 99.8%). Across the city’s full range that is an ~11-point spread: Centrum sits about +3.2% above what a city-average location would charge, Zuidoost about −7.7% below.
Two qualifications carry the real story. First, the premium follows tourist beds, not tourist photo spots. It rides on hotel and Airbnb density; proximity to the Dam or the Rijksmuseum contributes nothing on its own. Second, this number is the floor, not the ceiling of what a visitor experiences. It is the part of the markup you cannot escape by choosing the same kind of restaurant. The larger part of “the centre is expensive” runs through which restaurants and which menus stand there. That composition channel is deliberately removed by this design, and the variance decomposition (dossier D4) shows it dominates everything else.
Figure 1: The headline geography: modelled location premium by wijk (city district), venue dots overlaid. Symmetric ±8% scale.
The question, and why the obvious answer lies
Everyone who lives here believes centre restaurants overcharge. Believing is easy; measuring is harder, because the obvious comparison (average price here versus average price there) mostly measures what kind of restaurants stand where, not what the location itself charges.
The corpus makes the problem concrete. Split the 899 venues into three equal groups (terciles) by tourism exposure and compare median dish prices:
Figure 2: The naive comparison, computed from the model frame. Comparing all dishes shows a +17% centre premium; restricting to mains halves it. Neither number tells you what location itself charges: the mix of dishes and restaurant types differs across the groups.
The all-dishes gap is +17%; carve out just the mains and it drops to +9%. That volatility is the tell: the “centre premium” you compute depends on what you accidentally compare. Centre menus carry different dishes (more snacks and small plates), different concepts (more pizza and steak, fewer neighbourhood eetcafés), different price bases (per-piece items). A defensible answer has to hold all of that constant and then ask what location adds.
That is a hedonic regression’s job: price the characteristic, here location, rather than the product.
Measuring “touristy”: the exposure instrument
“Tourist territory” needs a number before it can have a coefficient. We composed tourism intensity from three open-data signals, each measured around every venue: hotel-bed density, Airbnb listing density, and proximity to the major landmarks. The three are standardized and combined into a single score: the first principal component, which here is nearly identical to a simple average (correlation 0.999), so nothing exotic hides in the weighting. The composite is frozen. Its loadings were fixed once and reused unchanged through every re-fit, so no later result could quietly re-tune what “touristy” means.
Two supporting checks. The Airbnb layer (the gemeente’s own 2022 counts) was validated against current InsideAirbnb data, used as a diagnostic only, never as a model input (their data is licensed non-commercial): district ranks correlate at ρ=0.95, so staleness doesn’t move the map. And housing value (WOZ, the municipal property valuation per neighbourhood) enters the model separately, so “touristy” isn’t just re-measuring “expensive area”.
The model
The unit of observation is one priced food dish: 34,996 of them across 899 venues (drinks excluded by design; they price on different economics). The model:
The tourism term is standardised, so its coefficient reads per standard deviation (SD) of exposure: the verdict’s “one big step”. For scale, the least- and most-touristed terciles of venues sit about 2.2 SD apart.
Every term is there to kill a specific way of lying:
Term
What it prevents
dish role (starter / main / sweet / …)
comparing fries against tartare
concept family (24 menu-derived families: pizza, sushi, indonesian, steakhouse, …)
comparing a pizzeria against a raw bar; pizzerias are judged against pizzerias
price basis (per-plate vs per-piece, count-derived)
per-piece items (sushi, bitterballen) reading as absurdly “cheap dishes”
WOZ (housing value per buurt)
“touristy” secretly meaning “rich area”
(1 | restaurant), a venue random effect
one expensive outlier masquerading as a neighbourhood effect
log(price)
expensive dishes drowning out cheap ones: effects read as percentages, so a 10% move counts the same on a €40 rib-eye as on a €12 burger
The price basis term deserves its one-paragraph war story, because it changed the answer. In the first fit, sushi and dumpling venues clustered at the “cheap” extreme. An artifact: their menus price per piece. Recoding the basis from the portion count (not from the concept, which would have been circular) and quarantining genuinely non-comparable lines (per-weight, multi-person bundles, ~1.4%) moved the tourism coefficient from +3.3% to +3.7% and tightened it. The lesson generalises: the biggest threats to this estimate were measurement artifacts, not confounders, and they were found by chasing implausible residuals, not by adding controls.
Why Bayesian, and what “99.8%” actually means
The model is fitted with brms (Bayesian regression models via Stan). The Bayesian estimator was chosen for the message, not the point estimate: with ~35,000 dishes the estimate barely moves either way (the frequentist lme4 companion fit lands on the same +4.2%, 95% confidence interval [+1.4%, +7.1%], p = .003). What Bayes buys is uncertainty statements a reader can take literally: “there is a 99.8% probability the premium is positive” is a sentence the posterior can actually license, where a p-value famously cannot.
Show the code
draws <- readr::read_csv("_data/m3_tax_draws.csv", show_col_types =FALSE)qs <-quantile(draws$tax_pct, c(.05, .5, .95))dd <-density(draws$tax_pct)dens <-data.frame(x = dd$x, y = dd$y)ggplot(dens, aes(x, y)) +geom_area(data =subset(dens, x >= qs[1] & x <= qs[3]),fill ="#b5623f", alpha = .25) +geom_line(colour ="#b5623f", linewidth = .8) +geom_vline(xintercept =0, linetype ="dashed", colour ="grey40") +geom_vline(xintercept = qs[2], colour ="#b5623f") +annotate("text", x = qs[2], y =max(dens$y) *1.06,label =sprintf("median %+.1f%%", qs[2]), size =3.4) +annotate("text", x =0, y =max(dens$y) * .5, label ="zero", angle =90,vjust =-0.5, size =3, colour ="grey40") +labs(x ="Price effect of +1 SD tourism exposure (%)", y =NULL) +theme_minimal() +theme(axis.text.y =element_blank(), panel.grid.major.y =element_blank())
Figure 3: The full posterior of the tourist tax (16,000 draws from the accepted fit). Shaded: the central 90% credible interval [+1.8%, +6.6%]. The sliver of posterior mass below zero is 0.2%.
Fitting discipline, briefly: the workflow ran prior-predictive checks before fitting (the first prior set failed its gate and was revised before seeing results), and the accepted fit is clean: 0 divergent transitions, all convergence diagnostics within tolerance (maximum R̂ 1.007, minimum bulk effective sample size 1,020). A first re-fit showed marginal convergence on the headline coefficient itself and was re-run with 50% more iterations rather than explained away.
What it found
The geography
Feeding each district’s average tourism exposure through the coefficient’s posterior gives the premium map. One honest subtlety: these are the same single coefficient evaluated at different exposures, with uncertainty carried through, not independent per-area estimates (several districts are too thin to support those).
Figure 4: Modelled location premium by stadsdeel (city borough), with 90% credible intervals. The city-wide span is ~11 points; no borough’s interval crosses into implausible territory.
For calibration: +4.2% of a €20 main is about 85 cents. The tourist tax is real and robust, and it is not gouging. The gouging intuition people carry comes from the composition channel (the €19 tourist-menu pasta exists in the centre and not in Noord), which is precisely what this design controls away and what dossiers D3/D4 measure instead.
It follows beds, not photo spots
Pulling the composite apart, the premium rides on the accommodation components, where tourists sleep (hotel beds and Airbnbs), while distance to landmarks contributes nothing on its own. Restaurants price the neighbourhood’s tourist population, not its postcard value. This is also the practical reading for anyone using the map: the premium zone is the bed zone.
The stress-test battery
Every threat we could name, tested. The estimate survived all of them; two moved it slightly up (meaning the shipped +4.2% is conservative).
The threat
The test
The verdict
Per-piece pricing artifact (sushi “cheap”)
count-derived price_basis + quarantine of non-comparable lines
wijk variance estimates exactly 0; coefficient and SE bit-identical
Selection: only venues that publish priced menus online
in-model rate by tourism tercile + logistic check
46.6% / 41.5% / 44.1%, odds ratio 0.96/SD, p=.32: flat, and the faint tilt runs the conservative way
Model calibration (are the intervals honestly sized?)
posterior-predictive + LOO-PIT checks
intervals well-calibrated; one known mild distributional mis-fit, documented below
One deep-fried cross-check
A regression with 24 strata is one kind of evidence. The project also built the opposite kind: a price check on a literal commodity, with no model in it at all. Most cafés buy near-identical bitterballen from the same handful of suppliers, so differences in the price per piece reflect location and venue, with no chef and no recipe in the way. That is the same trick The Economist plays with the Big Mac: hold the product constant and price differences become pure environment. It is why the full treatment in dossier D7 is called the Bitterballen Index. Across 138 venues, price per piece correlates with tourism exposure at ρ = +0.39, the same direction and comparable size as the hedonic answer. A finding that survives both a 35,000-dish regression and a one-product price check is hard to dismiss as a modelling artifact.
What this is, and what it isn’t
A location premium, not a proven mechanism. The design identifies a robust conditional association, not why it exists. Some is plausibly demand-side (tourists don’t come back); some is cost pass-through (commercial rents: our WOZ control measures residential value, which only partly tracks them). The honest name for the estimand is a conditional-prediction premium.
The floor, not the ceiling. Comparing pizzerias with pizzerias deliberately removes the composition channel. Since tourist zones also attract pricier concepts, the total markup a visitor experiences is larger than +4.2%. That larger channel is measured in D4.
Menu price ≠ paid price. We observe menus, not receipts; portions are unobserved. If centre portions shrink, the true per-calorie premium is bigger than reported.
The universe is venues publishing a priced menu online. Tested (row 9 above) and flat across tourism terciles, but still the universe.