Search code examples
rdplyrdata.tablerun-length-encodingdifftime

Using run-length encoding with timestamps


My objective is to use the rle() function to find out how long bike stations have been empty. Using the test data below, rle(test$bikes) will return the lengths of recurring values in test$bikes.

> rle(test$bikes)
Run Length Encoding
  lengths: int [1:9] 3 2 3 1 5 1 7 1 1
  values : num [1:9] 0 1 2 1 0 1 0 1 0



> test
   station_id                time bikes
1           1 2017-12-25 00:00:02     0
2           1 2017-12-25 00:01:02     0
3           1 2017-12-25 00:02:02     0
4           1 2017-12-25 00:03:02     1
5           2 2017-12-25 00:04:02     1
6           2 2017-12-25 00:05:02     2
7           2 2017-12-25 00:06:02     2
8           2 2017-12-25 00:07:02     2
9           2 2017-12-25 00:08:02     1
10          3 2017-12-25 00:09:02     0
11          3 2017-12-25 00:10:02     0
12          3 2017-12-25 00:11:02     0
13          3 2017-12-25 00:12:02     0
14          3 2017-12-25 00:13:02     0
15          4 2017-12-25 00:14:03     1
16          4 2017-12-25 00:15:02     0
17          4 2017-12-25 00:16:02     0
18          4 2017-12-25 00:17:02     0
19          4 2017-12-25 00:18:02     0
20          5 2017-12-25 00:19:02     0
21          5 2017-12-25 00:20:02     0
22          5 2017-12-25 00:21:02     0
23          5 2017-12-25 00:22:02     1
24          5 2017-12-25 00:23:02     0

My goal is to go one step further and to produce an output that groups by staiton_id, and returns the difference in time (in minutes) only when test$bikes has recurring zeros. This can occur multiple times for each station (for exmaple, for station 5 in the test data). In the end, the above dataset would produce the following output:

> output
  station_id diff_time      interval
1          1         2 00:00 - 00:02
2          3         4 00:09 - 00:13
3          4         3 00:15 - 00:18
4          5         2 00:19 -00:21
5          5         0 00:23 - 00:23

Any suggestions on how to do this with dplyr and rle would be appreciated!

Here is the test data:

> dput(test)
structure(list(station_id = c(1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 
3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5), time = structure(c(1514178002.88487, 
1514178062.99145, 1514178122.88463, 1514178182.63461, 1514178242.71401, 
1514178302.20358, 1514178362.13263, 1514178422.88907, 1514178482.6502, 
1514178542.59171, 1514178602.51222, 1514178662.23203, 1514178722.04015, 
1514178782.87382, 1514178843.02124, 1514178902.71852, 1514178962.6987, 
1514179022.42077, 1514179082.19535, 1514179142.97175, 1514179202.81556, 
1514179262.85187, 1514179322.66264, 1514179382.50223), class = c("POSIXct", 
"POSIXt"), tzone = ""), bikes = c(0, 0, 0, 1, 1, 2, 2, 2, 1, 
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0)), .Names = c("station_id", 
"time", "bikes"), row.names = c(NA, 24L), class = "data.frame")

Solution

  • We can use rleid from data.table. Create a grouping variable ('grp') based on the run-length-id of 'bikes', then grouped by 'station_id' and 'grp', specify the i where 'bikes' are 0, summarise the output by taking the difftime of the last and first observation of 'time' and also paste the formattted corresponding 'time' elements

    library(data.table)
    setDT(test)[, grp:= rleid(bikes)][bikes==0, 
     .(diff_time = as.numeric(round(difftime(time[.N], time[1], unit = "min"))), 
      interval = paste(format(time[1], "%M:%S"), format(time[.N], "%M:%S"), sep=" - ")),
              .(station_id, grp)]