Fix forecast chart continuity and hover
This commit is contained in:
@@ -84,10 +84,14 @@ def build_forecast(values, dates, horizon_weeks=8, metric='E1RM'):
|
||||
horizon_days = horizon_weeks * 7
|
||||
future_x = np.arange(0, horizon_days + 1, 7, dtype=float) + x[-1]
|
||||
future_dates = [last_date + timedelta(days=int(day - x[-1])) for day in future_x]
|
||||
future_values = intercept + slope * future_x
|
||||
# Anchor the projection to the most recent observed value. A pure
|
||||
# regression line can sit noticeably above or below the latest workout,
|
||||
# which makes the forecast appear to jump when it starts.
|
||||
future_values = observed_values[-1] + slope * (future_x - x[-1])
|
||||
|
||||
# A 95% prediction band, widened for points further from the observations.
|
||||
prediction_error = 1.96 * residual_std * np.sqrt(
|
||||
# A restrained 90% prediction band, widened for points further from the
|
||||
# observations so the uncertainty remains readable on the small chart.
|
||||
prediction_error = 1.645 * residual_std * np.sqrt(
|
||||
1 + (1 / len(observed_values)) + ((future_x - mean_x) ** 2 / sum_squared_x)
|
||||
)
|
||||
lower_values = np.maximum(future_values - prediction_error, 0)
|
||||
@@ -212,6 +216,7 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
|
||||
forecast_points = []
|
||||
forecast_band_lower = []
|
||||
forecast_band_upper = []
|
||||
forecast_labels = []
|
||||
forecast_start_position = None
|
||||
if forecast.get('available'):
|
||||
forecast_start_position = round((observed_max_date - observed_min_date).days / total_span, 3)
|
||||
@@ -220,6 +225,11 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
|
||||
forecast_points.append((round(((point['value'] - min_e1rm) / e1rm_range) * vb_height, 1), position))
|
||||
forecast_band_lower.append((round(((point['lower'] - min_e1rm) / e1rm_range) * vb_height, 1), position))
|
||||
forecast_band_upper.append((round(((point['upper'] - min_e1rm) / e1rm_range) * vb_height, 1), position))
|
||||
forecast_labels.append((
|
||||
position,
|
||||
f"{point['date'].strftime('%d %b %y')} · Projected E1RM: {point['value']}kg "
|
||||
f"(range {point['lower']}–{point['upper']}kg)"
|
||||
))
|
||||
|
||||
# Return exercise data with SVG dimensions and data points
|
||||
return {
|
||||
@@ -243,6 +253,7 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
|
||||
'forecast_points': forecast_points,
|
||||
'forecast_band_lower': forecast_band_lower,
|
||||
'forecast_band_upper': forecast_band_upper,
|
||||
'forecast_labels': forecast_labels,
|
||||
'forecast_start_position': forecast_start_position
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user