Project Space

Author

Brady Baur

Building the Perfect Jump

Background Information

The goal of this observational study is to identify different contributing driver metrics to output metrics like jump height. The database originates from Hawkins Dynamics force plate which capture a plethora of data points during an athletes jump. The database uses and “ODS” system to categorize metrics. This separates the variables into three categories, Output, Driver, and Strategy. This allows us to look for clues to maximize output metrics in both large groups and individuals.

For this exercise, we have a group of baseball players. We are specifically looking to create the perfect jump for a pitcher. The sport is unique in which there is no time frame for a pitcher. In sports like basketball, football, or volleyball, the elastic ability to react to a ball or opposing player becomes essential altering the time frame in which the athlete can generate momentum and force. Without that worry we can look to maximize things like Peak propulsive force.

Lastly, in order to use the data the clean_names command and removal of the “NA” data was completed.

library(openintro)
Loading required package: airports
Loading required package: cherryblossom
Loading required package: usdata
library(readr)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ purrr     1.0.2
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
Jumps <- read_csv("Data/Starters_Baseball-04_27_22-02_21_24--averagesOnly_Countermovement_Jump - Force Plate Average Data.csv")
Rows: 780 Columns: 88
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (8): TestId, Date, Time, Name, Segment, Position, Type, Tags
dbl (79): System Weight, Jump Height, Jump Momentum, Countermovement Depth, ...
lgl  (1): Excluded

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cleaned_jumps <- read_csv("Data/cleaned_jumps.csv")
Rows: 780 Columns: 88
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (8): test_id, date, time, name, segment, position, type, tags
dbl (79): system_weight, jump_height, jump_momentum, countermovement_depth, ...
lgl  (1): excluded

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ppfv2 <- read_csv("Data/cleaned_jumps.csv")
Rows: 780 Columns: 88
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (8): test_id, date, time, name, segment, position, type, tags
dbl (79): system_weight, jump_height, jump_momentum, countermovement_depth, ...
lgl  (1): excluded

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cleaned_jumps |>
  janitor::clean_names() |>
  filter(test_id != "NA")
# A tibble: 390 × 88
   test_id date  time  name  segment position type  excluded tags  system_weight
   <chr>   <chr> <chr> <chr> <chr>   <chr>    <chr> <lgl>    <chr>         <dbl>
 1 AVERAGE 21/0… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           770.
 2 AVERAGE 3/1/… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           804.
 3 AVERAGE 22/1… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           790.
 4 AVERAGE 17/1… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           778.
 5 AVERAGE 23/0… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           742.
 6 AVERAGE 21/0… N/A   AJ L… N/A     <NA>     Coun… NA       <NA>           887.
 7 AVERAGE 14/0… N/A   AJ L… N/A     <NA>     Coun… NA       <NA>           883.
 8 AVERAGE 7/7/… N/A   AJ L… N/A     <NA>     Coun… NA       <NA>           892.
 9 AVERAGE 19/0… N/A   AJ L… N/A     <NA>     Coun… NA       <NA>           894.
10 AVERAGE 7/6/… N/A   AJ L… N/A     <NA>     Coun… NA       <NA>           893.
# ℹ 380 more rows
# ℹ 78 more variables: jump_height <dbl>, jump_momentum <dbl>,
#   countermovement_depth <dbl>, braking_rfd <dbl>, stiffness <dbl>,
#   force_at_min_displacement <dbl>, relative_force_at_min_displacement <dbl>,
#   avg_braking_force <dbl>, avg_relative_braking_force <dbl>,
#   peak_braking_force <dbl>, peak_relative_braking_force <dbl>,
#   avg_propulsive_force <dbl>, avg_relative_propulsive_force <dbl>, …

Firstly, Is Peak Propulsive force a good predictor of other Output Metrics?

ggplot(data = ppfv2, 
       mapping = aes(x = peak_propulsive_force,
                  y = jump_momentum,
                  color = jump_height)) +
  geom_point() +
  labs(title = "Propulsive Force's Connection to Output Metrics",
  x = "Peak Propulsive Force",
  y = "Jump Momentum",
  color = "Jump Height")+
   geom_smooth(method = "lm",
               color = "orange")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 390 rows containing non-finite values (`stat_smooth()`).
Warning: Removed 390 rows containing missing values (`geom_point()`).

ppfv2 <- write_csv(cleaned_jumps, "Data/ppfv2.csv")
ppfv2 <- read_csv("Data/ppfv2.csv")
Rows: 780 Columns: 88
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (8): test_id, date, time, name, segment, position, type, tags
dbl (79): system_weight, jump_height, jump_momentum, countermovement_depth, ...
lgl  (1): excluded

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ppfv3 <- write_csv(ppfv2, "Data/ppfv3.csv")
ppfv3 <- read_csv("Data/ppfv3.csv")
Rows: 780 Columns: 88
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (8): test_id, date, time, name, segment, position, type, tags
dbl (79): system_weight, jump_height, jump_momentum, countermovement_depth, ...
lgl  (1): excluded

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ppfv3 <- ppfv2 |>
  filter(system_weight != "NA")
pro_JM <- cor(ppfv3$peak_propulsive_force, ppfv3$jump_momentum)

As shown, There is almost a direct correlation between jump momentum and PPF. There is a slightly less obvious but still relatively strong relationship between PPF and jump height. Utilizing jump momentum is important because it takes into account the athletes weight and creates an even playing field as a heavier athlete may create the same amount of force at takeoff but won’t reach as high of a maximum jump height.

Building the Ideal Jump

Firstly, each jump consists of five phases. For this observational study we will look at the three that occur before takeoff. The question being asked: Is there an ideal amount of time to spend in each phase to maximize force output?

look for correlation between jump height, momentum, or peak propulsive power and each phase’s lengths

CMJ Handbook: https://app.hubspot.com/documents/5212164/view/729954794?accessId=b55ee5

jump_phase <- cleaned_jumps |>
  select(peak_propulsive_force, jump_height, jump_momentum, unweighting_phase_percent, braking_phase_percent, propulsive_phase_percent) |>
  filter(peak_propulsive_force != "NA")
cleaned_jumps
# A tibble: 780 × 88
   test_id date  time  name  segment position type  excluded tags  system_weight
   <chr>   <chr> <chr> <chr> <chr>   <chr>    <chr> <lgl>    <chr>         <dbl>
 1 AVERAGE 21/0… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           770.
 2 <NA>    <NA>  <NA>  <NA>  <NA>    <NA>     <NA>  NA       <NA>            NA 
 3 AVERAGE 3/1/… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           804.
 4 <NA>    <NA>  <NA>  <NA>  <NA>    <NA>     <NA>  NA       <NA>            NA 
 5 AVERAGE 22/1… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           790.
 6 <NA>    <NA>  <NA>  <NA>  <NA>    <NA>     <NA>  NA       <NA>            NA 
 7 AVERAGE 17/1… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           778.
 8 <NA>    <NA>  <NA>  <NA>  <NA>    <NA>     <NA>  NA       <NA>            NA 
 9 AVERAGE 23/0… N/A   AJ G… N/A     <NA>     Coun… NA       <NA>           742.
10 <NA>    <NA>  <NA>  <NA>  <NA>    <NA>     <NA>  NA       <NA>            NA 
# ℹ 770 more rows
# ℹ 78 more variables: jump_height <dbl>, jump_momentum <dbl>,
#   countermovement_depth <dbl>, braking_rfd <dbl>, stiffness <dbl>,
#   force_at_min_displacement <dbl>, relative_force_at_min_displacement <dbl>,
#   avg_braking_force <dbl>, avg_relative_braking_force <dbl>,
#   peak_braking_force <dbl>, peak_relative_braking_force <dbl>,
#   avg_propulsive_force <dbl>, avg_relative_propulsive_force <dbl>, …
ggplot(data = jump_phase, mapping = aes(x = unweighting_phase_percent,
                                        y = peak_propulsive_force)) +
  geom_point(color = rgb(.9,.5,0)) +
    labs(title = "The Unweighting Phase",
  x = "Peak Propulsive Force",
  y = "% of time Spent in Unweighting Phase") +
   geom_smooth(method = "lm",
               color = "black")
`geom_smooth()` using formula = 'y ~ x'

Unw_JM <- cor(jump_phase$unweighting_phase_percent, jump_phase$jump_momentum)
Unw_JM
[1] 0.2072974
ggplot(data = jump_phase, mapping = aes(x = braking_phase_percent,
                                        y = peak_propulsive_force)) +
  geom_point(color = rgb(.7,.1,.1)) +
    labs(title = "The Braking Phase",
  x = "% of time Spent in Braking Phase",
  y = "Jump Momentum",
  color = "Jump Height") +
  scale_x_continuous(
     limits = c(13, 35)) +
   geom_smooth(method = "lm",
                 color = "black")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 1 rows containing non-finite values (`stat_smooth()`).
Warning: Removed 1 rows containing missing values (`geom_point()`).

Brk_JM <- cor(jump_phase$braking_phase_percent, jump_phase$jump_momentum)
Brk_JM
[1] -0.2100204
ggplot(data = jump_phase, mapping = aes(x = propulsive_phase_percent,
                                        y = peak_propulsive_force,)) +
  geom_point(  color = rgb(0,.5,1)) +
  labs(title = "The Propulsive Phase",
  x = "% of time Spent in Propulsive Phase",
  y = "Jump Momentum") +
   scale_x_continuous(
     limits = c(25, 45)) +
   geom_smooth(method = "lm",
                 color = "black")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 1 rows containing non-finite values (`stat_smooth()`).
Warning: Removed 1 rows containing missing values (`geom_point()`).

Prop_JM <- cor(jump_phase$propulsive_phase_percent, jump_phase$jump_momentum)
Prop_JM
[1] -0.110741
tibble::tibble(pro_JM, Brk_JM,Prop_JM, Unw_JM)
# A tibble: 1 × 4
  pro_JM Brk_JM Prop_JM Unw_JM
   <dbl>  <dbl>   <dbl>  <dbl>
1  0.927 -0.210  -0.111  0.207

We can conclude from this data two things:

1) Different athletes have different moving strategies leading to massive variations in results. A taller slower athletes make take a significant amount of time to gather force and will continue to accelerate throughout the entire propulsive phase. A shorter athlete may reach their peak velocity right out of the braking phase but acceleration will plateau during the propulsive phase leading to a higher percentage of time spent in the propulsive phase.

2) Even with variation we can see that as jump momentum increases, time spent in the braking and propulsive phases decreases. This makes sense from a physics stand point as the more force you can put into the ground the more force the ground will put back into you. With the braking phase we also see that the faster an athlete can change directions the more force they can put out as well. Being able to “catch” the bodies weight on the way down and send it back up is important for force output.

The Landing Phase

With a little background knowledge from the Hawkins Dynamics Counter-Movement Jump Playbook we learn that when landing, less peak force is better. We also know distributing the force evenly to both legs is as important as distributing the force throughout the landing phase.

L_phase <- cleaned_jumps |>
  select(l_r_peak_landing_force, peak_landing_force, stiffness, system_weight, name)
landing_force_perc <- L_phase |>
  filter(system_weight != "NA")  |>
  mutate(peak_landing_force/system_weight) |>
  rename(porp_landing_force = "peak_landing_force/system_weight")
landing_force_perc
# A tibble: 390 × 6
   l_r_peak_landing_force peak_landing_force stiffness system_weight name     
                    <dbl>              <dbl>     <dbl>         <dbl> <chr>    
 1                  5.88               4127.    -4199.          770. AJ Galvin
 2                 -0.360              5649.    -4250.          804. AJ Galvin
 3                 -6.37               5022.    -4006.          790. AJ Galvin
 4                -14.2                4981.    -4088.          778. AJ Galvin
 5                 -7.02               4919.    -5016.          742. AJ Galvin
 6                  6.59               6422.    -5524.          887. AJ Lee   
 7                  5.91               6098.    -5397.          883. AJ Lee   
 8                  9.37               6243.    -5168.          892. AJ Lee   
 9                  6.92               6527.    -4996.          894. AJ Lee   
10                  0.252              5751.    -4611.          893. AJ Lee   
# ℹ 380 more rows
# ℹ 1 more variable: porp_landing_force <dbl>
ggplot(data = landing_force_perc, aes(x = porp_landing_force)) +
  geom_histogram(binwidth = 0.5,
                 color = "black",
                 fill = rgb(.2,.2,1)) +
  labs(title = "Distribution of Athletes % Landing Force",
       x = "Proportion of Landing Force/System Weight",
       y = "# of Jumps")

Hawkins Dynamics states in their guide that a peak force of more than 5x your body weight is unsafe. However, most athletes can get away with up to 7x their body weight especially baseball players who are not jumping hundreds of times a game like a basketball player might. Poor landing mechanics are still a concern as the bodies ability to transfer and absorb force is an important piece for baseball especially in the lead landing leg for pitchers.

landing_force_perc |>
  filter(porp_landing_force > 7.5)
# A tibble: 87 × 6
   l_r_peak_landing_force peak_landing_force stiffness system_weight name       
                    <dbl>              <dbl>     <dbl>         <dbl> <chr>      
 1                 -6.78               6896.    -4557.          842. Aidan Mald…
 2                -19.5                7272     -4320.          840. Blake Crea…
 3                  3.99               6645     -4412.          822. Brady Baur 
 4                 -8.15               6194.    -4514.          793. Brady Baur 
 5                 -9.45               6766.    -4224.          792. Brady Baur 
 6                  0.743              6455.    -4565.          794. Brady Baur 
 7                  1.89               6385     -4871.          788. Brady Baur 
 8                 -4.63               5971.    -4147.          771. Brady Baur 
 9                 -7.08               5980.    -4231.          762. Brady Baur 
10                 -5.23               4625     -3406.          575. Braydon Fi…
# ℹ 77 more rows
# ℹ 1 more variable: porp_landing_force <dbl>

Using this function we can identify athletes who may be at risk for injury. The demands for sports are high and an athlete with a slightly aggressive landing is not the end of the world. However, if pain arises we now have a box to look into as a possible source of something like knee pain.

landing_force_perc |>
  filter(porp_landing_force > 10)
# A tibble: 7 × 6
  l_r_peak_landing_force peak_landing_force stiffness system_weight name        
                   <dbl>              <dbl>     <dbl>         <dbl> <chr>       
1                   6.53              9008     -5344.          839. Dayne McNau…
2                  -5.20             10096.    -4087.          831. JP Massey   
3                  27.8               7568     -3659.          756. Oliver Giel 
4                  19.5               7980     -3970.          724. Oliver Giel 
5                  18.5               9259     -4151.          762. Seth Betts  
6                  10.0               9454.    -4444.          774. Seth Betts  
7                 -11.3               8288     -5629.          824. Tai Atkins  
# ℹ 1 more variable: porp_landing_force <dbl>

We can look even farther and see those who fall into the “Red” category which is ver high risk for injury. It may be in the coach’s best interest to look for mechanical flaws like the caving in of the knees during the landing phase.

Concluding Thoughts

The world of comprehending data especially in terms of athletes is an endless sea of rabbit holes. In the baseball world people are continuously trying to justify different metrics to prove their correctness. With what we have collected and found here is that we can use data to support our claims but its important to remember the context in which the data is collected. You can’t cookie cutter every athlete into moving the same just because the data supports it.

Citations

Hawkin Metric Database, www.hawkindynamics.com/hawkin-metric-database. Accessed May 2024.

HubSpot, Inc. Hubstop, app.hubspot.com/documents/5212164/view/729954794?accessId=b55ee5. Accessed 14 May 2024.

McMahon, J. J., Suchomel, T. J., Lake, J. P., & Comfort, P. (2018). Understanding the Key Phases of the Countermovement Jump Force-Time Curve. Strength and Conditioning Journal, 40(4), 96-106. https://doi.org/10/1519/ssc.00000000000375