cfilipov / MuscleBook

[ABANDONED] Muscle Book is an iOS workout tracker for strength training and body building.
GNU General Public License v3.0
42 stars 18 forks source link

[Question] How is "Max Activation" determined? #31

Open JonathanGuberman opened 8 years ago

cfilipov commented 8 years ago

First of all, I meant to call this field simply Activation, because the "max" part isn't entirely true. I renamed the column in the db and a bunch of variables but must have missed the UI label.

There are two overall metrics I found many lifters care about: Volume and Intensity. Volume is simple: reps * sets * weight (and because we log sets individually, the number you see is just reps * weight * 1). Volume is the total weight moved. Intensity is how much of your one-rep max (1rm) you lifted. So if your 1rm for bench press is 200 lbs, but you lifted 100 lbs (regardless of the reps), then you did 50% intensity.

Now volume is a nice number to know, but knowing that you lifted a total of 2,000 lbs some day doesn't give you much info. So I also calculate relative volume. That is, what was the volume of this exercise compared to the most volume you ever lifted for this exercise? So perhaps your highest volume for deadlift was 4,000 lbs, so 2,000 lbs would be 50% relative volume.

We now have two metrics for each set: Intensity (percent of max weight) and Rel. Volume (percent of max volume). The hard part is now trying to figure out how do you describe the overall effort of the whole workout? It would be pointless to total up the volume of all the sets, because deadlift and squat days will make arm days look like low volume in comparison. So what I do is calculate the average intensity for every set of the workout, and the average relative volume for every set of the workout.

Now we have two metrics for each workout: ave. intensity and ave rel. volume. You can use either one to describe your workout, but I need one metric for highlighting the punchcard graph and the anatomy diagram! It's often advised to only crank up either the volume or the intensity for a particular workout, so if I just choose one, lifters would be discouraged from focusing on intensity or volume. What I chose to do is simply take the max of the two metrics. If either one of these are high, then you workout was a high-effort workout.

It's a good idea to take a break for a second and mention one of the motivations behind this app is to help track what many lifter refer to as linear progression (confusingly they don't mean "linear" in the mathematical sense). To make progress lifting you have to push yourself. The ways to push yourself are usually by lifting heavier (intensity) or lifting more (volume). If your goal is to grow or get stronger, then you want to see some >100% activations over time. If you haven't been getting >100% activation, then you have a clue as to why you aren't making progress. Similarly, if your goal is to maintain, staying at 80% is probably the goal. another facet of weight training is periodization. Lifting at >=100% every single workout is counter productive. You need some light "delaod" weeks/days too. The philosophy of MB is that the lifter already knows these concepts and just wants to easily have the necessary figures calculated for them so they can adjust their programming (in the weight training sense) as needed.

The Activation metric is a quantized as follows:

enum Activation {
    case None
    case Light // Below average intensity and volume
    case Medium // Above average intensity or volume
    case High // High intensity or volume (> 80% best)
    case Max // New PR (>100%) or failure set (toggle failure in data entry)
}

Intensity and rel. volume (and the averages) are percentages, here is how they are mapped to activation:

if percent == 0 { self = .None }
else if percent > 1.0 { self = .Max }
else if percent >= 0.8 { self = .High }
else if percent < 0.5 { self = .Light }
else { self = .Medium }

Now some caveats:

If you never tested your 1rm then you don't have the past data needed to figure out you intensity. In this case I just use the RM (the max weight ever lifted, regardless of reps) as a fallback. If that's missing too then you wont have those calculations (though arguable it should be counted as Max because this is your first time doing the exercise, I've been thinking of doing this to encourage trying to workouts since that's a great way to switch things up and should be recorded as higher effort overall for trying something different)

Another thing worth mentioning. The "warmup" toggle in the data entry screen isn't just there for your information. It's also used to exclude that set from the average calculations, otherwise your averages will be weighed down artificially (I'm not actually sure if I'm doing this right now, but it was the intent, if it's including warmups in the cal, it's a bug).

There should also be another heuristic. If you hit a PR during your workout (having any set with >100% intensity) then that should count the whole workout as Max activation, because you hit a freakin PR! You shouldn't have your workout dragged down to High or Medium just because you hit a PR and then decided to do some light curls to cool off.

This is the difficulty with coming up with reasonable metrics about the workout. It's not very clean. There are a lot of special cases that need to be considered. I'd love to hear your thoughts on all this.

cfilipov commented 8 years ago

By the way, all this is obviously focused on weight-based exercises. I haven't had time to figure out how bodyweight or purely time-based (planks) would fit into this. Those are probably easier, just haven't implemented anything for them yet in terms of overall metrics.

Another caveat. Some lifters like to include tempo as part of their intensity calculation. This really complicates things because duration is [will be] optional, so I will have different calculations based on if this data is available. also the question of how the hell does one factor tempo into it at all!? These calculations get pretty complicated, not in the mathematical sense, it's just simple arithmetic, but programming it is just a bunch of special cases based on cumulative data which gets ugly fast.

JonathanGuberman commented 8 years ago

OK, all of this explains why my PR squat today was categorized as "light" (it was my first time recording squats in the app). It might be better if the intensity isn't shown for first-time users, or if it isn't shown until there are a few data points. You also may want to allow new users to set a PR in an exercise to use as a basis of comparison (of course, once historical data can be input or imported, this will be less of an issue).

The comments on you Activation enum say describe "Light" and "Medium" as below and above average, respectively, but they've got nothing to do with average, they're just below 50% maximum and between 50% and 80% maximum.

You do seem to be checking the "warmup" flag. You also set intensity to maximum if the "failure" flag is set. I hadn't realized those were anything more than an annotation for my own reference; very cool!

I think the concept can be applied to bodyweight and time-based exercises by generalizing the concept of "1RM" to "PR," where you looked at the percentage of max reps (for bodyweight) and max duration (for time-based). No need for a volume comparison for those, it'd just be straight up percentage, I think. Bodyweight exercises are a little trickier, though, because many of them can be weighted as well; it's unclear to me whether the intensity of a set of pull-ups or dips should be calculated based on reps or based on a percentage of max weight of the weighted version.

I think tempo is best ignored for this calculation, at least for the time being. It's complicated enough as it is!

cfilipov commented 8 years ago

The whole "Personal Records" section should be hidden in the data entry screen when there is no past data for the selected exercise. It was doing that before, I may have broken that recently or something, that's a bug.

One more problem with the way things are being calculated right now. Let say my best deadlift was 300 lbs. On the workout where I reached that weight, let's say I only did one set of one rep at 300 lbs (1x1@300). Let's also say that in the my best volume was another workout where I lifted 5 sets of 10 reps at 200 lbs (5x10@200), so a total of 10,000 lbs volume. So my PRs here are 300 lbs max weight (100% intensity) and 10,000 lbs max volume. Now a month later I've worked hard and got stronger, so I log a workout where I do 5x5@300. This is "only" 100% of my 1RM, but I've never done this many reps or sets of this exercise, it should count as Max, because 5x5@300 is certainly harder than 1x1@300! But it's also not Max in terms of volume either, because the volume is only 7,500 lbs which is much less than my best volume of 10,000 lbs. The end result is the app considers this workout no different than the one where I did less volume at the same intensity.

Let me know if you can think of a way to represent this. I actually ran into this exact situation recently.

cfilipov commented 8 years ago

By the way, regarding your comment about checking the warmup flag in this code.

Volume and intensity can be measured for a particular set, or for the whole workout (by averaging the vol and intensity for each set as mentioned earlier). Similarly, Activation can refer to the activation of the specific set, or the whole workout. The code you linked to is used to give you the activation for that specific set. When figuring out the activation for the whole workout I use the average of all the intensities and relative volumes for the workout and take the bigger of the two. In other words, the activation for the workout is not based on the set's activation fields but on the data that field is based on (because doing it on the activation of each set loses some information about the set).

This is where the activation for the whole workout is calculated. You might not be familiar with Swift, but if you squint your eyes and look at it just right you can see it's just SQL in a funny syntax. Notice there's no predicate in the where clause (pronounced filter in SQLite.swift dsl) to exclude rows where warmup == true.

I will need to fix this because this is going to make you average lower than it should. Similarly for Failure.

Re: the comments on the Activation enum. The old saying goes, the only thing worse than no comments are out of date or misleading comments! I used to use above/below average for activation but later switched to the more common %1RM. There are so many things to do...