-
-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Labels
triage neededThis issue needs to be reviewedThis issue needs to be reviewed
Description
When one's weight change is a fairly steady slope (as typically happens during dieting), an EMA lags because it assumes a constant level with noise.
It would be nice to have a choice of an alternative method (selectable in Settings). One example is Holt’s Linear Trend Method (Double Exponential Smoothing), which tracks the actual weight better.
Rough sketch:
/// <summary>
/// Computes weight trends using Holt's Linear Trend (Double Exponential Smoothing)
/// to reduce lag during consistent weight loss (e.g., Wegovy).
/// </summary>
private static List<ComputedMeasurement> ComputeWeightTrends(List<SourceMeasurement> sourceMeasurements)
{
// Alpha: Smoothing factor for the Level (Weight). 0.1 is standard for weight.
const decimal ALPHA = 0.1m;
// Beta: Smoothing factor for the Trend (Slope). 0.1 allows quick adaptation to rate changes.
const decimal BETA = 0.1m;
decimal level = 0;
decimal trend = 0;
var measurements = new List<ComputedMeasurement>();
for (int i = 0; i < sourceMeasurements.Count; i++)
{
var sourceMeasurement = sourceMeasurements[i];
var weight = sourceMeasurement.Weight;
if (i == 0)
{
// Initialization
level = weight;
// If we have at least two points, initialize the trend (slope)
// based on the first real change. Otherwise assume flat (0).
if (sourceMeasurements.Count > 1)
{
trend = sourceMeasurements[1].Weight - sourceMeasurements[0].Weight;
}
else
{
trend = 0;
}
}
else
{
decimal prevLevel = level;
decimal prevTrend = trend;
// 1. Update Level (The smoothed weight value)
// We combine the new reading with the *expected* value (prevLevel + prevTrend)
level = ALPHA * weight + (1 - ALPHA) * (prevLevel + prevTrend);
// 2. Update Trend (The rate of change/slope)
// We combine the recent change (level - prevLevel) with the previous trend
trend = BETA * (level - prevLevel) + (1 - BETA) * prevTrend;
}
measurements.Add(new ComputedMeasurement
{
Date = sourceMeasurement.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
ActualWeight = Math.Round(sourceMeasurement.Weight, 3),
// 'level' is now the best estimate of the current true weight
TrendWeight = Math.Round(level, 3),
WeightIsInterpolated = sourceMeasurement.WeightIsInterpolated,
FatIsInterpolated = false
});
}
return measurements;
}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
triage neededThis issue needs to be reviewedThis issue needs to be reviewed