Runs in Iowa City
Running and Biking in Chicago

If you use runkeeper and pay for a yearly subscription (runkeeper elite), you can export your data and plot all of your activities simultaneously using R. I’ve written a script for doing so (Special thanks to flowing data which has a tutorial that helped with a few key parts of this).

The script does a few unique things.


  1. Export your runkeeper data. The option is available for subscribers only under the settings menu.

Exporting Runkeeper Data

Exporting can be done from within the settings menu

  1. Place the script below within a folder containing your runkeeper data. Set the num_locations variable to the number of places you have lived/run. This will be used to pull out the number of distinct running locations automatically.

  2. Install the necessary R packages. You can run the following code within R to do so.
  1. Run the script below from within R Studio or on unix based machines using RScript plot_runkeeper.R. If you are using Rstudio, be sure to set the working directory using setwd()
# Special thanks for insights from regarding this.


num_locations <- 5

# Usage: Place this script in the directory containing your runkeeper data. You can run from terminal using 'Rscript map_runkeeper.R', or
# set your working directory to the location and run within RStudio (use setwd("~/location/of/runkeeper/data")).
# See below on how to set the number of clusters.

# GPX files downloaded from Runkeeper
files <- dir(pattern = "\\.gpx")

# Generate vectors for data frame
index <- c()
latitude <- c()
longitude <- c()
file <- c()

c <- 1 # Set up Counter

for (f in 1:length(files)) {
  curr_route <- readGPX(files[f])
# Treat interrupted GPS paths as seperate routes (useful if you occasionally stop running..walk for a bit, and start again like I do.)
for (i in curr_route$tracks[[1]]) {
  c <- c + 1
  location <- i
  file <- c(file,rep(files[f], dim(location)[1])) 
  index <- c(index, rep(c, dim(location)[1]))
  latitude <- c(latitude, location$lat)
  longitude <- c(longitude, location$lon)
routes <- data.frame(cbind(index, latitude, longitude,file))

# Because the routes dataframe takes a while to generate for some folks - save it!
save(routes, file="routes.Rdata")
# Use to load as needed.

# Fix data types
routes$file <- as.character(routes$file)
routes$latitude <- as.numeric(levels(routes$latitude)[routes$latitude])
routes$longitude <- as.numeric(levels(routes$longitude)[routes$longitude])
routes <- transform(routes, index = as.numeric(index))

# Load Meta Data
meta_data <- read.csv("cardioActivities.csv", stringsAsFactors=FALSE)
meta_data <- rename(meta_data, c("GPX.File" = "file"))

# Bind routes
routes <- left_join(routes, meta_data, by="file") %.%

# Use this function specify activity color if you have multiple activities.
activity_color <- function(activity) {
  if (activity=="Cycling") {
    color = "#00000060"
  } else if (activity=="Hiking") {
    color = "#00000060"
  } else {
    color = "#0080ff60"

# Identify clusters of points, which will correspond to locations you have run. For example,
# I have run in Boston, Iowa City, Chicago, and a few other cities. You will want to set the minimum krange
# to the number of cities you have run in (5 in my case).
clusters <- pamk(routes[,c("latitude", "longitude")], krange=num_locations:20, diss=T, usepam=F)$pamobject$medoids

# Plot Everything
for (r in 1:max(row(clusters))) {
  lat_range <- clusters[r,][1] + rnorm(20, sd=0.1)
  lon_range <-clusters[r,][2] + rnorm(20, sd=0.1)
  setroutes <- filter(routes, (latitude > min(lat_range) & latitude < max(lat_range)),
                      longitude > min(lon_range) &  longitude < max(lon_range))
  routeIds <- unique(setroutes$index)
  # Albers projection
  locProj <- mapproject(setroutes$longitude, setroutes$latitude, "rectangular", par=38)
  setroutes$latproj <- locProj$x
  setroutes$lonproj <- locProj$y
  # Map the projected points
  pdf(sprintf("%s-all.pdf", r))
  plot(setroutes$latproj, setroutes$lonproj, type="n", asp=1, axes=FALSE, xlab="", ylab="")
  for (i in routeIds) {
    currRoute <- subset(setroutes, index==i)
    lines(currRoute$latproj, currRoute$lonproj, col=activity_color(currRoute$Type), lwd=0.4)