WellAlly Logo
WellAlly康心伴
Development

Predicting Workout Plateaus with Time-Series Forecasting in Python

Go beyond simple workout logging. Learn how to use Python, Pandas, and Prophet for time-series forecasting to predict your future strength gains and identify workout plateaus before they happen.

W
2025-12-16
10 min read

We've all been there. You're consistently hitting the gym, adding weight to the bar, and feeling stronger each week. Then, suddenly, your progress stalls. The weights that once felt manageable now feel impossibly heavy, and you're stuck at the same numbers for weeks on end. This frustrating experience is known as a workout plateau, and it's a common hurdle for lifters of all experience levels.

But what if you could see a plateau coming before it hits? In this deep-dive tutorial, we'll explore how to use time-series forecasting in Python to predict your future strength gains and, more importantly, identify when you're likely to hit a plateau. By the end, you'll have a practical, data-driven approach to understanding your training progress.

We will build a model to forecast our one-rep max (1RM) for the bench press and analyze the forecast to pinpoint when our gains are predicted to level off. This will empower you to make proactive changes to your training program, whether that means adjusting your volume, intensity, or taking a much-needed deload.

This matters to developers because it's a fantastic, real-world application of data science principles. You get to work with tangible data that you can generate yourself and see the direct impact of your code on your personal fitness goals.

Prerequisites:

  • Basic understanding of Python and the pandas library.
  • Familiarity with foundational machine learning concepts.
  • Python (3.6+) with Jupyter Notebook or your favorite IDE.
  • The following libraries installed: pandas, numpy, plotly, and prophet.

Understanding the Problem

A workout plateau, in data terms, is a period where the rate of progress (the slope of your strength curve) approaches zero. While you can often "feel" a plateau, a more rigorous approach is to model your progress over time and forecast when your performance is likely to stagnate.

Traditional methods of tracking workouts in notebooks or spreadsheets are great for logging what you've done, but they are retrospective. Time-series forecasting allows us to be prospective—to look into the future based on the patterns of the past.

We'll use a popular time-series forecasting library called Prophet, developed by Facebook's Core Data Science team. Prophet is well-suited for this task because it's robust to missing data (we all miss a gym day now and then), handles trends and seasonality well, and is relatively easy to use even for those new to forecasting.

Prerequisites

Before we start, let's get our environment set up. You'll need to install the necessary Python libraries.

code
pip install pandas numpy prophet plotly
Code collapsed
  • pandas & numpy: For data manipulation and numerical operations.
  • prophet: The forecasting library we'll be using.
  • plotly: For creating interactive visualizations.

Step 1: Generating a Synthetic Workout Dataset

Real-world workout logs can be messy. For this tutorial, we'll create a clean, synthetic dataset that models the journey of a lifter: a period of steady progress followed by a plateau. This will make it easier to see our model in action.

What we're doing

We will create a pandas DataFrame with two columns: ds (the date of the workout) and y (the metric we're tracking). For this example, y will be the estimated one-rep max (1RM) for the bench press. The 1RM is a great metric for tracking strength as it provides a consistent measure of progress, even as your rep ranges change. We'll use the common Epley formula to estimate it: 1RM = weight * (1 + reps / 30).

Implementation

Let's write a Python script to generate this data. We'll simulate a lifter who trains 3 times a week for several months.

code
# src/data_generation.py
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

def generate_workout_data():
    """
    Generates a synthetic workout dataset simulating progress and a plateau.
    """
    start_date = datetime(2024, 1, 1)
    # Simulate 9 months of training, 3 times a week
    dates = pd.to_datetime([start_date + timedelta(days=i*2.33) for i in range(108)])

    # Initial strength
    initial_1rm = 135  # lbs
    
    # Phase 1: Newbie gains (first 4 months) - rapid, consistent progress
    newbie_gains_period = int(108 * 0.45)
    newbie_gains = np.linspace(0, 55, newbie_gains_period)
    
    # Phase 2: Intermediate gains (next 3 months) - slower progress
    intermediate_period = int(108 * 0.30)
    intermediate_gains = np.linspace(5, 15, intermediate_period)

    # Phase 3: Plateau (last months) - minimal gains
    plateau_period = int(108 * 0.25)
    plateau_gains = np.random.uniform(0, 1.5, plateau_period)
    
    # Combine gains and add some noise
    total_gains = np.concatenate([newbie_gains, intermediate_gains, plateau_gains])
    noise = np.random.normal(0, 2, len(dates))
    
    # Calculate estimated 1RM over time
    estimated_1rm = initial_1rm + total_gains + noise

    df = pd.DataFrame({'ds': dates, 'y': estimated_1rm})
    df['y'] = df['y'].round(1)

    return df

workout_df = generate_workout_data()
print(workout_df.head())
print("\n--- Last 5 entries ---")
print(workout_df.tail())
Code collapsed

How it works

This script creates a timeline of workouts and simulates three distinct phases of a lifter's journey:

  1. Newbie Gains: A period of rapid and linear strength increase.
  2. Intermediate Gains: Progress slows down as the lifter becomes more experienced.
  3. Plateau: Strength gains become minimal and fluctuate.

We add some random noise to make the data more realistic. "Good" and "bad" days in the gym are a reality!

Expected Output

code
          ds      y
0 2024-01-01 00:00:00  133.5
1 2024-01-03 07:55:12  136.9
2 2024-01-05 15:50:24  138.8
3 2024-01-07 23:45:36  139.7
4 2024-01-10 07:40:48  141.4

--- Last 5 entries ---
             ds      y
103 2024-09-02 15:50:24  206.5
104 2024-09-04 23:45:36  205.8
105 2024-09-07 07:40:48  207.2
106 2024-09-09 15:36:00  206.0
107 2024-09-12 00:00:00  206.8
Code collapsed

Step 2: Visualizing the Historical Data

Before we start forecasting, it's crucial to visualize our data to understand its patterns.

Implementation

code
# src/visualization.py
import plotly.express as px

def plot_historical_data(df):
    fig = px.line(df, x='ds', y='y', title='Bench Press 1RM Progression Over Time',
                  labels={'ds': 'Date', 'y': 'Estimated 1RM (lbs)'})
    fig.update_layout(template="plotly_white")
    fig.show()

plot_historical_data(workout_df)
Code collapsed

How it works

This will generate an interactive plot showing our simulated lifter's progress. You can clearly see the initial steep curve, the leveling off, and the eventual plateau.

(A sample image of what the plot would look like)

Step 3: Building and Training the Forecasting Model

Now for the exciting part! We'll use Prophet to create a forecast. Prophet requires the data to be in a specific format: the date column must be named ds and the value column y, which we've already done.

What we're doing

We'll instantiate a Prophet model, fit it to our historical data, and then create a "future" DataFrame to hold our predictions.

Implementation

code
# src/forecasting.py
from prophet import Prophet

# Initialize the model
# changepoint_prior_scale helps adjust the flexibility of the trend.
# A higher value makes the trend more flexible.
model = Prophet(changepoint_prior_scale=0.5, yearly_seasonality=False, weekly_seasonality=True, daily_seasonality=False)

# Fit the model to our data
model.fit(workout_df)

# Create a dataframe for future predictions (e.g., the next 3 months)
future = model.make_future_dataframe(periods=90)

# Generate the forecast
forecast = model.predict(future)

# Display the last few rows of the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
Code collapsed

How it works

  1. Prophet(...): We create an instance of the Prophet model. We've enabled weekly seasonality because workout schedules often have a weekly pattern. We've increased changepoint_prior_scale to allow the model to better capture the change in the trend from rapid growth to a plateau.
  2. model.fit(workout_df): This is where the model learns the patterns from our historical data.
  3. make_future_dataframe(periods=90): Prophet creates a DataFrame extending 90 days into the future.
  4. model.predict(future): This populates the future DataFrame with predictions (yhat) and uncertainty intervals (yhat_lower, yhat_upper).

Expected Output

code
             ds        yhat  yhat_lower  yhat_upper
202 2024-12-07 19:12:00  210.034785  205.860111  214.364426
203 2024-12-08 19:12:00  209.689408  205.158756  213.784411
204 2024-12-09 19:12:00  209.914285  205.479532  214.072972
205 2024-12-10 19:12:00  210.158564  205.795817  214.394158
206 2024-12-11 19:12:00  210.228532  205.908851  214.469447
Code collapsed

Step 4: Analyzing the Forecast to Predict a Plateau

Having a forecast is great, but our goal is to identify a plateau. A plateau occurs when the rate of change of our progress approaches zero. Prophet handily provides a trend component in its forecast, which we can analyze.

What we're doing

We'll visualize the forecast and its components. Then, we'll compute the daily rate of change of the forecasted trend to quantitatively pinpoint when our progress stagnates.

Implementation

code
# src/analysis.py
from prophet.plot import plot_plotly

# Plot the forecast
fig1 = model.plot(forecast)
# Use plot_plotly for an interactive version
# fig1_interactive = plot_plotly(model, forecast)

# Plot the forecast components (trend and seasonality)
fig2 = model.plot_components(forecast)

# --- Plateau Detection ---
# Calculate the daily change in the trend
forecast['trend_change'] = forecast['trend'].diff()

# Identify the date when the trend change becomes negligible (e.g., < 0.1 lbs per day)
plateau_start_date = forecast[forecast['trend_change'] < 0.1].iloc[0]['ds']

print(f"A potential plateau is predicted to start around: {plateau_start_date.date()}")

# Add a marker to the forecast plot for the plateau
import matplotlib.pyplot as plt

# Get the figure and axes from the plot
ax = fig1.gca()
# Add a vertical line to signify the start of the plateau
ax.axvline(plateau_start_date, color='r', linestyle='--', lw=2)
ax.text(plateau_start_date, workout_df['y'].min(), '  Plateau Predicted', color='r')

plt.show() # Display the plot with the plateau line
Code collapsed

How it works

Prophet's plot functions make it easy to visualize the forecast. The black dots represent our actual data, the dark blue line is the prediction (yhat), and the light blue area is the uncertainty interval.

The key to our plateau detection is calculating forecast['trend'].diff(). This gives us the day-over-day change in our underlying strength trend. We then find the first date where this change drops below a small threshold (in this case, 0.1 lbs). This is our predicted start of the plateau. When we add this as a vertical line on our forecast plot, we get a powerful visual indicator of when we need to change things up.

Putting It All Together

Let's see the complete picture: the historical data, the forecast, and our predicted plateau point all on one graph. The red dashed line on the plot will show exactly when our model predicts the gains will stop.

(A sample image of what the forecast plot with the red plateau line would look like)

This visualization is incredibly powerful. You can see your past progress and a statistically-backed prediction of your future progress, including the point of stagnation.

Alternative Approaches

  • ARIMA: Another powerful class of models for time-series forecasting is ARIMA (AutoRegressive Integrated Moving Average). ARIMA models can be very effective but often require more statistical knowledge to tune the parameters (p, d, q) correctly. Prophet is generally more automated and easier for beginners.
  • LSTM Neural Networks: For more complex, non-linear patterns in your workout data, you could explore Long Short-Term Memory (LSTM) networks, a type of recurrent neural network. However, this requires significantly more data and is a much more complex implementation.

Conclusion

We've successfully built a time-series model to forecast strength progression and, most importantly, predict the onset of a workout plateau. By analyzing the rate of change of the forecast's trend, we can get a data-driven heads-up that it's time to adjust our training.

This is just the beginning. You can apply this same methodology to other exercises, track different metrics like workout volume, or even incorporate external factors (regressors in Prophet) like sleep quality or daily calorie intake to build a more sophisticated model.

So, go ahead and start logging your workouts! With a bit of Python and data science, you can take your fitness journey to the next level. ✨

Resources

#

Article Tags

pythondatasciencefitnessforecasting
W

WellAlly's core development team, comprised of healthcare professionals, software engineers, and UX designers committed to revolutionizing digital health management.

Expertise

Healthcare TechnologySoftware DevelopmentUser ExperienceAI & Machine Learning

Found this article helpful?

Try KangXinBan and start your health management journey

© 2024 康心伴 WellAlly · Professional Health Management