Live from the balcony
Model trained 2026-10-12 Β· Brier 0.098 Β· +28% skill over climatology Β· AUC 0.858
Tested in order, and the first rung that matches wins. The rain rungs read the fitted probability shown on the pill above β the same number, so the two cannot disagree. Every other rung reads its own model where one has cleared its gates, and the threshold it always had where none has yet; the right-hand column says which.
| When | It says | Evidence |
|---|---|---|
| Thunder model above 9% | Thunderstorm possible | fitted against thunder the stations' observers reported |
| Rain model above 60% | Rain likely | fitted against observed rainfall |
| Rain model above 10% | Rain possible | fitted against observed rainfall |
| Dew-point spread under 3Β°C and humidity rising | Fog or drizzle possible | hand-made: no fog model has cleared its gates yet |
| Sky model above 60% | Cloudy | fitted against observed cloud cover |
| Sky model between 30% and 60% | Little change | fitted against observed cloud cover |
| Sky model below 30%, humidity below 70% | Fair and settled | fitted against observed cloud cover |
Right now the pressure ranks at 65% of its last 30 days.
Each is fitted like the rain model β the same trees, the same signals, the same weather-service stations β to what those stations observed: how much of the sky was cloud, how far one could see, and thunder their observers heard. Each must clear the same gates before it ships, and until it has, its rung keeps the threshold it always had.
| Model | Held-out year | This balcony | Now |
|---|---|---|---|
| Overcast: 80% cloud or more over the next 6 h | +0.27 skill Β· AUC 0.80the rule: AUC 0.64 | +0.12 skill over 1,961 h | 62 % |
| Fog: visibility under 1,000 m within 6 h | not shipped yet: the hand-made rung runs | ||
| Thunder within 6 h | +0.12 skill Β· AUC 0.91the rule: AUC 0.52 | level matched to the stations | 0 % |
Fog has to prove itself on this balcony before it ships, not only at the stations: a sensor that sits at 100% whenever it is wet looks, to a model fitted on ventilated screens, like exactly the saturated air fog forms in.
Each model that has shipped is written up in full below, the same way as the rain chance.
"Falling barometer means rain" is the first rule anyone reaches for, and on this station it is worse than useless. A fall of more than 1 hPa over six hours scored a Hanssen-Kuipers score of -0.09 against rain within six hours; inside the wettest conditions, rain followed a rising barometer 64% of the time against 39% for a falling one β the wrong way round. The rules this ladder replaced were built entirely on tendency and scored -0.074.
Where the pressure sits does carry signal, but no fixed hPa threshold survives the change of season: a plain 'below 1020 hPa' scored a critical success index of 0.05 in one month of the sample and 0.44 in another. Ranking the reading against the station's own last 30 days is stable across all of them, which is why the column above is a percentile. The tendency is still measured and shown on the pressure card, and the rain model reads it too β not as a rule, but as one signal among the others, where it does earn its place (see what each signal is worth, below).
The reading itself is whatever the sensor reports. This app never reduces it to sea level and cannot tell whether the sensor already has: across the archive it averages 1024 hPa, where sea-level pressure averages about 1013. So read it as this balcony's own barometer rather than as the figure a forecast quotes β and note that nothing here reads it as an absolute, which is exactly what the percentile is for.
Taken as a yes/no rain forecast the ladder scores CSI 0.27 and KSS +0.27, against -0.074 for what it replaced.
Gradient-boosted trees: 300 small decision trees, each splitting the hour on one signal at a time, whose outputs add up to log-odds that a sigmoid turns into a probability. Trees rather than a weighted sum, because these signals matter in combination β a sharp chill at saturation means rain under any barometer, and a sum can only add the two. Serving it is a walk down each tree, so the container carries no numpy and no scikit-learn; the training job in CI does, and it ships the trees back as plain arrays in JSON.
It was not fitted to this balcony. One summer of readings is all the archive holds, and a model that has only seen July learns that cool air means dry air β the model this replaced did, and with rain on the sensor in October it said 11%. So it learns from the weather service's stations, every season of many years, and this balcony is where every retrain has to prove itself.
On the most recent year at those stations, 11 Oct 2025 to 11 Oct 2026: 43,671 hours that the fit being scored never saw, every season once.
| Brier | AUC | CSI | KSS | |
|---|---|---|---|---|
| This model | 0.098 | 0.858 | 0.385 | 0.517 |
| The ladder above | 0.264 | 0.680 | 0.270 | 0.361 |
| Always saying 21% | 0.137 | β | 0.000 | 0.000 |
Its skill over the base rate, season by season: winter +0.21, spring +0.32, summer +0.17, autumn +0.38. A model fitted to one summer has no business in any of the other three β this one has seen each of them many times.
On this balcony's own readings, 22 Jul 2026 to 11 Oct 2026 β 1,961 hours, 10% of them followed by rain at the nearest weather-service gauge β it scored Brier 0.069, a skill of +0.249, AUC 0.833, where the ladder ranked the same hours at AUC 0.751. That is the test a weather-service screen cannot pass on this sensor's behalf.
Straight from the weather-service stations it scored a skill of -0.016 on those hours: it ranked them well, but its probabilities did not fit this sensor, which is sun-baked and holds dew where a ventilated screen does neither. So the last step is a calibration fitted to the balcony's own record β two numbers that rescale the log-odds without changing which hour ranks above which β and the figures above are for the calibrated model, each stretch of the record scored by a calibration that was fitted without it.
A retrained model only ships if it clears the same gates on both tests: skill over the base rate, ranking hours at least as well as the ladder, and no sharp regression against the model already deployed. Refusing to ship is a normal outcome.
The same fit and the same held-out year, refitted from scratch without each signal in turn. Positive means the model got worse without it β that is what having evidence for a signal looks like. A row near zero is a signal the model could lose without noticing, usually because another one already carries what it knows.
| Without | Brier | AUC | Cost of dropping it |
|---|---|---|---|
| Dew point | 0.103 | 0.843 | +0.0049 |
| Pressure rank, 30 days | 0.099 | 0.851 | +0.0011 |
| Hour of the day | 0.099 | 0.855 | +0.0010 |
| Temperature change, 24 h | 0.099 | 0.857 | +0.0008 |
| Pressure change, 12 h | 0.099 | 0.857 | +0.0007 |
| Humidity swing, 6 h | 0.099 | 0.857 | +0.0006 |
| Pressure rank, 7 days | 0.098 | 0.857 | +0.0005 |
| Humidity | 0.098 | 0.858 | +0.0004 |
| Peak humidity, 1 h | 0.098 | 0.857 | +0.0004 |
| Dew-point change, 3 h | 0.098 | 0.857 | +0.0004 |
| Pressure change, 1 h | 0.098 | 0.857 | +0.0004 |
| Pressure change, 6 h | 0.098 | 0.857 | +0.0004 |
| Temperature swing, 24 h | 0.098 | 0.857 | +0.0004 |
| Temperature swing, 12 h | 0.098 | 0.858 | +0.0003 |
| Time saturated, 3 h | 0.098 | 0.858 | +0.0002 |
| Temperature change, 1 h | 0.098 | 0.857 | +0.0002 |
| Pressure change, 3 h | 0.098 | 0.858 | +0.0002 |
| Temperature swing, 3 h | 0.098 | 0.857 | +0.0002 |
| Temperature change, 3 h | 0.098 | 0.858 | +0.0001 |
| Temperature swing, 6 h | 0.098 | 0.857 | +0.0001 |
| Peak humidity, 3 h | 0.098 | 0.858 | +0.0000 |
| Lowest humidity, 24 h | 0.098 | 0.858 | +0.0000 |
Every hour, what the page was showing is written down. Once the weather has happened those are scored against the nearest weather-service rain gauge β the same kind of observation the model learned from. This is the deployed model's own record β not a fit to a held-out past, but the thing you were actually shown.
463 hours logged, 22 Sep 2026 to 11 Oct 2026, of which 12% saw rain. A calibrated probability matches its own claim: in the rows where it said 40%, it should have rained about 40% of the time.
| When it said | It rained | Hours |
|---|---|---|
| 0β10 % | 0 % | 281 |
| 10β20 % | 5 % | 65 |
| 20β30 % | 16 % | 51 |
| 30β40 % | 35 % | 17 |
| 40β50 % | 67 % | 18 |
| 50β60 % | 69 % | 16 |
| 60β70 % | 100 % | 7 |
| 70β80 % | 75 % | 8 |
Over those hours the deployed model scored Brier 0.057, a skill of +0.445 over climatology, AUC 0.951. The threshold ladder, scored on exactly the same hours, managed CSI 0.329 against the model's 0.293.
Those hours span 6 retrained models, because the model is refitted weekly. That is why this is logged rather than replayed: replaying the archive would credit every past hour to the model shipped today.
The labels are rain gauges β each one standing beside the sensors the model learned from β so the question is rain at a point like this balcony, not somewhere in the district. They replaced labels from a 25 km reanalysis, and that was measured rather than assumed: scored on the same two years at eight stations, the same trees ranked wet hours above dry ones as well against the gauges (AUC 0.842) as against the reanalysis (AUC 0.837). Point rain had looked unpredictable only because it was being asked of one summer and a weighted sum.
Each tree walks one path from its root to a leaf, and every step down it is a split on one signal that moves the tree's output up or down. Credit each step to the signal that made it, add them up over every tree, and you have the effects below: the starting point plus all of them is exactly the number the sigmoid squashes, which makes this table the prediction rather than a picture of it. There is no fixed weight to print beside them β what a signal is worth depends on the others, which is the reason for using trees.
| Signal | Now | Effect now |
|---|---|---|
| Pressure change, 3 h | +2.7 hPa | +0.98 |
| Pressure rank, 30 days | 65 % | -0.42 |
| Humidity | 100 % | +0.39 |
| Peak humidity, 1 h | 100 % | +0.38 |
| Pressure change, 12 h | +4.0 hPa | -0.37 |
| Pressure change, 1 h | +0.5 hPa | +0.35 |
| Humidity swing, 6 h | 51 pp | +0.32 |
| Temperature swing, 12 h | 8.1 Β°C | -0.27 |
| Dew point | 9.3 Β°C | +0.22 |
| Dew-point change, 3 h | +1.0 Β°C | +0.21 |
| Temperature swing, 6 h | 8.1 Β°C | -0.21 |
| Lowest humidity, 24 h | 46 % | +0.18 |
| Peak humidity, 3 h | 100 % | +0.16 |
| Pressure rank, 7 days | 100 % | -0.15 |
| Temperature swing, 24 h | 11.9 Β°C | -0.15 |
| Temperature change, 24 h | -2.0 Β°C | -0.07 |
| Temperature change, 1 h | -0.5 Β°C | +0.06 |
| Temperature swing, 3 h | 3.5 Β°C | -0.05 |
| Pressure change, 6 h | +2.4 hPa | -0.01 |
| Hour of the day | 22 h | +0.01 |
| Time saturated, 3 h | 47 % | -0.00 |
| Temperature change, 3 h | -4.2 Β°C | -0.00 |
| Starting point, before any signal | -3.28 | |
| Total, squashed to a probability | 15 % | |
The same kind of trees, the same signals and the same weather-service stations as the rain chance, asked a different question: will the next 6 hours be mostly overcast? The labels are the cloud cover each station reported every hour in eighths of the sky β by observers until 2022, by instruments since. A balcony cannot see the sky, so the model reads it from what the sky does to the air: a clear day heats and a clear night cools, an overcast one does neither. That is why it watches how far the temperature and the humidity have swung over the last hours to a day.
The banner does not read it at a single cut. Above 60% it says cloudy, below 30% fair, and between them little change: the probability is calibrated, so a band can be drawn where the reader would draw it, and "unsure" is not reported as "mixed sky".
On the most recent year at those stations, 11 Oct 2025 to 11 Oct 2026: 43,324 hours that the fit being scored never saw, every season once.
| Brier | AUC | CSI | KSS | |
|---|---|---|---|---|
| This model | 0.181 | 0.803 | 0.600 | 0.420 |
| The humidity rule it replaces | 0.376 | 0.635 | 0.408 | 0.270 |
| Always saying 56% | 0.249 | β | 0.538 | 0.000 |
Its skill over the base rate, season by season: winter +0.28, spring +0.23, summer +0.16, autumn +0.18.
On this balcony's own readings, 22 Jul 2026 to 11 Oct 2026 β 1,961 hours, 44% of them overcast at the nearest weather-service station that reports cloud β it scored Brier 0.218, a skill of +0.116, AUC 0.692, where the humidity rule ranked the same hours at AUC 0.593.
Straight from the stations it scored a skill of +0.129 on those hours. What ships is calibrated to this balcony β two numbers fitted to its own record that rescale the log-odds without changing which hour ranks above which β and the figures above are for the calibrated model, each stretch of the record scored by a calibration fitted without it.
The same decomposition as the rain chance: the starting point plus every effect below is exactly the number squashed into this probability.
| Signal | Now | Effect now |
|---|---|---|
| Humidity | 100 % | +0.57 |
| Pressure change, 3 h | +2.7 hPa | +0.40 |
| Pressure rank, 30 days | 65 % | -0.28 |
| Temperature swing, 3 h | 3.5 Β°C | -0.27 |
| Pressure change, 1 h | +0.5 hPa | +0.17 |
| Temperature swing, 12 h | 8.1 Β°C | -0.16 |
| Lowest humidity, 24 h | 46 % | -0.15 |
| Dew-point change, 3 h | +1.0 Β°C | +0.15 |
| Dew point | 9.3 Β°C | +0.14 |
| Pressure change, 12 h | +4.0 hPa | -0.13 |
| Temperature swing, 6 h | 8.1 Β°C | -0.11 |
| Pressure rank, 7 days | 100 % | -0.10 |
| Peak humidity, 1 h | 100 % | +0.07 |
| Temperature change, 1 h | -0.5 Β°C | +0.04 |
| Humidity swing, 6 h | 51 pp | +0.04 |
| Peak humidity, 3 h | 100 % | +0.03 |
| Temperature swing, 24 h | 11.9 Β°C | +0.02 |
| Pressure change, 6 h | +2.4 hPa | -0.01 |
| Time saturated, 3 h | 47 % | -0.00 |
| Temperature change, 24 h | -2.0 Β°C | -0.00 |
| Temperature change, 3 h | -4.2 Β°C | +0.00 |
| Hour of the day | 22 h | +0.00 |
| Starting point, before any signal | +0.12 | |
| Total, squashed to a probability | 62 % | |
Will there be thunder within 6 hours? The labels are thunder reported by the stations' observers, and only while someone was there to report it. The instruments that replaced them report cloud and visibility but not thunder, so once a station's observers had gone every hour reads βnothing reportedβ, whatever the sky did. Each station is used only up to its own last report, which is why this model has seen nothing more recent than 30 Sep 2021.
Thunder is rare β 2.2% of 6-hour windows at those stations β so its Brier skill looks small beside the rain chance even where it ranks hours well. The rule it replaces, a warm and humid summer afternoon, ranked them barely better than a coin toss.
On the last year of reports at those stations, 30 Sep 2020 to 30 Sep 2021: 16,777 hours that the fit being scored never saw.
| Brier | AUC | CSI | KSS | |
|---|---|---|---|---|
| This model | 0.025 | 0.910 | 0.167 | 0.237 |
| The summer-afternoon rule it replaces | 0.031 | 0.522 | 0.042 | 0.044 |
| Always saying 2% | 0.028 | β | 0.000 | 0.000 |
Its skill over the base rate, season by season: spring +0.02, summer +0.09, autumn -0.33.
Nothing near this balcony reports thunder any more, so the model cannot be scored here. What can be checked is its level: on this balcony's readings from 22 Jul 2026 to 12 Oct 2026 it said 6.3% on average, where the stations had thunder in 3.3% of windows in the same months β so its log-odds are shifted by -0.74 to match. That corrects for how this sensor differs from a weather-service screen, without changing which hour ranks above which.
The same decomposition as the rain chance: the starting point plus every effect below is exactly the number squashed into this probability.
| Signal | Now | Effect now |
|---|---|---|
| Humidity | 100 % | -0.69 |
| Dew point | 9.3 Β°C | +0.44 |
| Hour of the day | 22 h | -0.41 |
| Temperature change, 3 h | -4.2 Β°C | -0.33 |
| Temperature swing, 3 h | 3.5 Β°C | +0.22 |
| Pressure change, 1 h | +0.5 hPa | +0.22 |
| Humidity swing, 6 h | 51 pp | +0.21 |
| Pressure change, 12 h | +4.0 hPa | -0.19 |
| Temperature swing, 24 h | 11.9 Β°C | +0.18 |
| Pressure rank, 7 days | 100 % | +0.17 |
| Pressure rank, 30 days | 65 % | -0.13 |
| Temperature swing, 6 h | 8.1 Β°C | +0.11 |
| Lowest humidity, 24 h | 46 % | -0.08 |
| Temperature swing, 12 h | 8.1 Β°C | +0.08 |
| Pressure change, 3 h | +2.7 hPa | +0.06 |
| Temperature change, 24 h | -2.0 Β°C | +0.06 |
| Peak humidity, 1 h | 100 % | -0.05 |
| Dew-point change, 3 h | +1.0 Β°C | -0.04 |
| Time saturated, 3 h | 47 % | -0.04 |
| Peak humidity, 3 h | 100 % | +0.03 |
| Pressure change, 6 h | +2.4 hPa | -0.03 |
| Temperature change, 1 h | -0.5 Β°C | +0.00 |
| Starting point, before any signal | -6.48 | |
| Total, squashed to a probability | 0 % | |