knitr::opts_chunk$set(fig.width = 7, fig.height = 5) options(digits = 2)
cycleRtools
is a package intended to open up the power of R to cyclists and those interested in cycling data. Functions are provided for reading raw data files into the R environment--a signficant barrier in any case--as well as several specialist functions that are provided by other, often proprietary platforms.
library(cycleRtools)
For these examples we'll be using a preloaded dataset, named intervaldata
(i.e data(intervaldata)
). The corresponding Garmin .fit file is provided in the extdata
directory of this package, tarballed together with other files of various formats. See ?read_ride
and the example therein.
Briefly, if intervaldata.fit
is the file name, then this dataset was generated via:
intervaldata <- read_ride("intervaldata.fit", format = TRUE, CP = 310, # Critical power. sRPE = 7) # Session RPE.
Note that read_ride()
is a generic wrapper for all read_*
functions in this package (read_fit()
, read_srm()
etc...) and calls the appropriate function according to file extension.
When these (numeric) arguments are supplied, they are associated with the parsed data and subsequently used by other functions in this package; they can also serve as useful records should the object be saved as part of ongoing training monitoring. They correspond to "Critical Power" and "session RPE", respectively. For more information on these metrics see Jones et al., 2010 and Foster et al., 2001.
Calling any read_*
function with the argument format = TRUE
will generate a data.frame-type object of class cycleRdata
. The creation of this class was deemed necessary so that certain columns could be assumed to exist in the data; thus ultimately making life more straightforward for the user. See ?cycleRdata
for details of the format.
It goes without saying that once these cycling data are within the R environment, endless analytical procedures become available. For the purposes of this demonstration, only some of those procedures facilitated by this package are shown.
As is customary, data should first be plotted. cycleRdata
objects have an associated plot
method:
plot(x = intervaldata, # "x" is the data, for consistency with other methods. y = 1:3, # Which plots should be created? see below. xvar = "timer.min", # What should be plotted on the x axis? xlab = "Time (min)", # x axis label. laps = TRUE, # Should different laps be coloured? breaks = TRUE) # Should stoppages in the ride be shown?
The y
argument for this plot
method specifies the way in which the plot stack should be generated. Specifically length(y)
determines the number of plots to be stacked, and the combination of c(1, 2, 3)
controls what is plotted and where. As is described in ?plot.cycleRdata
, numbers in this argument give:
So for example:
plot(intervaldata, y = c(3:1)) # Inverts the above plot. plot(intervaldata, y = 2) # Just plots power. plot(intervaldata, y = c(1, 3)) # W' balance over elevation.
Plots can also be zoomed, and the title metric will be adjusted accordingly.
## Zoom to 0-50 minutes. plot(intervaldata, y = 3, xvar = "timer.min", xlim = c(0, 50))
Another want of cyclists is the ability to analyse "time in zones". This is provided primarily by the functions zone_time()
and zdist_plot()
.
zone_time(data = intervaldata, column = power.W, # What are we interested in? zbounds = c(100, 200, 300), # Zone boundaries. pct = FALSE) / 60 # Output in minutes. ## How about time above and below CP, as a percentage? ## NB: column = power.W is the default. zone_time(intervaldata, zbounds = 310, pct = TRUE)
zdist_plot(data = intervaldata, binwidth = 10, # 10 Watt bins. zbounds = c(100, 200, 300), # Zone boundaries. xlim = c(50, 400)) # Zoom to 50-400 Watts.
cycleRdata
objects have a summary
method, which calculates common metrics and will produce a lap and/or interval summary as appropriate.
summary(intervaldata)
Also see ?summary_metrics
for other common metrics.
Best mean powers for specified durations can be generated via the mmv()
function. This will return both the best recorded values, as well as when those were recorded in the ride (seconds).
times_sec <- 2:20 * 60 # 2-20 minutes. prof <- mmv(data = intervaldata, column = power.W, # Could also use speed.kmh. windows = times_sec) print(prof)
This returns the usual, hyperbolic power-time profile.
hypm <- lm(prof[1, ] ~ {1 / times_sec}) # Hyperbolic model. ## Critical Power (Watts) and W' (Joules) estimates hypm <- setNames(coef(hypm), c("CP", "W'")) print(hypm) ## Plot with the inverse model overlaid. plot(times_sec, prof[1, ], ylim = c(hypm["CP"], max(prof[1, ])), xlab = "Time (sec)", ylab = "Power (Watts)") curve((hypm["W'"] / x) + hypm["CP"], add = TRUE, col = "red") abline(h = hypm["CP"], lty = 2) legend("topright", legend = c("Model", "CP"), bty = "n", lty = c(1, 2), col = c("red", "black"))
More sophisticated modelling is provided by the Pt_model
function; see ?Pt_model
. Briefly:
ms <- Pt_model(prof[1, ], times_sec) print(ms) plot(times_sec, prof[1, ], ylim = c(hypm["CP"], max(prof[1, ])), xlab = "Time (sec)", ylab = "Power (Watts)") ## Showing an exponential model, as it best fits these data. curve(ms$Pfn$exp(x), add = TRUE, col = "red")
Viewing a route map is simple thanks to the leaflet
package.
library(leaflet) leaflet(intervaldata) %>% addTiles() %>% addPolylines(~lon, ~lat)
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.