Skip to content

Alternative trend fomula #396

@timboldt

Description

@timboldt

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.

Image

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;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    triage neededThis issue needs to be reviewed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions