Generating R plots from Strava data
Motivation
I’ve been running and riding for a while now, but have eased up lately and have no accountability. I wanted a quick and easy way to check my overall progress. I figured R would integrate with Strava’s API and allow me to plot my distance and pace, the two metrics I care about. Although I’ve had to take a rest for the past few months due to illness, the artefects below are informative.
Code is provided below to help you generate your own graphs in a similar style.
Interactive Chart
The code below can be turned into an interactive plot with the following short code snippet. I have noticed however that ggplotly
does not support all of features that might be found in a ggplot
; in particular, it was difficult to insert a second y-axis, and the dynamicTicks=TRUE
option is required to ensure that the x-axis isn’t converted from dates.
interactive_plot <- plotly::ggplotly(s, tooltip = c("text"), dynamicTicks=TRUE)
The second y-axis
Code
config.yaml
client_id: XXXX
secret: abc123
plot.R
library("yaml")
library("httr")
library("rjson")
library("gtools")
library("ggplot2")
library("anytime")
library("ggrepel")
library("dplyr", warn.conflicts = FALSE)
library("magrittr")
library("lubridate", warn.conflicts = FALSE)
credentials = read_yaml("~/Documents/Blog Research/2020/R-Strava/config.yaml")
app <- oauth_app("strava", credentials$client_id, credentials$secret)
endpoint <- oauth_endpoint(
request = NULL,
authorize = "https://www.strava.com/oauth/authorize",
access = "https://www.strava.com/oauth/token"
)
token <- oauth2.0_token(endpoint, app, as_header = FALSE,
scope = "activity:read_all")
activities <- GET("https://www.strava.com/", path = "api/v3/activities",
query = list(access_token = token$credentials$access_token, per_page = 200))
activities <- content(activities, "text")
activities <- fromJSON(activities)
activities <- lapply(activities, function(x) { # Apply an anonymous function on each list elements
x[sapply(x, is.null)] <- NA # Replace nulls by "missing" (N/A)
unlist(x)
})
df <- data.frame(t(sapply(activities[1],c)))
for (i in 2:length(activities)) {
de <- rbind(data.frame(t(sapply(activities[i],c))))
df <- smartbind(df, de)
}
non_zero <- df[df$distance != 0,]
non_zero$distance <- as.numeric(as.character(non_zero$distance))
non_zero$start_date_local <- anytime(as.factor(non_zero$start_date_local))
non_zero$elapsed_time <- as.numeric(as.character(non_zero$elapsed_time))
non_zero$pace <- (non_zero$elapsed_time/60)/(non_zero$distance/1000)
runs <- non_zero[non_zero$type == "Run",]
.labs = head(runs$name,40)
p <- ggplot(head(runs,40), aes(x=start_date_local, y=distance)) +
geom_point(aes(color=pace)) +
scale_color_gradientn(colors = c("#00AFBB", "#E7B800", "#FC4E07"), values = c(0, 0.8, 1), trans = "reverse") +
geom_text_repel(aes(label = .labs, color = pace), size = 3) +
labs(x = "Date", y = "Distance (m)", title = "Distance and Pace of Strava Runs") +
scale_x_datetime(date_breaks = "1 month", date_labels = "%b %Y") +
theme(axis.text.x=element_text(angle=60, hjust=1)) +
labs(color="Pace (mins/km)") +
theme(plot.title = element_text(hjust = 0.5))
run_distance_per_week <-
runs %>%
group_by(year_week = floor_date(start_date_local, "1 week")) %>%
summarize(count = n(),
distance = sum(distance)/1000,
time = sum(elapsed_time)/60,
pace = time/distance)
s <- ggplot() +
geom_col(run_distance_per_week, mapping = aes(x = year_week, y = distance, text = paste("<b>Week beginning:</b>", year_week, "<br><b>Distance:</b> ", format(round(distance, 2), nsmall = 2), "km")), fill = "#00AFBB") +
geom_point(run_distance_per_week, mapping = aes(x = year_week, y = pace, text = paste("<b>Week beginning:</b>", year_week, "<br><b>Pace:</b> ", format(round(pace, 2), nsmall = 2), "km/min")), size = 2, color = "#FF8855") +
scale_y_continuous(name = "Distance (km)",
sec.axis = sec_axis(~./1, name = "Pace (mins/km)")) +
labs(x = "Date", y = "Distance (m)", title = "Distance and Pace of Strava Runs per Week") +
scale_x_date(date_breaks = "1 month", date_labels = "%b %Y") +
theme(axis.text.x=element_text(angle=60, hjust=1)) +
labs(color="Pace (mins/km)") +
theme(plot.title = element_text(hjust = 0.5))