{"id":1777,"date":"2020-07-16T20:24:06","date_gmt":"2020-07-16T20:24:06","guid":{"rendered":"http:\/\/optimumsportsperformance.com\/blog\/?p=1777"},"modified":"2020-07-16T20:54:17","modified_gmt":"2020-07-16T20:54:17","slug":"r-tips-tricks-force-velocity-power-profile-graphs-in-r-shiny","status":"publish","type":"post","link":"https:\/\/optimumsportsperformance.com\/blog\/r-tips-tricks-force-velocity-power-profile-graphs-in-r-shiny\/","title":{"rendered":"R Tips &#038; Tricks: Force-Velocity-Power Profile Graphs in R Shiny"},"content":{"rendered":"<p>A colleague of mine was asking about how he could produce plots of force-velocity-power profiles for his coaches based on their <span style=\"color: #0000ff;\"><strong><a style=\"color: #0000ff;\" href=\"https:\/\/gymaware.com\/products\/\">Gymaware<\/a><\/strong><\/span> testing. Rather than plotting static plots for this stuff it is sometimes easier to build out a nice shiny app so that the coaches can interact with it or the practitioner can quickly change between players or position groups when giving a presentation to the staff.<\/p>\n<p>All code and data is available at my <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\">GITHUB<\/a><\/span><\/strong> page (<strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/FV_Profile_Data_Viz.R\">R Script<\/a><\/span><\/strong> and <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/FV_Profile_Data_Viz.csv\">Data<\/a><\/span><\/strong>).<\/p>\n<p>This tutorial will cover:<\/p>\n<p>1) Building polynomial regression to represent the average team trend<br \/>\n2) Iterative approaches to building static plots<br \/>\n3) Iterative approaches to building Shiny web apps<\/p>\n<p>This tutorial has a number of different iterations of plots and web apps so working through the R-code on your own is advised so you can see how every step is performed.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Data<\/strong><\/span><\/p>\n<p>After loading the two required packages, {<span style=\"color: #0000ff;\"><strong>tidyverse<\/strong><\/span>} and {<span style=\"color: #0000ff;\"><strong>shiny<\/strong><\/span>}, we load in the data set and we see that it consists of Force, Power, and Velocity values across 5 different external loads for 3 different athletes:<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-12.50.50-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1778\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-12.50.50-PM.png\" alt=\"\" width=\"314\" height=\"335\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-12.50.50-PM.png 540w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-12.50.50-PM-281x300.png 281w\" sizes=\"auto, (max-width: 314px) 100vw, 314px\" \/><\/a><\/p>\n<p>If you want to use the R-script to produce reports for yourself going forward just ensure that your data has the above columns (named the same way, since R is case-sensitive). If you have data with different column names, your two choices are: (1) change my code to match the column names of your data; or, (2) once you pull the data into R change the columns names in your code to match mine.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Average Trend Line (2nd Order Polynomial)<\/strong><\/span><\/p>\n<p>Eventually, we are going to want to compare our athletes to the team average (or position group average if your sport is more heterogeneous). This type of data is often modeled as a. 2nd order polynomial regression. Thus, we will build this type of regression to predict both Velocity and Power from Force. Once I have these two regressions built I can create a data frame that consists of a sequence of Force values (from the minimum to the maximum observed in the team&#8217;s data) and predict the velocity and power at each unit of force.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nfit_velo &lt;- lm(Velocity ~ poly(Force, 2), data = fv_profile)\r\nfit_power &lt;- lm(Power ~ poly(Force, 2), data = fv_profile)\r\n\r\navg_tbl &lt;- data.frame(Force = seq(from = min(fv_profile$Force),\r\n                                  to = max(fv_profile$Force),\r\n                                  by = 0.5))\r\n\r\navg_tbl$Velocity_Avg &lt;- predict(fit_velo, newdata = avg_tbl)\r\navg_tbl$Power_Avg &lt;- predict(fit_power, newdata = avg_tbl)\r\ncolnames(avg_tbl)&#x5B;1] &lt;- &quot;Force_Grp&quot;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Static Plots<\/strong><\/span><\/p>\n<p>I&#8217;ll walk through a few iterations of static plots. See the <strong><span style=\"color: #0000ff;\">GITHUB<\/span><\/strong> code to walk through how these were produced.<\/p>\n<p><em>1) All athletes with an average team trend line<\/em><\/p>\n<p>This plot gives us a sense for the trend in Velocity as it relates to increasing amounts of force. Below you will see one with a solid trend line and one with a dashed trend line. Feel free to use which ever one you prefer.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1779\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team.png\" alt=\"\" width=\"335\" height=\"311\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team-624x579.png 624w\" sizes=\"auto, (max-width: 335px) 100vw, 335px\" \/><\/a><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team_dotted.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1780\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team_dotted.png\" alt=\"\" width=\"333\" height=\"309\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team_dotted.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team_dotted-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/team_dotted-624x579.png 624w\" sizes=\"auto, (max-width: 333px) 100vw, 333px\" \/><\/a><\/p>\n<p><em>2) Team trend for Velocity and Power<\/em><\/p>\n<p>The common way this type of data is visualized in the sports science literature it is a bit tricky in R because it requires a dual y-axis. To obtain this, within my <span style=\"color: #0000ff;\"><strong>ggplot<\/strong><\/span> call I shrink Power down to a scale more similar to Velocity (the main y-axis) by dividing it by 1000. Then when I call the second y-axis with the <span style=\"color: #0000ff;\"><strong>sec_axis() <\/strong><span style=\"color: #000000;\">function I multiply Power by 1000 to put it back on its normal scale.<\/span><\/span><\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/fvp_team.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1781\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/fvp_team.png\" alt=\"\" width=\"426\" height=\"395\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/fvp_team.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/fvp_team-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/fvp_team-624x579.png 624w\" sizes=\"auto, (max-width: 426px) 100vw, 426px\" \/><\/a><\/p>\n<p><em>3) Accounting for individuals<\/em><\/p>\n<p>The above plots are a look at the entire team (in this case only 3 athletes). However, we may want to look at the individuals more explicitly. As such, we build four plots to show individual differences:<\/p>\n<p>1) All athletes all on the same plot with their corresponding individualized trend lines <strong>(NOTE:<\/strong> if you have a lot of athletes, this plot can get pretty busy and ultimately become useless).<br \/>\n2) Plot each individual as a facet to look at the athletes separately.<br \/>\n3) Create the same plot as #2 but add in the group average trend line (which we created using our 2nd order polynomial regression) to allow us to compare each athlete to the groupx.<br \/>\n4) Plot each individual as a facet with velocity and power on separate y-axes.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1782\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy.png\" alt=\"\" width=\"407\" height=\"378\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy-624x579.png 624w\" sizes=\"auto, (max-width: 407px) 100vw, 407px\" \/><\/a><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Rplot.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1783\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Rplot.png\" alt=\"\" width=\"426\" height=\"395\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Rplot.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Rplot-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Rplot-624x579.png 624w\" sizes=\"auto, (max-width: 426px) 100vw, 426px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_f_t.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1784\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_f_t.png\" alt=\"\" width=\"472\" height=\"438\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_f_t.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_f_t-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_f_t-624x579.png 624w\" sizes=\"auto, (max-width: 472px) 100vw, 472px\" \/><\/a><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_fvp.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter  wp-image-1785\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_fvp.png\" alt=\"\" width=\"495\" height=\"459\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_fvp.png 706w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_fvp-300x278.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/indy_fvp-624x579.png 624w\" sizes=\"auto, (max-width: 495px) 100vw, 495px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Shiny App Development<\/strong><\/span><\/p>\n<p>The still figures above are nice but making things interactive is much more useful. I have four {<span style=\"color: #0000ff;\"><strong>shiny<\/strong><\/span>} web iterations that we can go through. Again, using the R code while reading this will help you understand what is going on under the hood. Additionally, you&#8217;ll want to run these in R so that R can open up the webpage on your computer and allow you to play with the app. Below is just still shots of each app iteration.<\/p>\n<p><em>1) Version 1: Independent Player Plots<\/em><\/p>\n<p>This version allows you to select one player at a time.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1786\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM-1024x601.png\" alt=\"\" width=\"625\" height=\"367\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM-1024x601.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM-300x176.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM-768x450.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM-624x366.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.13.13-PM.png 1746w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><em>2) Version 2: Add Players to Facets<\/em><\/p>\n<p>This version lets you select however many players you want and it will add them in as facets. This is a useful app if you are presenting to the staff and want to select or de-select several players to show how they compare to each other.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1787\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM-1024x579.png\" alt=\"\" width=\"625\" height=\"353\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM-1024x579.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM-300x170.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM-768x434.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM-624x353.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.15.11-PM.png 1800w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><em>3) Version 3: Same as Version 2 but add Power to the Plot on a second y-axis<\/em><\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1788\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM-1024x577.png\" alt=\"\" width=\"625\" height=\"352\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM-1024x577.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM-300x169.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM-768x433.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM-624x352.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.16.17-PM.png 1796w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><em>4) Version 4: Combine all plot details<\/em><\/p>\n<p>Version 4 combines everything from this tutorial in one single web app. You can select the\u00a0 force-velocity-power profile for individual athletes (added to the plot as facets) and see the team average trend line (added to the plot as a dashed line) for velocity and power, to allow you to make comparisons between each player and to the group average.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1789\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM-1024x595.png\" alt=\"\" width=\"625\" height=\"363\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM-1024x595.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM-300x174.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM-768x446.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM-624x363.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-16-at-1.18.59-PM.png 1782w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Conclusion<\/strong><\/span><\/p>\n<p>{<span style=\"color: #0000ff;\"><strong>tidyverse<\/strong><\/span>} makes it incredibly easy to manipulate data and quickly iterate plots to our liking while {<span style=\"color: #0000ff;\"><strong>shiny<\/strong><\/span>} offers an easy way for us to turn our plots into an interactive webpage.<\/p>\n<p>Again, for the code and data see my <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\">GITHUB<\/a><\/span><\/strong> page (<strong><a href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/FV_Profile_Data_Viz.R\"><span style=\"color: #0000ff;\">R Script<\/span><\/a><\/strong> and <strong><a href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/FV_Profile_Data_Viz.csv\"><span style=\"color: #0000ff;\">Data<\/span><\/a><\/strong>).<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A colleague of mine was asking about how he could produce plots of force-velocity-power profiles for his coaches based on their Gymaware testing. Rather than plotting static plots for this stuff it is sometimes easier to build out a nice shiny app so that the coaches can interact with it or the practitioner can quickly [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45,42],"tags":[],"class_list":["post-1777","post","type-post","status-publish","format-standard","hentry","category-r-tips-tricks","category-sports-science"],"_links":{"self":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/1777","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/comments?post=1777"}],"version-history":[{"count":3,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/1777\/revisions"}],"predecessor-version":[{"id":1792,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/1777\/revisions\/1792"}],"wp:attachment":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/media?parent=1777"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/categories?post=1777"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/tags?post=1777"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}