UltraChampsaur Race
On July 2022, I ran a moutain race of about ~30km / ~1500m height denivelation. Due to pandemic that was my first race since Oct-2019, 2 1/2 years before.


The race went well. I was ahead of my teammates for most of the race until Pascal joined me with 6 km to go and we crossed the finish line together (he actually waited for me in the last few kilometers to make this friendly finish, thanks to him). I felt tired during the last climb (see the summit on the left at km 20 below) and the descent which is the last third of the race.

Import data
To benchmark the performances I decided to download Chronosport data (excel file) which I open using JMP.

For some reason, the time at Piolit (column "Piolit 1") and the time at arrival (column "Temps") were not defined in the same way on the excel file and one was recognized as a continuous variable ("Temps" with a blue triangle) while the other was recognized as a categorical variable ("Piolit 1" with red bars).
Don’t worry, it was the perfect time to put into practice the new lessons learned in the JMP scripting guide I'm reading... Don't be afraid, I'm not a geek, I read novels in my spare time and learn scripting during my work hours.
Transform data : column formula editor
I wanted to have time in minutes.
Transforming the continuous variable "Temps = 2:32:43" (not my time unfortunately because it took me 2 hours 10 minutes more to finish) into 153 minutes is simple with this formula.

In Minutes() transforms a time in minutes, the same transform exists for years, months, week, … seconds.
Transforming the categorical variable “Piolit 1 = 1:54:57” (still not my time but the winner's) in 115 minutes was a good opportunity to use Regex() which is a wonderful way to deal with complex categorical variables (my example is actually quite simple).

The main point is to identify the various groups composing your input by using
(Regex( :Piolit 1, "(\d)(\W)(\d\d)(\W)(\d\d)")).
:Piolit 1 is the column variable. Regex() aim to identify groups in this column variable, each pair of parentheses being a group. Here we are looking for 5 groups:
- Group 1 = (\d) = the hours which contains 1 digit only
- Group 3 = (\d\d) = the minutes which contains 2 digits
- Group 5 = (\d\d) = the seconds which contains 2 digits
- Group 2 and Group 4 = (\W) = the “:” separator in between hour:minutes and minutes:seconds (the code (\W) actually recognizes non-alphanumeric characters)
Once we’ve done that, we reuse Groups 1, 3 & 5 :
- Group 1 for hours: Num(Regex(:Piolit 1,"(\d)(\W)(\d\d)(\W)(\d\d)","\1")) * 60
You replace :Piolit 1 (1:54:57 in row 1) by Group 1 (that’s what the “\1” stand for) transform it as a number (that’s what the Num does) and mutliply by 60 (that’s what the * 60 does).
- Group 3 for minutes: Num(Regex(:Piolit 1,"(\d)(\W)(\d\d)(\W)(\d\d)","\3" ) )
You replace :Piolit 1 (1:54:57 in row 1) by Group 3 (that’s what the “\3” stand for) transform it as a number (that’s what the Num does).
- Group 5 for seconds: Num(Regex(:Piolit 1,"(\d)(\W)(\d\d)(\W)(\d\d)" ,"\5")) / 60
You replace :Piolit 1 (1:54:57 in row 1) by Group 5 (that’s what the “\5” stand for) transform it as a number (that’s what the Num does) and divide by 60 (that’s what the / 60 does).
At the end, you add up the 3 components and round it up (that’s what the Round does) to get the 115 minutes (still the winner I am far from it).
Rank at arrival : graph builder
First of all I evaluated our performance vs.
a) all runners b) men only c) our category (M3H = 50-55 years old)


I did the calculation as a percentage rather than an absolute number so I divided
- the rank calculated on the time at arrival using Col Rank(:"Arrival (min)"n)
- by the number of event within the same column (“Arrival (min)”) using Col Number(:"Arrival min)"n).
For the sub-categories I’ve added a “by variable” so the calculation is done within the column by considering the level of another variable as instance
- b) “Sx” = “Male” or “Female”
- c) “Cat” = “SEF”, “SHE”, … “M6F”, “M8H” depending on gender and age.
The benefit being that you don’t need multiple formula calculations or complex if/then statements and more importantly there is no risk of error as the calculation is done column wise considering the row wise level.
The graph below highlighted on our category illustrate the rank % with the two of us selected for detailed analysis:

Ultimately the % reflect our position:
- 52% vs. all runners so we are “average” runners
- 60% vs. all male runners so we are under performant male
- 40% compared to male runners between 50 and 55 years old, which means that our performance is slightly better than that of men our age
Intermediate performance (at Piolit summit) vs. Arrival : graph builder
Now I have my data in the same format (time in minutes for both Piolit and Arrival) I did a scatterplot vizualization:

There is a diagonal relationship between these two variables, indicating that individuals have remained roughly constant over the last third of the race with no major changes.
I draw a fit line (after excluding the last runner which looks like an outlier) on the data and save the prediction formula as well as residuals to figure out if I underperformed during the last third (that was my feeling) and/or if Pascal overperformed (I suspected).

It is interesting to note that I am the "average" man with a residual = -0.2, which means that my performance is “normal”, while Pascal has a negative residual (-4.4 minutes), which means that he took less time compared to the other runners on the last third. “He still had some under his feet” (not sure it is the right expression in english but I think everyone can understand) and remember that he waited me during the last third so he actually should have done an even better job.
I also evaluated the difference in rank with an additional column formula:

and evaluated the rank we gained or lost compared to the other runners:

Not surprisingly, Pascal gained 10 ranks.
More surprisingly I gained 6 ranks. This means that
- globally, based on the residuals of the overall line of fit, I’m the “average” man
- BUT locally (compared to the few runners I was running/competing with) I did a decent job during the last third.
I hope this blog post will help you understand some of the formula editor functions available in JMP, from the simple to the more complex (once you start using Regex () you can do great things) as well as some data visualizations/analysis.
To be honest, I don't usually over-analyze my "performance", but I thought this was a good use case to exercise some of the benefits of JMP using non-industrial data to have more fun.