'Running through the fields, lying flat on the ground': Animating 2020 running goals

'Running through the fields, lying flat on the ground': Animating 2020 running goals

Full disclosure: I did not even have any running goals for 2020. But as it turned out: It has been a good year for running - at least one thing this year was good at. In this post, I want to run you through my R script which creates an animated graph that looks like I had the goal of running 500 kilometres this year (spoiler: I didn’t). This is what the finished graph looks like (for 2020-12-23):

Getting the data

You’ll a need a Strava API account if you want to replicate this closely. However, as you’ll see later in this post, you could plug in any dataframe with at least two columns: date and kilometres run at this date. As long as you have this, you won’t need the Strava API. If you want to use the Strava API, you’ll need to get the {rStrava} package first.

First, I’ll load some packages.

suppressPackageStartupMessages(library(rStrava))
suppressPackageStartupMessages(library(lubridate))
suppressPackageStartupMessages(library(gganimate))
suppressPackageStartupMessages(library(scales))

Now, we’ll get the data from the Strava API. During the authentication process, a browser window will open asking for permission to access your data.

app_name <- '<your app name>'
app_client_id  <- '<your app client ID>'
app_secret <- '<your app secret>'

# create the authentication token
stoken <- httr::config(token = strava_oauth(app_name, app_client_id, app_secret, app_scope="activity:read_all"))

act.list <- get_activity_list(stoken)
act.df <- compile_activities(act.list)
act.df <- act.df[,c("start_date_local", "distance", "type")]

I’ve only selected the relevant variables in the data. These are start_date_local, the timestamp when the activity has been started, distance, the recorded distance in kilometres (because that is how distances are being measured!), and type, which could be “Run”, “Ride”, “Hike”, “Walk” and so on. We need this to subset the activities for runs only. Note that this is not necessary - you can, of course, check for overall goals of all activites.

So, how does this dataset look like (in my case at least)?

scroll_box(kable_styling(kable(act.df)),
           width = "100%", height = "400px")
start_date_local distance type
2020-12-20T12:08:47Z 6.5112 Run
2020-12-19T15:26:01Z 3.0185 Hike
2020-12-05T17:29:16Z 6.7965 Run
2020-11-29T14:38:19Z 5.3272 Hike
2020-11-18T17:56:27Z 6.8013 Run
2020-11-11T16:35:54Z 15.1320 Run
2020-11-07T16:26:43Z 8.0320 Run
2020-11-04T17:32:00Z 6.8545 Run
2020-11-01T16:25:37Z 10.6356 Run
2020-10-29T16:57:55Z 7.0139 Run
2020-10-24T16:38:31Z 10.0092 Run
2020-10-21T16:45:56Z 15.0126 Run
2020-10-04T16:53:34Z 11.1620 Ride
2020-10-04T13:44:24Z 10.6620 Ride
2020-10-03T11:49:40Z 8.0098 Run
2020-09-30T16:55:39Z 15.2126 Run
2020-09-21T17:25:23Z 7.0135 Run
2020-09-20T15:45:28Z 3.2196 Ride
2020-09-20T11:48:45Z 4.4308 Ride
2020-09-19T11:02:46Z 3.0097 Run
2020-09-19T10:12:46Z 10.0177 Run
2020-09-18T16:45:47Z 4.4839 Ride
2020-09-15T16:57:02Z 3.8043 Run
2020-09-14T19:11:30Z 7.0129 Run
2020-09-11T23:08:36Z 7.8499 Ride
2020-09-11T18:05:34Z 12.1572 Ride
2020-09-07T14:46:11Z 8.7628 Run
2020-09-03T10:45:59Z 15.1499 Run
2020-09-01T10:40:14Z 10.5043 Run
2020-08-30T08:30:51Z 8.0094 Run
2020-08-28T10:53:47Z 18.0088 Run
2020-08-26T17:36:06Z 10.0204 Run
2020-08-25T18:05:07Z 5.5073 Run
2020-08-19T19:21:06Z 7.0085 Run
2020-08-17T17:49:22Z 6.0161 Run
2020-08-15T12:17:50Z 5.0268 Ride
2020-08-15T11:01:39Z 4.7177 Ride
2020-08-12T07:10:54Z 3.5644 Ride
2020-08-10T19:37:56Z 23.1406 Ride
2020-08-08T21:06:28Z 5.0087 Run
2020-08-08T07:03:06Z 6.2347 Ride
2020-08-06T20:26:21Z 9.8608 Ride
2020-08-06T19:50:48Z 8.7674 Ride
2020-08-02T16:02:24Z 12.0106 Run
2020-08-01T17:13:45Z 4.5471 Ride
2020-08-01T16:32:47Z 6.1145 Ride
2020-07-30T18:14:18Z 12.3930 Ride
2020-07-29T18:56:58Z 6.3769 Run
2020-07-27T18:28:04Z 6.6501 Ride
2020-07-19T16:41:58Z 10.0077 Run
2020-07-08T17:16:01Z 13.0075 Run
2020-07-04T14:06:26Z 10.0119 Run
2020-06-29T16:56:46Z 7.0083 Run
2020-06-24T18:50:28Z 10.7589 Ride
2020-06-24T17:43:41Z 10.8192 Ride
2020-06-15T12:08:29Z 12.0062 Run
2020-06-03T17:48:56Z 12.0115 Run
2020-05-29T17:27:54Z 7.5011 Run
2020-05-29T16:39:54Z 5.3362 Run
2020-05-25T17:54:32Z 7.0091 Run
2020-05-16T14:58:07Z 15.0033 Run
2020-05-12T17:01:36Z 22.4918 Ride
2020-05-08T18:24:39Z 7.0076 Run
2020-05-01T14:38:56Z 17.0104 Run
2020-04-27T15:56:56Z 20.0196 Ride
2020-04-20T08:45:08Z 7.3816 Run
2020-04-17T18:16:31Z 5.0091 Run
2020-04-13T14:56:20Z 16.6037 Ride
2020-04-12T10:52:33Z 12.0081 Run
2020-04-05T17:05:34Z 11.0084 Run
2020-03-30T17:02:05Z 8.0094 Run
2020-03-25T16:49:58Z 8.0058 Run
2020-03-25T14:09:05Z 2.4352 Walk
2020-03-18T17:33:43Z 7.0131 Run
2020-03-08T12:14:09Z 7.0051 Run
2020-03-05T07:06:23Z 4.0774 Ride
2020-03-03T18:35:44Z 4.0269 Ride
2020-02-26T14:28:56Z 2.0783 Walk
2020-02-23T16:33:18Z 7.0074 Run
2020-02-19T17:10:34Z 5.0079 Run
2020-01-09T17:15:34Z 5.0218 Run
2020-01-02T12:24:07Z 2.9290 Walk
2019-12-30T13:36:10Z 8.0415 Run
2019-10-09T11:08:53Z 3.0416 Walk
2019-10-06T12:57:10Z 8.1990 Walk
2019-09-16T17:54:06Z 10.0065 Run
2019-09-09T16:25:04Z 7.0040 Run
2019-09-04T14:16:32Z 13.1491 Walk
2019-09-03T17:59:33Z 12.0108 Run
2019-08-28T16:59:35Z 6.0090 Run
2019-08-25T11:01:20Z 4.3331 Walk
2019-08-24T17:42:03Z 12.8546 Ride
2019-08-22T07:10:27Z 4.0557 Ride
2019-08-21T17:31:36Z 8.6797 Run
2019-08-20T21:35:52Z 4.0338 Ride
2019-08-20T07:05:00Z 3.9825 Ride
2019-08-19T15:44:51Z 11.0532 Ride
2019-08-19T13:51:35Z 7.0037 Ride
2019-08-12T17:36:21Z 10.0097 Run
2019-08-03T19:33:23Z 7.0159 Run
2019-07-22T14:43:49Z 12.8757 Walk
2019-07-19T18:35:06Z 5.8047 Run
2019-07-15T16:47:24Z 8.3031 Run
2019-07-08T13:19:08Z 50.2936 Ride
2019-07-01T18:34:53Z 10.0076 Run
2019-06-25T10:06:52Z 4.0129 Ride
2019-06-16T17:52:20Z 9.7427 Ride
2019-06-16T17:24:31Z 2.9733 Ride
2019-06-16T16:32:10Z 9.3708 Ride
2019-06-12T16:29:15Z 7.0044 Run
2019-06-11T19:06:11Z 3.9996 Ride
2019-06-11T07:14:32Z 3.9769 Ride
2019-06-09T15:41:54Z 23.3266 Ride
2019-06-09T10:44:28Z 27.4610 Ride
2019-06-05T18:46:55Z 6.6384 Run
2019-05-16T18:47:55Z 4.0429 Ride
2019-05-16T07:07:27Z 4.3472 Ride
2019-05-15T17:09:44Z 4.0550 Run
2019-04-28T16:32:24Z 12.7741 Ride
2019-04-28T16:19:11Z 4.4228 Ride
2019-04-25T16:29:44Z 13.9924 Ride
2019-04-19T11:08:10Z 13.9537 Ride
2019-04-03T16:59:00Z 7.5035 Run
2019-03-20T16:36:09Z 8.5210 Run
2019-03-09T13:03:45Z 8.0170 Run
2019-02-05T17:37:07Z 9.0142 Run
2019-01-27T15:41:33Z 25.7647 Ride
2019-01-23T16:56:29Z 10.0208 Run
2019-01-15T16:30:35Z 6.0205 Run
2019-01-09T17:04:36Z 7.0131 Run
2018-12-27T16:02:44Z 6.0202 Run
2018-12-12T10:11:07Z 6.0712 Run
2018-11-29T18:14:02Z 9.0179 Run
2018-11-14T16:54:27Z 6.0138 Run
2018-11-12T16:50:37Z 8.4765 Run
2018-11-04T17:03:13Z 10.0233 Run
2018-11-01T10:56:14Z 6.8576 Walk
2018-10-17T17:01:11Z 8.5058 Run
2018-09-28T19:12:14Z 7.4662 Run
2018-09-22T15:48:56Z 40.0037 Ride
2018-09-15T17:06:34Z 10.2327 Run
2018-09-09T11:11:11Z 43.9069 Ride
2018-09-07T16:43:32Z 6.0166 Run
2018-08-30T17:28:10Z 10.0101 Run
2018-08-21T18:08:26Z 7.0076 Run
2018-08-19T11:19:53Z 5.3950 Run
2018-08-15T19:13:13Z 6.0196 Run
2018-08-12T13:06:36Z 4.1308 Run
2018-08-04T16:46:21Z 12.9166 Ride
2018-08-04T16:28:52Z 2.0064 Run
2018-08-04T16:01:35Z 8.0818 Ride
2018-07-30T18:57:50Z 4.9528 Run
2018-07-02T18:01:34Z 18.7269 Ride
2018-07-02T16:17:25Z 19.4094 Ride
2018-06-27T18:40:24Z 18.8614 Ride
2018-06-24T12:19:04Z 7.3337 Hike
2018-06-21T18:01:52Z 33.8825 Ride
2018-06-13T17:15:35Z 22.7718 Ride
2018-06-10T11:26:29Z 25.0428 Ride
2018-06-08T18:59:02Z 4.4132 Run
2018-06-06T17:32:58Z 26.9046 Ride
2018-05-31T17:13:24Z 5.4909 Walk
2018-05-26T10:56:39Z 9.4007 Hike
2018-05-25T11:10:06Z 9.0973 Hike
2018-05-23T18:31:34Z 6.1423 Run
2018-05-20T17:07:12Z 4.4071 Run
2018-05-13T11:06:07Z 13.3962 Run
2018-05-05T16:00:49Z 8.2364 Run
2018-05-02T19:37:50Z 6.0234 Run
2018-04-30T18:34:25Z 10.0213 Run
2018-04-28T15:26:05Z 11.0106 Run
2018-04-25T17:41:20Z 10.0221 Run
2018-04-21T09:40:56Z 11.0149 Run
2018-04-18T18:14:05Z 9.6041 Run
2018-04-15T13:14:25Z 10.4718 Run
2018-04-11T16:35:31Z 12.5152 Run
2018-04-06T17:16:28Z 2.9243 Ride
2018-04-06T07:58:54Z 2.8334 Ride
2018-04-05T18:15:29Z 2.7772 Ride
2018-03-30T16:24:09Z 6.0143 Run
2018-03-24T15:03:34Z 12.5026 Run
2018-03-21T16:45:29Z 7.0528 Run
2018-03-17T14:48:40Z 6.0271 Run
2018-03-12T14:52:38Z 10.0220 Run
2018-03-07T17:14:50Z 9.0163 Run
2018-03-05T12:22:27Z 14.3510 Ride
2018-03-05T11:31:52Z 14.1463 Ride
2018-03-04T16:11:28Z 7.5139 Run
2018-02-21T16:16:23Z 7.0248 Run
2018-02-18T11:34:17Z 6.0247 Run
2018-02-14T16:12:19Z 9.4757 Run
2018-02-10T14:06:47Z 15.2457 Run
2018-02-07T16:57:01Z 8.0145 Run
2018-02-03T12:11:37Z 6.0102 Run
2018-01-27T15:03:34Z 8.1768 Run
2018-01-24T17:29:13Z 10.0201 Run
2018-01-17T16:53:42Z 11.7243 Run
2018-01-13T17:11:13Z 6.0069 Run
2018-01-10T17:45:24Z 6.4334 Run
2018-01-10T16:45:35Z 2.5158 Run
2018-01-07T15:41:28Z 7.0257 Run
2017-12-30T15:53:22Z 10.0051 Run
2017-12-09T20:00:31Z 7.2105 Run
2017-12-01T19:16:07Z 5.9960 Run
2017-11-22T17:17:18Z 10.5203 Run
2017-11-19T10:40:47Z 7.3402 Run
2017-11-08T18:35:03Z 6.0048 Run
2017-10-31T14:38:14Z 5.8287 Run
2017-10-21T12:24:35Z 7.6540 Run
2017-10-14T16:45:59Z 7.0890 Run
2017-09-30T12:40:30Z 53.3179 Ride
2017-09-27T17:21:34Z 7.0583 Run
2017-09-24T18:51:45Z 6.0237 Run
2017-09-18T18:08:51Z 6.0117 Run
2017-09-08T20:02:38Z 6.0247 Run
2017-09-03T15:08:17Z 12.2499 Ride
2017-09-03T11:21:23Z 25.1322 Ride
2017-09-01T16:10:33Z 6.0132 Run
2017-08-30T19:08:27Z 5.8463 Run
2017-08-23T18:57:54Z 10.0127 Run
2017-08-20T14:13:44Z 3.8379 Run
2017-07-31T18:44:39Z 6.0184 Run
2017-07-26T19:05:28Z 6.1249 Run
2017-07-22T20:51:55Z 2.3603 Ride
2017-07-22T15:38:43Z 2.5587 Ride
2017-07-20T18:39:13Z 5.3206 Run
2017-07-17T19:54:41Z 6.8633 Run
2017-07-15T15:22:18Z 6.0219 Run
2017-07-12T19:30:12Z 2.7561 Ride
2017-07-12T18:49:27Z 6.9518 Run
2017-07-10T18:29:18Z 4.9003 Run
2017-07-04T18:52:01Z 6.0223 Run
2017-07-02T12:18:22Z 11.1081 Run
2017-06-28T18:33:46Z 6.0333 Run
2017-06-23T17:46:22Z 2.8099 Ride
2017-06-15T16:57:34Z 4.7602 Run
2017-06-07T16:35:44Z 12.0148 Run
2017-05-28T14:54:32Z 4.3528 Run
2017-05-25T14:17:26Z 5.0575 Run
2017-05-02T17:16:42Z 5.2449 Run
2017-04-30T16:49:17Z 7.8565 Run
2017-04-17T17:51:27Z 6.5049 Run
2017-04-08T17:43:40Z 5.2094 Run
2017-03-01T16:47:10Z 8.4576 Run
2017-02-06T10:08:12Z 13.3531 Run
2017-01-27T16:45:23Z 6.6739 Run
2017-01-04T16:26:57Z 4.4256 Run
2017-01-02T17:15:53Z 5.9622 Run
2016-11-27T08:05:45Z 4.7086 Run
2016-11-24T17:09:36Z 4.7156 Run
2016-11-12T15:09:24Z 11.0977 Run
2016-10-02T16:40:00Z 12.3215 Run
2016-09-28T18:50:51Z 6.5303 Run
2016-09-25T14:13:09Z 29.1221 Ride
2016-09-21T17:28:59Z 6.2100 Run
2016-09-14T19:00:53Z 7.0752 Run
2016-09-07T18:28:43Z 6.7215 Run
2016-09-04T17:45:52Z 6.4221 Run
2016-09-01T17:15:03Z 5.8054 Run
2016-08-31T17:35:26Z 3.8420 Run
2016-08-13T17:39:22Z 7.1644 Run
2016-08-13T16:58:54Z 5.7650 Run
2016-08-09T19:13:23Z 5.1868 Run
2016-08-01T07:23:57Z 4.9957 Run
2016-07-18T18:53:29Z 7.6737 Run
2016-07-10T16:21:02Z 4.1099 Run
2016-06-26T11:41:41Z 13.2323 Run
2016-06-17T19:12:25Z 8.6722 Run
2016-05-31T07:46:37Z 6.0532 Run
2016-05-25T17:34:50Z 7.0192 Run
2016-05-17T18:58:14Z 8.5503 Run
2016-04-22T16:55:37Z 7.9123 Run
2016-04-20T17:07:36Z 7.2182 Run
2016-04-03T15:05:13Z 6.1108 Run
2016-02-28T15:56:14Z 6.0268 Ride
2016-02-21T15:48:26Z 6.0321 Run
2016-01-25T17:55:41Z 4.6693 Run
2016-01-16T15:57:06Z 5.6074 Run
2016-01-07T13:21:43Z 8.7511 Run
2015-12-28T17:55:28Z 4.9738 Run
2015-12-23T17:46:53Z 7.3202 Ride
2015-12-12T15:16:45Z 8.9871 Run
2015-11-10T19:07:27Z 5.3444 Run
2015-11-01T18:23:39Z 8.1649 Run
2015-10-21T17:20:23Z 9.3940 Ride
2015-10-08T18:53:19Z 6.3890 Ride
2015-10-06T18:51:15Z 5.6765 Run
2015-10-03T18:24:31Z 7.0025 Run
2015-09-26T16:00:09Z 8.0303 Run
2015-09-18T18:57:31Z 6.6804 Run
2015-09-09T07:20:26Z 3.4641 Ride
2015-08-30T17:33:47Z 6.1239 Run
2015-08-28T17:28:09Z 10.7709 Run

Now this is exactly the point where you can plug in any dataframe - you just need the same variables. Now for…

Preparing the data

First, we need some global variables we gonna use throughout the rest of the script. Here, you can choose other values as well.

  • goal: (num) - That’s the goal in kilometres you want(ed) to achieve. My value: 500
  • sports: (chr) - Choose from the possible entries in the type column, this should also work with several types. My value: “Run”
  • selected.year: (num) - Choose the year you want to visualise. My value: 2020
  • current.day: (date) - This is just for the vertical line marking the current day of the year. My value: Sys.Date()
goal <- 500
sports <- "Run"
selected.year <- 2020
current.day <- Sys.Date()

We are selecting the relevant rows from the dataframe (year and type of sports). Then, we are sorting by timestamp and convert the timestamp into a date.

select.df <- act.df[act.df$type %in% sports &
                      year(act.df$start_date_local) %in% selected.year,]

select.df <- select.df[order(select.df$start_date_local),]
select.df$date <- as.Date(date(select.df$start_date_local))

In the next step, we are aggregating the dataset by date. Why? Because it might be possible that there are several activities at one day (e.g. when splitting up a run into two shorter ones). It leads to problems later on (when merging) if we have more than one row per date. I check before and after aggregating how many rows there are in the dataset. You will see that some aggregation has been done. By the way: Of course, we have to use sum() as the aggregation function because we want to get the total kilometres at each day.

nrow(select.df)
## [1] 52
select.df <- aggregate(distance ~ date, data = select.df, FUN = sum)
nrow(select.df)
## [1] 50

Now, I will introduce another dataframe, year.df, because our current dataset select.df has one problem when it comes to the visualisation later: select.df has one row per activity. So, unless you run every single day of the year, some (actually most) dates will be left out. However, for the visualisation to really work, we need a datapoint every single day of the year. For every day without a recorded activity, we will put zero kilometres in.

I’m sure there are other ways to do this, but I’m building year.df like this:

  • Create year.df as an one-column dataframe. This column holds a sequence of dates starting at January 1st of selected.year and ending on December 31st of the same year. Note the by = "day" argument in the call to seq().
  • Then, I’m merging year.df with select.df by column date. All entries (= dates) from year.df should be kept. Result of this operation is a two-column year.df holding the additional information from select.df, namely kilometres per day or NA if no activity has been recorded at that day.
  • Sort year.df by date
  • Replace every NA with 0.
year.df <- data.frame(date = seq(as.Date(paste0(selected.year, "-01-01")),
                                 as.Date(paste0(selected.year, "-12-31")),
                                 by = "day"))
year.df <- merge(year.df, select.df, by = "date", all.x = T, all.y = F)
year.df <- year.df[order(year.df$date),]
year.df$distance <- ifelse(is.na(year.df$distance), 0, year.df$distance)

Now for calculating the interesting statistics for each day:

  • Cumulated distance (for this, the dataframe has to be sorted by date, and we did this above)
  • The remaining distance till goal
  • The remaining days until the end of the year
  • The (theoretical) distance per day and week that is needed to complete the goal

We could calculate all these variables within the ggplot() call below - but this would make the plot call quite confusing. So I’m calculating them beforehand for transparency.

year.df$cum.distance <- round(cumsum(year.df$distance))

year.df$remaining.distance <- goal - year.df$cum.distance
year.df$days.till.end <- as.numeric(
  as.Date(paste0(selected.year, "-12-31")) -
    year.df$date)

year.df$dist.per.day.to.goal <- year.df$remaining.distance / year.df$days.till.end
year.df$dist.per.week.to.goal <- year.df$dist.per.day.to.goal * 7

year.df$dist.per.day.to.goal <- round(year.df$dist.per.day.to.goal, 1)
year.df$dist.per.week.to.goal <- round(year.df$dist.per.week.to.goal, 1)

Let’s wrap up data preparations with some “cosmetic” stuff like setting negative remaining distances (when overfulfilling your goal) to 0 and inserting a dash for the final day of the year (this prevents Infs from showing up in the plot).

year.df[year.df$dist.per.day.to.goal < 0, "dist.per.day.to.goal"] <- 0
year.df[year.df$dist.per.week.to.goal < 0, "dist.per.week.to.goal"] <- 0
year.df[year.df$remaining.distance < 0, "remaining.distance"] <- 0

year.df[year.df$days.till.end == 0, "dist.per.day.to.goal"] <- "-"
year.df[year.df$days.till.end == 0, "dist.per.week.to.goal"] <- "-"

Let’s have another look at the resulting dataframe.

scroll_box(kable_styling(kable(year.df)),
           width = "100%", height = "400px")
date distance cum.distance remaining.distance days.till.end dist.per.day.to.goal dist.per.week.to.goal
2020-01-01 0.0000 0 500 365 1.4 9.6
2020-01-02 0.0000 0 500 364 1.4 9.6
2020-01-03 0.0000 0 500 363 1.4 9.6
2020-01-04 0.0000 0 500 362 1.4 9.7
2020-01-05 0.0000 0 500 361 1.4 9.7
2020-01-06 0.0000 0 500 360 1.4 9.7
2020-01-07 0.0000 0 500 359 1.4 9.7
2020-01-08 0.0000 0 500 358 1.4 9.8
2020-01-09 5.0218 5 495 357 1.4 9.7
2020-01-10 0.0000 5 495 356 1.4 9.7
2020-01-11 0.0000 5 495 355 1.4 9.8
2020-01-12 0.0000 5 495 354 1.4 9.8
2020-01-13 0.0000 5 495 353 1.4 9.8
2020-01-14 0.0000 5 495 352 1.4 9.8
2020-01-15 0.0000 5 495 351 1.4 9.9
2020-01-16 0.0000 5 495 350 1.4 9.9
2020-01-17 0.0000 5 495 349 1.4 9.9
2020-01-18 0.0000 5 495 348 1.4 10
2020-01-19 0.0000 5 495 347 1.4 10
2020-01-20 0.0000 5 495 346 1.4 10
2020-01-21 0.0000 5 495 345 1.4 10
2020-01-22 0.0000 5 495 344 1.4 10.1
2020-01-23 0.0000 5 495 343 1.4 10.1
2020-01-24 0.0000 5 495 342 1.4 10.1
2020-01-25 0.0000 5 495 341 1.5 10.2
2020-01-26 0.0000 5 495 340 1.5 10.2
2020-01-27 0.0000 5 495 339 1.5 10.2
2020-01-28 0.0000 5 495 338 1.5 10.3
2020-01-29 0.0000 5 495 337 1.5 10.3
2020-01-30 0.0000 5 495 336 1.5 10.3
2020-01-31 0.0000 5 495 335 1.5 10.3
2020-02-01 0.0000 5 495 334 1.5 10.4
2020-02-02 0.0000 5 495 333 1.5 10.4
2020-02-03 0.0000 5 495 332 1.5 10.4
2020-02-04 0.0000 5 495 331 1.5 10.5
2020-02-05 0.0000 5 495 330 1.5 10.5
2020-02-06 0.0000 5 495 329 1.5 10.5
2020-02-07 0.0000 5 495 328 1.5 10.6
2020-02-08 0.0000 5 495 327 1.5 10.6
2020-02-09 0.0000 5 495 326 1.5 10.6
2020-02-10 0.0000 5 495 325 1.5 10.7
2020-02-11 0.0000 5 495 324 1.5 10.7
2020-02-12 0.0000 5 495 323 1.5 10.7
2020-02-13 0.0000 5 495 322 1.5 10.8
2020-02-14 0.0000 5 495 321 1.5 10.8
2020-02-15 0.0000 5 495 320 1.5 10.8
2020-02-16 0.0000 5 495 319 1.6 10.9
2020-02-17 0.0000 5 495 318 1.6 10.9
2020-02-18 0.0000 5 495 317 1.6 10.9
2020-02-19 5.0079 10 490 316 1.6 10.9
2020-02-20 0.0000 10 490 315 1.6 10.9
2020-02-21 0.0000 10 490 314 1.6 10.9
2020-02-22 0.0000 10 490 313 1.6 11
2020-02-23 7.0074 17 483 312 1.5 10.8
2020-02-24 0.0000 17 483 311 1.6 10.9
2020-02-25 0.0000 17 483 310 1.6 10.9
2020-02-26 0.0000 17 483 309 1.6 10.9
2020-02-27 0.0000 17 483 308 1.6 11
2020-02-28 0.0000 17 483 307 1.6 11
2020-02-29 0.0000 17 483 306 1.6 11
2020-03-01 0.0000 17 483 305 1.6 11.1
2020-03-02 0.0000 17 483 304 1.6 11.1
2020-03-03 0.0000 17 483 303 1.6 11.2
2020-03-04 0.0000 17 483 302 1.6 11.2
2020-03-05 0.0000 17 483 301 1.6 11.2
2020-03-06 0.0000 17 483 300 1.6 11.3
2020-03-07 0.0000 17 483 299 1.6 11.3
2020-03-08 7.0051 24 476 298 1.6 11.2
2020-03-09 0.0000 24 476 297 1.6 11.2
2020-03-10 0.0000 24 476 296 1.6 11.3
2020-03-11 0.0000 24 476 295 1.6 11.3
2020-03-12 0.0000 24 476 294 1.6 11.3
2020-03-13 0.0000 24 476 293 1.6 11.4
2020-03-14 0.0000 24 476 292 1.6 11.4
2020-03-15 0.0000 24 476 291 1.6 11.5
2020-03-16 0.0000 24 476 290 1.6 11.5
2020-03-17 0.0000 24 476 289 1.6 11.5
2020-03-18 7.0131 31 469 288 1.6 11.4
2020-03-19 0.0000 31 469 287 1.6 11.4
2020-03-20 0.0000 31 469 286 1.6 11.5
2020-03-21 0.0000 31 469 285 1.6 11.5
2020-03-22 0.0000 31 469 284 1.7 11.6
2020-03-23 0.0000 31 469 283 1.7 11.6
2020-03-24 0.0000 31 469 282 1.7 11.6
2020-03-25 8.0058 39 461 281 1.6 11.5
2020-03-26 0.0000 39 461 280 1.6 11.5
2020-03-27 0.0000 39 461 279 1.7 11.6
2020-03-28 0.0000 39 461 278 1.7 11.6
2020-03-29 0.0000 39 461 277 1.7 11.6
2020-03-30 8.0094 47 453 276 1.6 11.5
2020-03-31 0.0000 47 453 275 1.6 11.5
2020-04-01 0.0000 47 453 274 1.7 11.6
2020-04-02 0.0000 47 453 273 1.7 11.6
2020-04-03 0.0000 47 453 272 1.7 11.7
2020-04-04 0.0000 47 453 271 1.7 11.7
2020-04-05 11.0084 58 442 270 1.6 11.5
2020-04-06 0.0000 58 442 269 1.6 11.5
2020-04-07 0.0000 58 442 268 1.6 11.5
2020-04-08 0.0000 58 442 267 1.7 11.6
2020-04-09 0.0000 58 442 266 1.7 11.6
2020-04-10 0.0000 58 442 265 1.7 11.7
2020-04-11 0.0000 58 442 264 1.7 11.7
2020-04-12 12.0081 70 430 263 1.6 11.4
2020-04-13 0.0000 70 430 262 1.6 11.5
2020-04-14 0.0000 70 430 261 1.6 11.5
2020-04-15 0.0000 70 430 260 1.7 11.6
2020-04-16 0.0000 70 430 259 1.7 11.6
2020-04-17 5.0091 75 425 258 1.6 11.5
2020-04-18 0.0000 75 425 257 1.7 11.6
2020-04-19 0.0000 75 425 256 1.7 11.6
2020-04-20 7.3816 82 418 255 1.6 11.5
2020-04-21 0.0000 82 418 254 1.6 11.5
2020-04-22 0.0000 82 418 253 1.7 11.6
2020-04-23 0.0000 82 418 252 1.7 11.6
2020-04-24 0.0000 82 418 251 1.7 11.7
2020-04-25 0.0000 82 418 250 1.7 11.7
2020-04-26 0.0000 82 418 249 1.7 11.8
2020-04-27 0.0000 82 418 248 1.7 11.8
2020-04-28 0.0000 82 418 247 1.7 11.8
2020-04-29 0.0000 82 418 246 1.7 11.9
2020-04-30 0.0000 82 418 245 1.7 11.9
2020-05-01 17.0104 99 401 244 1.6 11.5
2020-05-02 0.0000 99 401 243 1.7 11.6
2020-05-03 0.0000 99 401 242 1.7 11.6
2020-05-04 0.0000 99 401 241 1.7 11.6
2020-05-05 0.0000 99 401 240 1.7 11.7
2020-05-06 0.0000 99 401 239 1.7 11.7
2020-05-07 0.0000 99 401 238 1.7 11.8
2020-05-08 7.0076 106 394 237 1.7 11.6
2020-05-09 0.0000 106 394 236 1.7 11.7
2020-05-10 0.0000 106 394 235 1.7 11.7
2020-05-11 0.0000 106 394 234 1.7 11.8
2020-05-12 0.0000 106 394 233 1.7 11.8
2020-05-13 0.0000 106 394 232 1.7 11.9
2020-05-14 0.0000 106 394 231 1.7 11.9
2020-05-15 0.0000 106 394 230 1.7 12
2020-05-16 15.0033 121 379 229 1.7 11.6
2020-05-17 0.0000 121 379 228 1.7 11.6
2020-05-18 0.0000 121 379 227 1.7 11.7
2020-05-19 0.0000 121 379 226 1.7 11.7
2020-05-20 0.0000 121 379 225 1.7 11.8
2020-05-21 0.0000 121 379 224 1.7 11.8
2020-05-22 0.0000 121 379 223 1.7 11.9
2020-05-23 0.0000 121 379 222 1.7 12
2020-05-24 0.0000 121 379 221 1.7 12
2020-05-25 7.0091 129 371 220 1.7 11.8
2020-05-26 0.0000 129 371 219 1.7 11.9
2020-05-27 0.0000 129 371 218 1.7 11.9
2020-05-28 0.0000 129 371 217 1.7 12
2020-05-29 12.8373 141 359 216 1.7 11.6
2020-05-30 0.0000 141 359 215 1.7 11.7
2020-05-31 0.0000 141 359 214 1.7 11.7
2020-06-01 0.0000 141 359 213 1.7 11.8
2020-06-02 0.0000 141 359 212 1.7 11.9
2020-06-03 12.0115 153 347 211 1.6 11.5
2020-06-04 0.0000 153 347 210 1.7 11.6
2020-06-05 0.0000 153 347 209 1.7 11.6
2020-06-06 0.0000 153 347 208 1.7 11.7
2020-06-07 0.0000 153 347 207 1.7 11.7
2020-06-08 0.0000 153 347 206 1.7 11.8
2020-06-09 0.0000 153 347 205 1.7 11.8
2020-06-10 0.0000 153 347 204 1.7 11.9
2020-06-11 0.0000 153 347 203 1.7 12
2020-06-12 0.0000 153 347 202 1.7 12
2020-06-13 0.0000 153 347 201 1.7 12.1
2020-06-14 0.0000 153 347 200 1.7 12.1
2020-06-15 12.0062 165 335 199 1.7 11.8
2020-06-16 0.0000 165 335 198 1.7 11.8
2020-06-17 0.0000 165 335 197 1.7 11.9
2020-06-18 0.0000 165 335 196 1.7 12
2020-06-19 0.0000 165 335 195 1.7 12
2020-06-20 0.0000 165 335 194 1.7 12.1
2020-06-21 0.0000 165 335 193 1.7 12.2
2020-06-22 0.0000 165 335 192 1.7 12.2
2020-06-23 0.0000 165 335 191 1.8 12.3
2020-06-24 0.0000 165 335 190 1.8 12.3
2020-06-25 0.0000 165 335 189 1.8 12.4
2020-06-26 0.0000 165 335 188 1.8 12.5
2020-06-27 0.0000 165 335 187 1.8 12.5
2020-06-28 0.0000 165 335 186 1.8 12.6
2020-06-29 7.0083 172 328 185 1.8 12.4
2020-06-30 0.0000 172 328 184 1.8 12.5
2020-07-01 0.0000 172 328 183 1.8 12.5
2020-07-02 0.0000 172 328 182 1.8 12.6
2020-07-03 0.0000 172 328 181 1.8 12.7
2020-07-04 10.0119 182 318 180 1.8 12.4
2020-07-05 0.0000 182 318 179 1.8 12.4
2020-07-06 0.0000 182 318 178 1.8 12.5
2020-07-07 0.0000 182 318 177 1.8 12.6
2020-07-08 13.0075 195 305 176 1.7 12.1
2020-07-09 0.0000 195 305 175 1.7 12.2
2020-07-10 0.0000 195 305 174 1.8 12.3
2020-07-11 0.0000 195 305 173 1.8 12.3
2020-07-12 0.0000 195 305 172 1.8 12.4
2020-07-13 0.0000 195 305 171 1.8 12.5
2020-07-14 0.0000 195 305 170 1.8 12.6
2020-07-15 0.0000 195 305 169 1.8 12.6
2020-07-16 0.0000 195 305 168 1.8 12.7
2020-07-17 0.0000 195 305 167 1.8 12.8
2020-07-18 0.0000 195 305 166 1.8 12.9
2020-07-19 10.0077 205 295 165 1.8 12.5
2020-07-20 0.0000 205 295 164 1.8 12.6
2020-07-21 0.0000 205 295 163 1.8 12.7
2020-07-22 0.0000 205 295 162 1.8 12.7
2020-07-23 0.0000 205 295 161 1.8 12.8
2020-07-24 0.0000 205 295 160 1.8 12.9
2020-07-25 0.0000 205 295 159 1.9 13
2020-07-26 0.0000 205 295 158 1.9 13.1
2020-07-27 0.0000 205 295 157 1.9 13.2
2020-07-28 0.0000 205 295 156 1.9 13.2
2020-07-29 6.3769 212 288 155 1.9 13
2020-07-30 0.0000 212 288 154 1.9 13.1
2020-07-31 0.0000 212 288 153 1.9 13.2
2020-08-01 0.0000 212 288 152 1.9 13.3
2020-08-02 12.0106 224 276 151 1.8 12.8
2020-08-03 0.0000 224 276 150 1.8 12.9
2020-08-04 0.0000 224 276 149 1.9 13
2020-08-05 0.0000 224 276 148 1.9 13.1
2020-08-06 0.0000 224 276 147 1.9 13.1
2020-08-07 0.0000 224 276 146 1.9 13.2
2020-08-08 5.0087 229 271 145 1.9 13.1
2020-08-09 0.0000 229 271 144 1.9 13.2
2020-08-10 0.0000 229 271 143 1.9 13.3
2020-08-11 0.0000 229 271 142 1.9 13.4
2020-08-12 0.0000 229 271 141 1.9 13.5
2020-08-13 0.0000 229 271 140 1.9 13.6
2020-08-14 0.0000 229 271 139 1.9 13.6
2020-08-15 0.0000 229 271 138 2 13.7
2020-08-16 0.0000 229 271 137 2 13.8
2020-08-17 6.0161 235 265 136 1.9 13.6
2020-08-18 0.0000 235 265 135 2 13.7
2020-08-19 7.0085 242 258 134 1.9 13.5
2020-08-20 0.0000 242 258 133 1.9 13.6
2020-08-21 0.0000 242 258 132 2 13.7
2020-08-22 0.0000 242 258 131 2 13.8
2020-08-23 0.0000 242 258 130 2 13.9
2020-08-24 0.0000 242 258 129 2 14
2020-08-25 5.5073 247 253 128 2 13.8
2020-08-26 10.0204 257 243 127 1.9 13.4
2020-08-27 0.0000 257 243 126 1.9 13.5
2020-08-28 18.0088 275 225 125 1.8 12.6
2020-08-29 0.0000 275 225 124 1.8 12.7
2020-08-30 8.0094 283 217 123 1.8 12.3
2020-08-31 0.0000 283 217 122 1.8 12.5
2020-09-01 10.5043 294 206 121 1.7 11.9
2020-09-02 0.0000 294 206 120 1.7 12
2020-09-03 15.1499 309 191 119 1.6 11.2
2020-09-04 0.0000 309 191 118 1.6 11.3
2020-09-05 0.0000 309 191 117 1.6 11.4
2020-09-06 0.0000 309 191 116 1.6 11.5
2020-09-07 8.7628 318 182 115 1.6 11.1
2020-09-08 0.0000 318 182 114 1.6 11.2
2020-09-09 0.0000 318 182 113 1.6 11.3
2020-09-10 0.0000 318 182 112 1.6 11.4
2020-09-11 0.0000 318 182 111 1.6 11.5
2020-09-12 0.0000 318 182 110 1.7 11.6
2020-09-13 0.0000 318 182 109 1.7 11.7
2020-09-14 7.0129 325 175 108 1.6 11.3
2020-09-15 3.8043 329 171 107 1.6 11.2
2020-09-16 0.0000 329 171 106 1.6 11.3
2020-09-17 0.0000 329 171 105 1.6 11.4
2020-09-18 0.0000 329 171 104 1.6 11.5
2020-09-19 13.0274 342 158 103 1.5 10.7
2020-09-20 0.0000 342 158 102 1.5 10.8
2020-09-21 7.0135 349 151 101 1.5 10.5
2020-09-22 0.0000 349 151 100 1.5 10.6
2020-09-23 0.0000 349 151 99 1.5 10.7
2020-09-24 0.0000 349 151 98 1.5 10.8
2020-09-25 0.0000 349 151 97 1.6 10.9
2020-09-26 0.0000 349 151 96 1.6 11
2020-09-27 0.0000 349 151 95 1.6 11.1
2020-09-28 0.0000 349 151 94 1.6 11.2
2020-09-29 0.0000 349 151 93 1.6 11.4
2020-09-30 15.2126 364 136 92 1.5 10.3
2020-10-01 0.0000 364 136 91 1.5 10.5
2020-10-02 0.0000 364 136 90 1.5 10.6
2020-10-03 8.0098 372 128 89 1.4 10.1
2020-10-04 0.0000 372 128 88 1.5 10.2
2020-10-05 0.0000 372 128 87 1.5 10.3
2020-10-06 0.0000 372 128 86 1.5 10.4
2020-10-07 0.0000 372 128 85 1.5 10.5
2020-10-08 0.0000 372 128 84 1.5 10.7
2020-10-09 0.0000 372 128 83 1.5 10.8
2020-10-10 0.0000 372 128 82 1.6 10.9
2020-10-11 0.0000 372 128 81 1.6 11.1
2020-10-12 0.0000 372 128 80 1.6 11.2
2020-10-13 0.0000 372 128 79 1.6 11.3
2020-10-14 0.0000 372 128 78 1.6 11.5
2020-10-15 0.0000 372 128 77 1.7 11.6
2020-10-16 0.0000 372 128 76 1.7 11.8
2020-10-17 0.0000 372 128 75 1.7 11.9
2020-10-18 0.0000 372 128 74 1.7 12.1
2020-10-19 0.0000 372 128 73 1.8 12.3
2020-10-20 0.0000 372 128 72 1.8 12.4
2020-10-21 15.0126 387 113 71 1.6 11.1
2020-10-22 0.0000 387 113 70 1.6 11.3
2020-10-23 0.0000 387 113 69 1.6 11.5
2020-10-24 10.0092 397 103 68 1.5 10.6
2020-10-25 0.0000 397 103 67 1.5 10.8
2020-10-26 0.0000 397 103 66 1.6 10.9
2020-10-27 0.0000 397 103 65 1.6 11.1
2020-10-28 0.0000 397 103 64 1.6 11.3
2020-10-29 7.0139 404 96 63 1.5 10.7
2020-10-30 0.0000 404 96 62 1.5 10.8
2020-10-31 0.0000 404 96 61 1.6 11
2020-11-01 10.6356 415 85 60 1.4 9.9
2020-11-02 0.0000 415 85 59 1.4 10.1
2020-11-03 0.0000 415 85 58 1.5 10.3
2020-11-04 6.8545 421 79 57 1.4 9.7
2020-11-05 0.0000 421 79 56 1.4 9.9
2020-11-06 0.0000 421 79 55 1.4 10.1
2020-11-07 8.0320 429 71 54 1.3 9.2
2020-11-08 0.0000 429 71 53 1.3 9.4
2020-11-09 0.0000 429 71 52 1.4 9.6
2020-11-10 0.0000 429 71 51 1.4 9.7
2020-11-11 15.1320 445 55 50 1.1 7.7
2020-11-12 0.0000 445 55 49 1.1 7.9
2020-11-13 0.0000 445 55 48 1.1 8
2020-11-14 0.0000 445 55 47 1.2 8.2
2020-11-15 0.0000 445 55 46 1.2 8.4
2020-11-16 0.0000 445 55 45 1.2 8.6
2020-11-17 0.0000 445 55 44 1.2 8.8
2020-11-18 6.8013 451 49 43 1.1 8
2020-11-19 0.0000 451 49 42 1.2 8.2
2020-11-20 0.0000 451 49 41 1.2 8.4
2020-11-21 0.0000 451 49 40 1.2 8.6
2020-11-22 0.0000 451 49 39 1.3 8.8
2020-11-23 0.0000 451 49 38 1.3 9
2020-11-24 0.0000 451 49 37 1.3 9.3
2020-11-25 0.0000 451 49 36 1.4 9.5
2020-11-26 0.0000 451 49 35 1.4 9.8
2020-11-27 0.0000 451 49 34 1.4 10.1
2020-11-28 0.0000 451 49 33 1.5 10.4
2020-11-29 0.0000 451 49 32 1.5 10.7
2020-11-30 0.0000 451 49 31 1.6 11.1
2020-12-01 0.0000 451 49 30 1.6 11.4
2020-12-02 0.0000 451 49 29 1.7 11.8
2020-12-03 0.0000 451 49 28 1.8 12.2
2020-12-04 0.0000 451 49 27 1.8 12.7
2020-12-05 6.7965 458 42 26 1.6 11.3
2020-12-06 0.0000 458 42 25 1.7 11.8
2020-12-07 0.0000 458 42 24 1.8 12.2
2020-12-08 0.0000 458 42 23 1.8 12.8
2020-12-09 0.0000 458 42 22 1.9 13.4
2020-12-10 0.0000 458 42 21 2 14
2020-12-11 0.0000 458 42 20 2.1 14.7
2020-12-12 0.0000 458 42 19 2.2 15.5
2020-12-13 0.0000 458 42 18 2.3 16.3
2020-12-14 0.0000 458 42 17 2.5 17.3
2020-12-15 0.0000 458 42 16 2.6 18.4
2020-12-16 0.0000 458 42 15 2.8 19.6
2020-12-17 0.0000 458 42 14 3 21
2020-12-18 0.0000 458 42 13 3.2 22.6
2020-12-19 0.0000 458 42 12 3.5 24.5
2020-12-20 6.5112 465 35 11 3.2 22.3
2020-12-21 0.0000 465 35 10 3.5 24.5
2020-12-22 0.0000 465 35 9 3.9 27.2
2020-12-23 0.0000 465 35 8 4.4 30.6
2020-12-24 0.0000 465 35 7 5 35
2020-12-25 0.0000 465 35 6 5.8 40.8
2020-12-26 0.0000 465 35 5 7 49
2020-12-27 0.0000 465 35 4 8.8 61.2
2020-12-28 0.0000 465 35 3 11.7 81.7
2020-12-29 0.0000 465 35 2 17.5 122.5
2020-12-30 0.0000 465 35 1 35 245
2020-12-31 0.0000 465 35 0

Plotting and animating

I will start with the static plot and three elements:

  • The line of cumulated kilometres itself (the “steps”)
  • A horizontal line at 500 km (= goal)
  • A vertical line at current.day
  • A middle diagonal giving an impression of the “optimal” way to achieve the goal - steps under the line indicate “underperforming” and steps above the line indicate “overperforming”
  • Labels (the {frame_along} placeholder will be filled by {gganimate} later)
p <- ggplot(year.df, aes(x = date, y = cum.distance)) +
  geom_line() +
  geom_hline(yintercept = goal, col = "red", lwd = 1) +
  geom_vline(xintercept = current.day, col = alpha("grey", .5), lty = "dashed") +
  geom_segment(x = as.Date(paste0(selected.year, "-01-01")), y = 0,
                      xend = as.Date(paste0(selected.year, "-12-31")),
                      yend = goal, lty = "dashed", col = "red") +
  labs(x = "", y = "km completed", title = "Date: {frame_along}")
p

Now, we’re adding more elements to the plot:

  • A diagonal line connecting the current point to December 31st - this shows the “optimal” way to achieve the goal from the current point in time. The steeper this line gets, the harder it is to reach the goal until the end of the year.
  • A point at the end of the line
  • A text label indicating the current value of cumulated kilometres at the end of the line
  • A large label in the lower right part of the plot indicating the current status of the relevant statistics.
p <- p + geom_segment(aes(x = date, y = cum.distance),
               xend = as.Date(paste0(selected.year, "-12-31")),
               yend = goal, lty = "dotted") + 
  geom_point(size = 2) +
  geom_text(aes(date + 7,
                label = round(cum.distance)),
            hjust = 0, size = 4) +
  geom_label(aes(label = paste0("Remaining: ", remaining.distance, " km;\n",
                                "To achieve goal: ", dist.per.day.to.goal, " km/day\n",
                                "or ", dist.per.week.to.goal, " km/week"),
                 x = as.Date(paste0(selected.year, "-09-15")),
                 y = goal / 10),
             hjust = 0) +
  coord_cartesian(clip = 'off') +
  theme_minimal()
p

Well, this doesn’t look too good, right? It’s because all the animation phases (at least for the line) are plotted above each other. We need to add the animation element from {gganimate}. Here, transition_reveal(date) reveals the different phases along the date variable. Note that there are also other animation elements available for {gganimate}.

p <- p + transition_reveal(date)

And the final step: Animating the whole thing and write it into an MP4 file:

animate(p, fps = 60, duration = 30, width = 1440, height = 900,
        res = 200, end_pause = 25,
        renderer = av_renderer(file = "runs.mp4"))

That’s it. Now feel free to put in your own data and goal and let me know how it goes.