-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calculate athlete Critical Power? #25
Comments
After playing with this a little more and looking at some of the test cases, I think I may have figured this out... However am still a little confused from the results
Why is p_max (returned from the model 312) not the same as the actual power max from the df (342)? |
@AartGoossens hope you may be able to chime in on the above? I’m sure it’s something silly I’m doing... thanks! |
Thanks for the response @AartGoossens! Happy to help out with the project once you start it up again. I'm a little new to the statistics portions of all of this, but have enough python experience to code some stuff out once I understand the math... So ultimately what I am trying to do is build a small personal app that can run the CP model every day on a rolling period (perhaps rolling 90 days) and show me what my CP is every day based on the best efforts from that passed rolling time period. I'm building out my own charts which pull in metrics from a lot of other data sources which golden cheetah does not leverage, however I still want to use the golden cheetah metrics, hence why I am trying to replicate them... Here's an example of a modified PMC which is also incorporating HRV data and readiness scores from my oura ring, as well as non ride/run workouts which leverage HRss or weightlifting "WSS" (which I am calculating on my own), which can all be toggled with the top 3 teal buttons to show activity type specific CTL (fitness), but leverage ATL (fatigue) across all workouts (credits to Elevate app for some of my design choices): Since my original post I have tried out a few other tests, with larger date frames, however am still not able to match the numbers to what is showing in Golden Cheetah... The dataset being tested against is from 01/01/2020 onward, and should match to the model params shown in golden cheetah: Test 1 (fail):
Golden Cheetah CP (247) != Python CP (183) Test 2 (fail):
Golden Cheetah CP (247) != Python CP (189) As you can see, the outputs from the models above are not match what is showing in Golden Cheetah (screenshots above). So I am a little stuck here now, as I'm not sure what data (and how) I should be passing to the models... It is also worth mentioning that I am not defining an As a side note, I do have several other modules where your code has helped me so far, specifically with a lot of the "TrainingPeaks" metrics: So thanks for sharing all of this code in the first place! |
Really cool screenshots of your app! It is very interesting how you are combining training data and data from Oura. And the CP model fitting problem is interesting too. And I think the answer lies in which data GC exactly uses for the model fitting: Firstly, I think GC only uses a subset of your activities when you select "Data to fit: Performance tests". I expect that the other option ("MMP bests") will use all your activities. But I cannot find in the source code (should be somewhere here) what GC uses to recognize/tag performance tests, so I am not 100% sure. Secondly, the "search interval" settings seems to specify a range of data on which the model is fitted. But, again, I cannot find the exact mechanism in the source code. Maybe @liversedge can way in on this? Can you give us some pointers to how/where these settings are applied to the model fitting? Finally (and maybe you are already aware of this, but wanted to mention it just to be sure), GoldenCheetah does not use this library under the hood, so it might very well be possible that you will never get exactly the same results as in GC. |
In general we do not fit CP models to points beyond 20 minutes as they tend to be submaximal. |
The envelope fit for the extended cp model is complicated, but basically it tries to fit pmax using short duration window and cp using longer duration window and iteratively / brute force fits. |
@AartGoossens thanks for making the connection here with Mark. 1 last question for you, is it accurate for me to be setting the the time index to start at 1, rather than 0? (where I am setting @liversedge, what exactly do you mean by filtering out submax? Per your comment about filtering out intervals greater than 20 mins for the CP3 model, I'm getting much closer... With the following, I am getting a CP3 of 257W, where Golden Cheetah is showing a CP of 253W,
Unfortunately when I run the CP2 though, there's a little more of a gap, with Golden Cheetah showing 261, but the model giving me 280
|
In any MMP curve the vast majority of points will not be maximal - unless you go out and do maximal efforts for every duration from 1s - 20mins. You can eyeball this by looking at how bumpy the curve is and also the lack of a definable S shape might also suggest an absence of short maximal efforts. If you fit to a curve dominated by submax efforts your estimates will bias low. |
Filtering out >20mins will reduce low bias as almost noone does truly maximal efforts that long and even if they do they add little to the fit vs the 20 min efforts anyway since the curve is very flat from 20 mins out. |
Ah I see thanks for explaining this - right now it is indeed fitting to ALL data points under 20 mins. My goal here is to monitor how critical power is changing over time by using a rolling 90 day window to capture all workouts in this period, MMP all of these workouts, and then take the best efforts of each interval in this dataset (under 20 mins right now for the CP3) to feed to the model. My theory was that using a window of this size would allow for a large enough dataset where taking the max of all of these workouts MMPs would inherently filter out submax, but is there a additional way I can identify these submax data points programmatically? What’s the approach in golden cheetah if no workouts are marked as performance tests and the user is running the model on the “MMP” setting? Thanks! |
In the CP chart you can enable filtering in option, the PDModel.cpp has details on how the estimator works (e.g. truncting >1200s) |
Yes that makes sense because your first mmp value is actually your 1s max power. |
@AartGoossens I'm not sure if it is effecting it, but it looks like the initial parameters do not match up with what is in Golden Cheetah: https://github.com/GoldenCheetah/sweatpy/blob/master/sweat/pdm/critical_power.py#L98 |
I see- you are trying to replicate the results that gc gives in your own tool. |
Alright thanks for the help thus far... Really I'm just trying to replicate the results from GC with this sub-repo within GC. Unfortunately I'm a little new to coding (self-taught and using this to enhance) so have gone through the GC code as best I can, but from what I can tell the query I'm currently running matches with the dataset GC sends to the 3CP model. The only other difference I was able to find was the initial parameters the two libraries use mentioned above, so hopefully @AartGoossens can take a look once he gets a chance |
No probs, I think you are on a bit of a journey- coding, modelling, critical power model and diagnosing modelling errors. Would recommend familiarising yourself with basic modelling concepts and some of the literature on CP. The Jones (2010) review of CP is the best paper to read, its excellent and a few others spring to mind. Drop me an email liversedge gmail com and I'll forward you some stuff. |
Thanks will do! |
Opening up the code if you guys are interested in the charts: |
I've been playing with this library for a little bit now and it seems like the majority of models I am trying to run require an athlete to be defined for the df.
I've been able to do this, but can't figure out how to calculate the CP for that athlete.
Is calculating the CP for an athlete based off previous workout data something this library supports, or is it required that you define the CP for the athlete when assigning to a df.
The code shows
cp
as an optional argument, but leaving it blank causes a runtime error:The text was updated successfully, but these errors were encountered: