Each point is a time period (month / quarter / year depending on the view). We don’t plot every individual property sale — we summarise the period into a few easy-to-read lines.
IQR
IQR = Q3 − Q1
Band
band = [P25, P75]
Median line = the “typical” sale for that period.
Shaded band = the middle half of sales (from P25 to P75).
Bucket switcher controls how time is grouped (Month / Quarter / Year).
Percentiles follow the database’s continuous-percentile convention (PERCENTILE_CONT-style interpolation). This means a percentile can land between two observed sales — the lines are a robust summary, not “the price of one specific deal”.
Example (illustrative): blue dots = individual sales; green line = median (typical); green band = P25–P75.
Details are explained below: what data is included (1), robust handling of atypical sales (2), area buckets (3), log scale (4), and time buckets (5).
1) What we aggregate
What goes into each line
PPSQM
ppsqm = price / area
Price: uses sales with price > 0. Area is not required.
Price per sqm (PPSQM): uses sales where we can compute a meaningful value (needs price > 0 and area > 0).
We may ignore tiny “micro areas” because they can create unreal PPSQM spikes.
The same filters (locality/street/date window/property type) are applied consistently across all series.
If you see a Price line but no PPSQM line: most often that means “area is missing or unusable for many sales in this slice”.
2) Robust outlier handling
Tukey’s fences (two-sided IQR rule)
Rule
x < Q1 − k·IQR OR x > Q3 + k·IQR
Property datasets include rare sales that may not reflect a typical market price. To keep trends readable, we reduce the impact of extreme values using a standard robust rule called Tukey’s fences.
Why this helps: at street-level, a single atypical transaction can dominate a period. Robust filtering keeps the chart readable while still showing the main trend.
Green dashed lines (when shown) illustrate an example fence interval (illustrative only).
3) Area stratification (bucketed fences)
Different sizes, different thresholds
Bucket id
area_bucket = ⌊ area / bucket_sqm ⌋
Fallback
If n(area_bucket) ≥ min_samples: use bucket fences; else: fall back to locality-wide fences
PPSQM behaves differently for small vs large dwellings. To avoid mixing these, fences can be computed inside area buckets whenever there are enough samples.
This prevents “micro-sample rules”: we don’t want each street to invent its own thresholds from 3 sales. Instead, thresholds are based on a broader slice where the statistics are more stable.
4) Log scale (why it helps)
Making large ranges readable
Linear scale: equal vertical distance = equal difference (e.g. 900k → 1.0M is the same height change as 1.0M → 1.1M).
Log scale: equal vertical distance = equal percentage change (e.g. 500k → 1.0M (×2) is the same height change as 1.0M → 2.0M (×2)).
Why the line can look “flat” on a normal scale: if most sales are in a normal range but a few points are much higher, the chart has to fit everything on one axis. That can visually compress the normal range.
Log scale spreads the chart by ratios, so the normal movement becomes readable even when there are extreme values in the same view.
Note: log scale can only show positive values. If a period has no valid sales for a metric, you’ll see a gap.
Interactive controls (to “declutter”)
Use the Log scale switch when ranges are huge.
Click legend chips to hide/show lines (locality band/median and each street overlay).
Shift-click a chip to show only that series.
Use Reset to bring everything back.
If you still see nothing: most often it’s “no sales in that slice/period” or “PPSQM cannot be computed because area is missing”.