Method
How HealthTrend calculates an estimate
HealthTrend is an estimation product, not a weight logger. This page describes the model behind every number the analysis screen shows, from the idea to the equations, and names the code that implements each one.
1What HealthTrend estimates
A bathroom scale does not measure the thing you want to know. Body weight moves by a kilogram or more within a single day for reasons that have nothing to do with a trend — fluid, food, time of day — so any one reading is the underlying weight plus an unknown amount of noise.
HealthTrend estimates the quantity underneath: a latent weight, and the rate at which that weight is changing, in kilograms per week. Both are estimates with a stated uncertainty rather than measurements, and the rate is usually the more useful of the two — it answers “am I progressing” more directly than any single weight value does.
The estimator has no parameter through which a goal could reach it. A target weight is interpreted above the model, never inside it, so the same series produces the same estimate whatever anyone is aiming for.
The posterior mean of the level with its 95% band, the readings it saw, and a projection whose band widens with every step forward.
Figure 1 · Source: synthetic demo data, “Gradual loss” scenario
2How a reading changes the estimate
Each new reading is evidence, not a replacement. The estimate moves part of the way towards it, and how far depends on two things the model tracks: how uncertain the current estimate already is, and how noisy a single reading is assumed to be. A well-determined estimate barely moves for one surprising reading; an uncertain one moves a long way.
Time enters as real elapsed time, not as a count of readings. Two weigh-ins a day apart and two a month apart are treated differently, and a gap of thirty days is handled exactly as thirty one-day steps would be — the same distribution, not an approximation. Weighing yourself irregularly costs precision; it does not break the model.
The trajectory is filtered, not smoothed. Every point on the line reflects only the data available at that instant, so nothing earlier is rewritten when a new reading arrives: it is the estimate you would have had on the day. A retrospective view, which revises the past in light of what came after, is a different calculation and is not what the chart shows.
3What the uncertainty means
The band around the line is a 95% interval on the underlying weight — not on what a scale would read tomorrow morning. An interval for a future scale reading would be wider, by the measurement noise, and answers a different question.
It is the model’s own spread, computed from the assumptions below. It is exact only if those assumptions and those parameters are right; it does not include uncertainty about the parameters themselves, and its coverage has never been measured against real recorded data.
HealthTrend states the interval and stops there. It does not turn a spread into a label such as “high confidence” or “low confidence”, because that would be a judgement the numbers do not make and a threshold nobody has defined.
4How forecasting works
A forecast is the current state carried forward: the estimated weight moves at the estimated rate. Nothing is added to it, and no other information is used.
The interval grows with distance for two separate reasons. The rate is itself only an estimate, and its error compounds over a longer lever arm; and the rate is allowed to drift over the period rather than being held fixed. Together those make a distant forecast honestly vague rather than wrong with a narrow interval.
Horizons are fixed at 7, 30 and 90 days and are measured from a forecast origin. If the last weigh-in was some days ago, those days are real elapsed time in which the trend both moved and became less certain, so they are carried through as well.
The model has no notion of a floor, a ceiling or a return to any usual weight, so it is only locally valid: simulated far enough forward it produces weights no body could have. That is a true property of this class of model, and it is why the product forecasts 90 days rather than years.
5Model parameters
These are documented priors, not values fitted to anybody’s data. They are plausible, they are stated in product units and converted, and they determine how hard the estimate smooths — which makes them the single biggest influence on what the analysis screen shows.
| Parameter | Value | What it means |
|---|---|---|
| Measurement noise | 0.50 kg | How far a single scale reading is assumed to sit from the underlying weight, as one standard deviation. |
| Weekly-rate drift | 0.15 kg/week per week | How much the weekly rate is allowed to change from one week to the next. Raise it and the estimate follows recent readings more closely; lower it and the estimate is steadier and slower to turn. |
| Initial rate spread | 1.00 kg/week | How uncertain the rate is before any trend has been seen. With a single reading the model reports a weight and declines to invent a trend at all. |
| Process-noise intensity | 0.008099 | The weekly-rate drift expressed in the model’s own units. Derived, not chosen separately — see the appendix. |
These values are read from the analysis service rather than copied into this page, so they cannot drift apart from the ones an analysis actually used.
6Assumptions and limitations
These are stated because they are load-bearing. None of them has been validated against real recorded data.
- The parameters above are priors, not fitted values. They are plausible and documented, and they determine how hard the product smooths.
- Intervals are exact only for fixed parameters. Coverage on real data is unmeasured.
- There is no robustness to mistyped or freak readings, by design. The filter is linear, so one bad reading displaces the estimate in proportion to the gain, and the displacement decays as a damped oscillation rather than steadily.
- The model has no mean reversion, so it is only locally valid — the reason horizons stop at 90 days.
- Weigh-ins taken minutes apart are treated as independent, which shrinks the interval slightly more than reality warrants.
- A local linear trend cannot represent a flattening or a turn as structure; it tracks them by drifting velocity, which lags.
- The trajectory is filtered, not smoothed: each point reflects only the data available at that instant.
- Calibration has been demonstrated only on data drawn from the model itself, which validates the implementation rather than the choice of model.
7Mathematical appendix
The complete specification of the estimator, unsimplified. Everything above is a description of what follows.
A1 · State and observation
The state carries the underlying weight w in kilograms and its velocity v in kg/day. A weigh-in observes the weight alone, with Gaussian measurement noise of variance R.
A2 · Transition and process noise
Velocity is a Wiener process and weight is its integral, so the covariance added over a gap is the continuous noise integrated through the dynamics — not a diagonal random walk. That is what makes irregular weigh-in times principled rather than approximated: one 30-day gap is identically the same distribution as thirty one-day steps.
A3 · The filter recursion
Predict forward to the next weigh-in, then correct by however much the reading surprised the model. The surprise ν is the innovation, S its variance, and the gain K is the share of the surprise the estimate absorbs — the reason one reading moves the trend only part of the way.
The covariance update is written in Joseph form. It is algebraically identical to the shorter version and costs more arithmetic; it is used because only this form stays symmetric and positive semi-definite when accumulated over thousands of steps.
A4 · The interval that is published
The 95% range is the posterior standard deviation of the weight component, scaled by the normal quantile. The multiplier is usually written 1.96; the code holds it unrounded as a named constant so the choice is stated once rather than scattered as a literal.
A5 · Forecast propagation
A horizon h is measured from the forecast origin, but the state is carried over the total elapsed time τ — the lead λ since the last weigh-in, plus the horizon. Those lead days are real time in which the trend both moved and became less certain, so they are propagated through rather than skipped.
The three terms after Pww are the whole story of the widening band: current-weight uncertainty, current-velocity uncertainty over a longer lever arm, and drift in the trend itself. The τ3 term is what makes distant forecasts honestly vague rather than wrong with a narrow interval.
A6 · Units and the weekly rate
Velocity is estimated in kg/day and displayed in kg/week. The conversion is exact, and the standard deviation scales with it. The process-noise intensity is derived from a prior stated in product units, because σa in kg·day−3/2 is not humanly checkable.
A7 · Equation to code
Every equation above names the file and symbol that implements it. If an equation appears here and no code implements it, one of the two is wrong.
| Equation | File | Symbol |
|---|---|---|
| F(Δt) | app/core/model.py | transition_matrix |
| Q(Δt) | app/core/model.py | process_noise |
| initial state and covariance | app/core/model.py | initial_state |
| symmetrise, PSD checks | app/core/model.py | validate_covariance |
| predict step | app/core/kalman.py | predict |
| ν, S, K and the Joseph update | app/core/kalman.py | update |
| filter over irregular Δt | app/core/filter.py | run_filter |
| w ± z√Pww | app/core/types.py | StateEstimate.w_interval |
| τ = λ + h | app/core/forecast.py | _propagated_point |
| forecast mean and variance | app/core/forecast.py | forecast_at |
| forecast band | app/core/forecast.py | forecast_path |
| r = 7v | app/core/units.py | per_day_to_per_week |
| σa = d ⁄ (7√7) | app/core/units.py | sigma_accel_from_weekly_rate_drift |
| the whole pipeline | app/core/analyse.py | run_analysis |