With an upcoming half-marathon, I went looking online for a good race time and race pace calculator (of which there are some good simple ones and some more interesting ones). But let’s face it. What I really wanted was an R function but I couldn’t find one out there. So I made a couple functions that maybe I’ll package up and post to CRAN someday. In the meantime, here they are.

What you do with these splits is, of course, up to you. I suppose the nouveaux thing is to program the virtual partner on your Garmin to match your desired pace. That way, you can play a real-life video game and run at the same time. Win-win. Alternatively, you can use any number of old-school approaches to carry all your splits with you on your run (then ~~argue~~ discuss online about your favourite split-carrying method *ad nauseam*). But I digress.

**Technical note:** I decided to build the functions on seconds as a time unit. I find working with date and time objects and such in R really tricky (and somewhat overcomplicated for this application). So the math is done on seconds and the times/paces are reported as character vectors in the format “mm:ss”. This can be annoying if you want to do something with the numbers after the fact but I leave it to you to convert the character to date/time format.

There are two main functions here:

- The
function takes a race distance (in mi or km) and a pace (in min/mi or min/km, consistent with the distance input) and calculates a total time.*calcTime* - The
function takes a race distance (in mi or km, which you specify) and a race time in mm:ss and calculates pace in both min/mi and min/km. It also outputs tables of race time per mi or per km and for the finish distance.*calcPace*

Because of the formatting weirdness, both main functions (*calcTime* and *calcPace*) require the use of a third function CharMinSec, which converts seconds (numeric) to “mm:ss” (character). This function is embedded in the other two functions, just FYI.

*calcTime* to calculate a total race time from a /mi or /km pace:

# Calculate total race time from pace calcTime <- function(racePace, raceDistance){ # racePace is the per unit pace in mm:ss - character # raceDistance is the total race distance - numeric # Pace and race distance must use same units (km or mi or whatever) # Seconds to character time function CharMinSec <- function(sec){ outMin <- floor(sec/60) outSec <- ((sec/60)-outMin)*60 if(outSec==0 | round(outSec)<10){ outChar <- paste0(outMin,":0",round(outSec)) } else { outChar <- paste(outMin,round(outSec),sep=":") } outChar } paceMinSec <- as.numeric(strsplit(racePace,':')[[1]]) paceSec <- paceMinSec[1]*60+ paceMinSec[2] raceMin <- floor(paceSec*raceDistance/60) raceSec <- ((paceSec*raceDistance/60)-raceMin)*60 raceTime <- CharMinSec(raceMin*60+raceSec) list(TotalTime=raceTime, Minutes=raceMin, Seconds=raceSec) } # Example of 4:15/km for a half-marathon # (Since we're being a bit nerdy anyway, let's get nerdy about the distance too.) calcTime("4:15",21.097494) $TotalTime [1] "89:40" $Minutes [1] 89 $Seconds [1] 39.86097 # Example of 6:45/mi for a 5 miler calcTime("7:45", 5) $TotalTime [1] "38:45" $Minutes [1] 38 $Seconds [1] 45

*calcPace* to calculate a /mi or /km pace from a race time and distance and to create race time tables by mi or km:

# Calculate pace from total race time calcPace <- function(raceDistance, raceTime, raceUnits){ # raceDistance is the total race distance (in km or mi) - numeric # raceTime is the total goal time of the race (in MM:SS) - character # raceUnits is the distance units of raceDistance (either "km" or "mi") # Seconds to character time function CharMinSec <- function(sec){ outMin <- floor(sec/60) outSec <- ((sec/60)-outMin)*60 if(outSec==0 | round(outSec)<10){ outChar <- paste0(outMin,":0",round(outSec)) } else { outChar <- paste(outMin,round(outSec),sep=":") } outChar } minsec <- as.numeric(strsplit(raceTime,':')[[1]]) sec <- minsec[1]*60+ minsec[2] # race distances if(raceUnits=="mi"){ kmDist <- round(raceDistance*1.609344,2) miDist <- round(raceDistance,2) } else { kmDist <- round(raceDistance,2) miDist <- round(raceDistance*0.62137119,2) } # km pace secPerKM <- sec/kmDist paceKM <- CharMinSec(secPerKM) # mi pace secPerMI <- sec/miDist paceMI <- CharMinSec(secPerMI) # km splits splitKM <- data.frame(km=c(1:floor(kmDist),kmDist)) splitKM$min <- floor(secPerKM/60*splitKM$km) splitKM$sec <- round(secPerKM*splitKM$km - splitKM$min*60) splitKM$time <- lapply(secPerKM*splitKM$km, CharMinSec) # mi splits splitMI <- data.frame(mile=c(1:floor(miDist),miDist)) splitMI$min <- floor(secPerMI/60*splitMI$mile) splitMI$sec <- round(secPerMI*splitMI$mile - splitMI$min*60) splitMI$time <- lapply(secPerMI*splitMI$mile, CharMinSec) # output list(km_pace=paceKM, km_splits=splitKM, mi_pace=paceMI, mi_splits=splitMI) } # Example for a 90min half-marathon calcPace(21.097494, "90:00", "km") $km_pace [1] "4:16" $km_splits km min sec time 1 1.0 4 16 4:16 2 2.0 8 32 8:32 3 3.0 12 48 12:48 4 4.0 17 4 17:04 5 5.0 21 20 21:20 6 6.0 25 36 25:36 7 7.0 29 51 29:51 8 8.0 34 7 34:07 9 9.0 38 23 38:23 10 10.0 42 39 42:39 11 11.0 46 55 46:55 12 12.0 51 11 51:11 13 13.0 55 27 55:27 14 14.0 59 43 59:43 15 15.0 63 59 63:59 16 16.0 68 15 68:15 17 17.0 72 31 72:31 18 18.0 76 47 76:47 19 19.0 81 3 81:03 20 20.0 85 18 85:18 21 21.0 89 34 89:34 22 21.1 89 60 90:00 $mi_pace [1] "6:52" $mi_splits mile min sec time 1 1.00 6 52 6:52 2 2.00 13 44 13:44 3 3.00 20 36 20:36 4 4.00 27 28 27:28 5 5.00 34 19 34:19 6 6.00 41 11 41:11 7 7.00 48 3 48:03 8 8.00 54 55 54:55 9 9.00 61 47 61:47 10 10.00 68 39 68:39 11 11.00 75 31 75:31 12 12.00 82 23 82:23 13 13.00 89 15 89:15 14 13.11 90 0 90:00 # Example for a 40min 5-miler calcPace(5, "38:00", "mi") $km_pace [1] "4:43" $km_splits km min sec time 1 1.00 4 43 4:43 2 2.00 9 26 9:26 3 3.00 14 10 14:10 4 4.00 18 53 18:53 5 5.00 23 36 23:36 6 6.00 28 19 28:19 7 7.00 33 3 33:03 8 8.00 37 46 37:46 9 8.05 37 60 38:00 $mi_pace [1] "7:36" $mi_splits mile min sec time 1 1 7 36 7:36 2 2 15 12 15:12 3 3 22 48 22:48 4 4 30 24 30:24 5 5 38 0 38:00 6 5 38 0 38:00

It might be fun to include some more complicated calculations in a complete R package, like the run-walk calculator, or the age-grade estimator (I hesitate to call this a “calculator”). If I find myself unemployed or on a long flight, I’m on it.

Pingback: Cycling gear ratio calculator for R (or why you should just go buy a SRAM Eagle 1×12 group right now) | David R. Roberts