How I Overfit My Waistline in Python

Author

Wade K. Copeland

Published

October 23, 2025

Introduction

My wife and I are pursuing a mutual reduction in gravitational pull, and as a certified try-hard sweat lord, I naturally wrote a Python program to monitor our descent into svelteness. Daily fluctuations are misleading (empirical evidence: a single urination event produced a -2 lb delta), so I employed statistical learning to reveal the true signal beneath the noise. Future weight loss now arrives with 95% confidence and 0% chill.

Code
###########
# Imports #
###########
import pandas as pd
import numpy as np
import seaborn as sns
from numbers_parser import Document
import matplotlib.pyplot as plt
import statsmodels.formula.api as smf
import math
import session_info

##############
# Data setup #
##############
def setup_data():
    doc = Document("Waimee’s Weight.numbers")
    sheets = doc.sheets
    tables = sheets[0].tables
    data = tables[0].rows(values_only=True)
    data = pd.DataFrame(data[1:], columns=data[0])
    data = pd.melt(data, id_vars='Day', var_name='Person', value_name='Weight')
    data['Day'] = data['Day'].astype('int')
    data['Person'] = data['Person'].astype('category')
    data['Weight'] = data['Weight'].astype('float')
    return data

d = setup_data()

Results

Code
##################################
# Linear Model to Predict Weight #
##################################
model = smf.ols('Weight ~ Day + Person + Day:Person', data = d)

# Coefficients
intercept = model.fit().params.iloc[0]
person = model.fit().params.iloc[1]
day = model.fit().params.iloc[2]
day_person = model.fit().params.iloc[3]

####################
# Weight Loss Plot #
####################
days = np.linspace(d['Day'].min(), d['Day'].max(), 100)

predicted_data = pd.DataFrame({
    'Day': np.tile(days, 2),
    'Person': ['Person L']*len(days) + ['Person R']*len(days)
})

predicted = model.fit().predict(predicted_data)
predicted_data['Weight'] = predicted

def weight_plot(data, pdata):
    plt.figure(figsize = (10, 6))
    
    for person in pdata['Person'].unique():
        person_pred = pdata[pdata['Person'] == person]
        sns.lineplot(x = 'Day', y = 'Weight', color = 'black', data = person_pred, linewidth = 2, alpha = 0.4)
    
    sns.lineplot(x = 'Day', y = 'Weight', hue = 'Person', data = data)
    
    plt.xlabel('Day')
    plt.ylabel('Weight')
    
    plt.legend(title='Person')
    plt.grid(True)
    
    plt.savefig('weight_fig.png')

#########################
# Expectation Functions #
#########################
def predicted_weight(model, person, day):
    model_fit = model.fit()
    weight = model_fit.params.iloc[0] + model_fit.params.iloc[1]*person + model_fit.params.iloc[2]*day + model_fit.params.iloc[3]*person*day
    return weight

def predicted_day(model, person, weight):
    model_fit = model.fit()
    day = (weight - model_fit.params.iloc[0] - model_fit.params.iloc[1]*person)/(model_fit.params.iloc[2] + model_fit.params.iloc[3]*person)
    return day

def delta_weight_day(model, person):
    model_fit = model.fit()
    dweight = model_fit.params.iloc[2] + model_fit.params.iloc[3]*person
    return dweight

cruise_days = (pd.to_datetime('6-21-2026') - pd.Timestamp.now().normalize()).days

summary = {
    'Expected Weight<br>Loss Per Day (lbs/day)': {
        'Person R': round(delta_weight_day(model, 1), 2),
        'Person L': round(delta_weight_day(model, 0), 2)
    },
    
    'Expected Weight<br>Loss Per Week (lbs/week)': {
        'Person R': round(delta_weight_day(model, 1)*7, 2),
        'Person L': round(delta_weight_day(model, 0)*7, 2)
    },
    
    'Weight at Time of Cruise (lbs)': {
        'Person R': round(predicted_weight(model, 1, cruise_days), 2),
        'Person L': round(predicted_weight(model, 0, cruise_days), 2)
    },
    
    'Days to 200 lbs': {
        'Person R': math.ceil(predicted_day(model, 1, 200))-d['Day'].max(),
        'Person L': math.ceil(predicted_day(model, 0, 200))-d['Day'].max()
    }
}

Lineplot of daily weight with predicted values.
  Expected Weight
Loss Per Day (lbs/day)
Expected Weight
Loss Per Week (lbs/week)
Weight at Time of Cruise (lbs) Days to 200 lbs
Person R -0.23 -1.59 266.94 488.00
Person L -0.34 -2.40 221.64 258.00

Statistical Methods

Ordinary least-squares regression was used to fit the model (Fox 2015).

OLS Regression Results
Dep. Variable: Weight R-squared: 0.986
Model: OLS Adj. R-squared: 0.986
Method: Least Squares F-statistic: 1935.
Date: Mon, 27 Oct 2025 Prob (F-statistic): 5.81e-76
Time: 15:36:58 Log-Likelihood: -144.80
No. Observations: 86 AIC: 297.6
Df Residuals: 82 BIC: 307.4
Df Model: 3
Covariance Type: nonrobust
coef std err t P>|t| [0.025 0.975]
Intercept 302.7395 0.414 730.802 0.000 301.915 303.564
Person[T.Person R] 18.1904 0.586 31.050 0.000 17.025 19.356
Day -0.3422 0.016 -20.864 0.000 -0.375 -0.310
Day:Person[T.Person R] 0.1144 0.023 4.932 0.000 0.068 0.161
Omnibus: 7.918 Durbin-Watson: 1.056
Prob(Omnibus): 0.019 Jarque-Bera (JB): 13.017
Skew: -0.271 Prob(JB): 0.00149
Kurtosis: 4.827 Cond. No. 135.


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.


Equation 1 can be used to predict weight given the person and number of days since the diet started.

\[ \begin{equation} \begin{split} E[\text{Weight}|\text{Person}, \text{Day}] = 302.74 + 18.19*\text{Person} + -0.34*\text{Day} + 0.11*\text{Person}*\text{Day} \\ \text{ where Person = 1 if Person R and 0 otherwise} \end{split} \end{equation} \tag{1}\]

Equation 2 can be used to predict the day a given weight will be achieved by either person.

\[ \begin{equation} \begin{split} E[\text{Day}|\text{Weight}, \text{Person}] = \frac{\text{Weight} - 302.74 - 18.19*\text{Person}}{-0.34 + 0.11*\text{Person}} \\ \text{ where Person = 1 if Person R and 0 otherwise} \end{split} \end{equation} \tag{2}\]

Equation 3 can be used to determined the expected average weight loss per day.

\[ \begin{equation} \begin{split} \frac{d(E[\text{Weight}|\text{Person}, \text{Day}])}{d(Day)} = -0.34 + 0.11*\text{Person} \\ \text{ where Person = 1 if Person R and 0 otherwise} \end{split} \end{equation} \tag{3}\]

Python Session Information

All of the files needed to reproduce these results can be downloaded from the Git repository https://github.com/wkingc/weighme-py.

-----
matplotlib          3.10.7
numbers_parser      4.16.2
numpy               2.3.4
pandas              2.3.3
seaborn             0.13.2
session_info        v1.0.1
statsmodels         0.14.5
-----
Python 3.14.0 (v3.14.0:ebf955df7a8, Oct  7 2025, 08:20:14) [Clang 16.0.0 (clang-1600.0.26.6)]
macOS-26.0.1-arm64-arm-64bit-Mach-O
-----
Session information updated at 2025-10-27 15:36

References

Fox, John. 2015. Applied Regression Analysis and Generalized Linear Models. Sage publications.