{"id":3093,"date":"2023-06-01T03:02:51","date_gmt":"2023-06-01T03:02:51","guid":{"rendered":"http:\/\/optimumsportsperformance.com\/blog\/?p=3093"},"modified":"2023-06-01T12:08:33","modified_gmt":"2023-06-01T12:08:33","slug":"r-tips-tricks-normalizing-test-dates-calculating-test-differences","status":"publish","type":"post","link":"https:\/\/optimumsportsperformance.com\/blog\/r-tips-tricks-normalizing-test-dates-calculating-test-differences\/","title":{"rendered":"R Tips &#038; Tricks: Normalizing test dates &#038; calculating test differences"},"content":{"rendered":"<p>A friend of mine was downloading some force plate data from the software provider so that he could evaluate test data in a few of his athletes during return to play. The issue he was running into was that the different athletes all had different numbers of tests and different start and end testing times. The software exports the test outputs by date and he was wondering how he could normalize the dates to numeric values (e.g. Test 1, Test 2, etc.) so that he could model the date (since we can&#8217;t really use a Date in a regression model).<\/p>\n<p>I&#8217;ll be the first to admit that working with dates and times can be an incredible pain in the butt. For reference, I covered the topic of converting Catapult GPS practice duration strings to actual training minutes, <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/catapult-gps-converting-the-practice-duration-string-to-minutes\/\">HERE<\/a><\/span><\/strong>. To help him out, I provided a few different solutions depending on the research question. I also add some code for calculating changes in test performance between tests and from each test to baseline.<\/p>\n<p>The full code is available on my <span style=\"color: #0000ff;\"><strong><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/Normalizing%20test%20dates%20%26%20calculating%20test%20differences.R\">GITHUB page<\/a><\/strong><\/span>.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Loading Packages &amp; Simulating Data<\/strong><\/span><\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n## load packages ----------------------------------------------\r\nlibrary(tidyverse)\r\nlibrary(lubridate)\r\n\r\n## Simulate data ----------------------------------------------\r\nset.seed(78)\r\ndat &lt;- tibble(\r\n  \r\n  athlete = rep(c(&quot;Tom&quot;, &quot;Bob&quot;, &quot;Franklin&quot;), times = c(5, 10, 3)),\r\n  test_dates = c(\r\n    seq(as.Date(&quot;2023-01-01&quot;), as.Date(&quot;2023-01-5&quot;), by = &quot;days&quot;),\r\n    seq(as.Date(&quot;2023-02-15&quot;), as.Date(&quot;2023-02-24&quot;), by = &quot;days&quot;),\r\n    as.Date(c(&quot;2023-01-19&quot;, &quot;2023-01-30&quot;, &quot;2023-02-26&quot;))\r\n  ),\r\n  jump_height = round(rnorm(n = 18, mean = 28, sd = 2.5), 1)\r\n  \r\n)\r\n\r\ndat\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.33.38-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-3094\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.33.38-PM.png\" alt=\"\" width=\"313\" height=\"463\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.33.38-PM.png 518w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.33.38-PM-203x300.png 203w\" sizes=\"auto, (max-width: 313px) 100vw, 313px\" \/><\/a><br \/>\nWe can see that Tom has 5 tests, Bob has 10, and Franklin has only 3. Additionally, Tom and Bob tested every day, consecutively, while Franklin was less compliant and has larger time frames between his tests.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Create a test number<\/strong><\/span><\/p>\n<p>First, let&#8217;s normalize the Dates so that they are numeric. Basically, instead of dates we want a value indicating whether the test was test 1, or test 5, or test N. We will do this by creating a <strong>row_number() <\/strong>id\/counter for each individual athlete.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n### Create a test number ------------------------------------------\r\ndat &lt;- dat %&gt;%\r\n  group_by(athlete) %&gt;%\r\n  mutate(test_day = row_number())\r\n\r\ndat\r\n<\/pre>\n<pre><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.36.12-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-3095\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.36.12-PM.png\" alt=\"\" width=\"369\" height=\"428\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.36.12-PM.png 640w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.36.12-PM-259x300.png 259w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.36.12-PM-624x723.png 624w\" sizes=\"auto, (max-width: 369px) 100vw, 369px\" \/><\/a><\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>Calculating the time between tests<\/strong><\/span><\/p>\n<p>Alternatively, we may not just want to know the test number of each test but we may want to determine the amount of days between each test.<\/p>\n<p>The code to do this is a bit ugly looking so let&#8217;s unpack it.<\/p>\n<ol>\n<li>Since we are dealing with dates we use the <strong>difftime()<\/strong> function which takes an argument for the two times you are looking to calculate the difference between. Here, we are trying to calculate the difference in time (days) between one date and the date preceding it for each individual athlete.<\/li>\n<li>The <strong>difftime() <\/strong>function will produce a to time variable. If we want to make this numeric we need to convert it to a character so we do that with the <strong>as.character()<\/strong> function.<\/li>\n<li>Once the variable is a character we use the <strong>as.numeric() <\/strong>function to convert it to a numeric value.<\/li>\n<li>Finally, since the first value for each athlete will be an NA, since there is no date preceding the first test, we use the <strong>coalesce() <\/strong>function to fill in a 0 value for each of the NA&#8217;s, to indicate that this was the first test and thus there was no time between it and any other test.<\/li>\n<\/ol>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n### Calculate the time between tests -------------------------------\r\ndat &lt;- dat %&gt;%\r\n  group_by(athlete) %&gt;%\r\n  mutate(time_btw_tests = coalesce(as.numeric(as.character(difftime(test_dates, lag(test_dates)))), 0))\r\n\r\ndat\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-3096\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM.png\" alt=\"\" width=\"462\" height=\"391\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM.png 894w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM-300x254.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM-768x649.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.39.27-PM-624x528.png 624w\" sizes=\"auto, (max-width: 462px) 100vw, 462px\" \/><\/a><\/p>\n<p>Notice that Tom and Bob have 1 day between all of their tests while Franklin&#8217;s second test was 11 days after his first and his third test was 27 days after his second.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Calculate the difference in jump height from one test to the next<\/strong><\/span><\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n### Calculate difference in jump height from one day to the next -------------------\r\ndat &lt;- dat %&gt;%\r\n  group_by(athlete) %&gt;%\r\n  mutate(test_to_test_diff = jump_height - lag(jump_height))\r\n\r\ndat\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-3097\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM-1024x696.png\" alt=\"\" width=\"625\" height=\"425\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM-1024x696.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM-300x204.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM-768x522.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM-624x424.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.45.39-PM.png 1118w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>Here, we use the <strong>lag()<\/strong> function to calculate the difference in one value from the value before it within in the same column. Since we grouped by athlete, which is what we want, their first test will always have an NA, in this new column, since there was no test preceding it.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Calculating the difference in jump height from the baseline test<\/strong><\/span><\/p>\n<p>Finally, we might also be interested to evaluate the performance on each test relative to the athlete&#8217;s baseline test. To do this we simply subtract <strong>jump_height<\/strong> from the <strong>jump_height<\/strong> indexed in row one for each athlete.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n### Calculate difference in jump height from each test to the baseline test -------------\r\n\r\ndat &lt;- dat %&gt;%\r\n  group_by(athlete) %&gt;%\r\n  mutate(test_to_baseline_diff = jump_height - jump_height&#x5B;1])\r\n\r\ndat\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-3098\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM-1024x544.png\" alt=\"\" width=\"625\" height=\"332\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM-1024x544.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM-300x159.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM-768x408.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM-624x332.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/06\/Screenshot-2023-05-31-at-7.49.35-PM.png 1422w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Wrapping Up<\/strong><\/span><\/p>\n<p>Dates and times are always tricky to deal with. Most of the sports technology providers will proved data as dates (or unix timestamps) meaning that we have to do some cleaning of the data to codify the dates as numeric values representing the test number or the days between tests (depending on the research question). Additionally, using lag functions can be helpful for calculating he difference from one test to the next or from each test to the baseline.<\/p>\n<p>The entire code is available on my <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/Normalizing%20test%20dates%20%26%20calculating%20test%20differences.R\">GITHUB page<\/a><\/span><\/strong>.<\/p>\n<p>If you have any data cleaning issues that you are dealing with from various sports science technologies, feel free to reach out!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A friend of mine was downloading some force plate data from the software provider so that he could evaluate test data in a few of his athletes during return to play. The issue he was running into was that the different athletes all had different numbers of tests and different start and end testing times. [&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,43,42,27],"tags":[],"class_list":["post-3093","post","type-post","status-publish","format-standard","hentry","category-r-tips-tricks","category-sports-analytics","category-sports-science","category-strength-and-conditioning"],"_links":{"self":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/3093","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=3093"}],"version-history":[{"count":1,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/3093\/revisions"}],"predecessor-version":[{"id":3099,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/3093\/revisions\/3099"}],"wp:attachment":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/media?parent=3093"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/categories?post=3093"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/tags?post=3093"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}