Power duration modelling
Sweatpy has built-in classes and functions for power duration modelling.
Power duration model fitting¶
Sweatpy has a PowerDurationRegressor
class that helps you fit power data to a power duration model ("pdm").
The supported models are:
- 2 parameter pdm. Parameters: CP and W'.
- 3 parameter pdm. Parameters: CP, W' and Pmax.
The PowerDurationRegressor
follows the sklearn API guidelines for estimators/regressors to provide a familiar interface for data scientists.
Usage:
import sweat
durations = [5, 60, 300, 1200] # Durations in seconds
power = [1300, 600, 330, 290]
# Like all sklearn estimators, the regressor expects a 2 dimensional array of features
X = sweat.array_1d_to_2d(durations)
y = power
pdmreg = sweat.PowerDurationRegressor() # By default the 2 parameter model is used
pdmreg.fit(X, y);
The fitted parametes are then available as attributes on the regressor:
print(f"CP: {pdmreg.cp_}")
print(f"W': {pdmreg.w_prime_}")
To predict power for specific durations using the fitted model:
# Again, the regressor expects a 2 dimensional array of features.
X = sweat.array_1d_to_2d([30, 600, 3600])
print(f"Predicted power values: {pdmreg.predict(X)}")
3 parameter model usage:
import sweat
# Durations in seconds
durations = [5., 60., 300., 1200.]
power = [1300., 600., 330., 290.]
# Like all sklearn estimators, the regressor expects a 2 dimensional array of features.
X = sweat.array_1d_to_2d(durations)
y = power
pdmreg = sweat.PowerDurationRegressor(model="3 param")
pdmreg.fit(X, y)
print(f"CP: {pdmreg.cp_}")
print(f"W': {pdmreg.w_prime_}")
print(f"P_max: {pdmreg.p_max_}")
# Durations in seconds
durations = [30, 600, 3600]
# Again, the regressor expects a 2 dimensional array of features.
X = sweat.array_1d_to_2d(durations)
print(f"Predicted power values: {pdmreg.predict(X)}")
W'balance¶
When the model coefficients CP
and W'
of the 2 parameter cp model (see above) are known, you can model the W'balance.
Usage:
import pandas as pd
import sweat
# First create an artificial workout
artificial_power = 60*[100] + 60*[400] + 30*[100] + 60*[400] + 60*[100]
datetime = pd.to_datetime(list(range(len(artificial_power))), unit="s")
data = pd.DataFrame(dict(power=artificial_power), index=datetime)
# Define the model coefficients
cp = 300
w_prime = 20000
data["W'balance"] = sweat.w_prime_balance(data["power"], cp=cp, w_prime=w_prime).to_list()
data["power"].plot();
data["W'balance"].plot();
By default, the Waterworth implementation of the Skiba algorithm is used. The list of available algorithms is:
- waterworth
- skiba
- froncioni-skiba-clarke
For a comparison of W'balance algorithms, read this blog post.
To use a different algorithm than the default, pass the name of the algorithm to the sweat.w_prime_balance()
function.
Usage:
sweat.w_prime_balance(
power=data["power"],
cp=cp,
w_prime=w_prime,
algorithm="froncioni-skiba-clarke")
Comparison of power duration models¶
Sweatpy has implementations for these power duration models:
- 2 parameter
- 3 parameter
- Exponential
- Omni
import matplotlib.pyplot as plt
import pandas as pd
import sweat
x = [1, 10, 60, 120, 600, 1700, 2250, 2700, 3600]
X = sweat.array_1d_to_2d(x)
y = [1700, 1000, 700, 520, 400, 360, 355, 350, 340]
two_param = sweat.PowerDurationRegressor(model="2 param")
two_param.fit(X, y)
three_param = sweat.PowerDurationRegressor(model="3 param")
three_param.fit(X, y)
exponential = sweat.PowerDurationRegressor(model="exponential")
exponential.fit(X, y)
omni = sweat.PowerDurationRegressor(model="omni")
omni.fit(X, y)
durations = range(0, 3600)
durations_2d = sweat.array_1d_to_2d(durations)
data = pd.DataFrame({
"2 param": two_param.predict(durations_2d),
"3 param": three_param.predict(durations_2d),
"exponential": exponential.predict(durations_2d),
"omni": omni.predict(durations_2d),
})
fig = plt.figure(figsize=(15, 10))
ax = fig.add_subplot(1, 1, 1)
ax.set_xscale('log')
plt.scatter(x, y, color="black")
for model in ["2 param", "3 param", "exponential", "omni"]:
plt.plot(durations, data[model], label=model)
ax.legend();