difftime с отставанием / опережением на основе условий из другого столбца (и аккуратной структуры данных)

Я взял функцию из этого сообщения, чтобы создать случайное время эффективно генерировать случайную выборку времени и дат между двумя датами.

Вот мой набор данных:

latemail <- function(N, st="2012/01/01", et="2012/12/31") {
       st <- as.POSIXct(as.Date(st))
       et <- as.POSIXct(as.Date(et))
       dt <- as.numeric(difftime(et,st,unit="sec"))
       ev <- sort(runif(N, 0, dt))
       rt <- st + ev
 }
 set.seed(42); 
 a<- print(latemail(9))  
 a<- sort(a)

data <- data.frame(time= a, place=c("Start", "B", "O", "A", "A", "Start", "A", "O", "A"), ID=c(rep(1, 5), rep(2,4)))

Данные выглядят так:

                  time place ID
1  2012-02-19 04:40:45 Start  1
2  2012-04-14 12:34:56     B  1
3  2012-07-08 13:16:49     O  1
4  2012-08-22 07:41:26     A  1
5  2012-08-27 21:15:08     A  1
6  2012-09-14 10:22:03 Start  2
7  2012-09-25 22:30:49     B  2
8  2012-10-30 03:43:16     B  2
9  2012-11-29 22:42:03     O  2

Я хотел бы взять разницу во времени, когда место стоит «О» и «начало» в каждой группе (ID).

Вопросы: 1) Соответствует ли структура приведенных данных аккуратным данным? Потому что я думаю, что имеет смысл распределить данные, чтобы можно было использовать столбец difftime. Если у каждого идентификатора есть только одна строка, это будут аккуратные данные (чтобы разделить, например, между A, их можно было бы назвать A_1, A_2, если они должны быть столбцами). Но в каком формате аккуратные данные.
2) Есть ли лучший способ сделать это, чем ниже?

  data2 <- data %>% 
  filter(place %in% c("Start", "O")) %>% 
  group_by(ID) %>% 
  mutate(diff=difftime(lead(time), time, units="days")) %>% 
  filter(!is.na(diff))

Выход:

# A tibble: 2 x 4
# Groups:   ID [2]
  time                place    ID diff            
  <dttm>              <fct> <dbl> <time>          
1 2012-02-19 04:40:45 Start     1 140.31671 days  
2 2012-09-25 22:30:49 Start     2 " 65.04947 days"

person xhr489    schedule 28.11.2018    source источник


Ответы (1)


Мы можем сохранить структуру как есть, но немного упростить код, используя summarise (при условии, что у вас есть только один "O" и "Start" для каждого ID.

library(dplyr)

data %>%
  group_by(ID) %>%
  summarise(diff = difftime(time[place == "O"], time[place == "Start"]))


#     ID diff            
#  <dbl> <time>          
#1     1 140.31671 days  
#2     2 " 65.04947 days"

Если есть некоторые ID, у которых нет ни "Start", ни "O", мы можем вернуть для них NA.

data %>%
  group_by(ID) %>%
  summarise(diff = if (any(place == "O") & any(place == "Start"))
                   difftime(time[place == "O"], time[place == "Start"]) else NA)
person Ronak Shah    schedule 28.11.2018
comment
Спасибо, это более элегантно! Не могли бы вы также прокомментировать чистые данные. Я немного отредактировал свой вопрос. - person xhr489; 28.11.2018
comment
@ Дэвид Я не думаю, что spread данные здесь нужны. Здесь в каждой группе вы хотите вычесть только два значения, что возможно с помощью summarise. Расширение данных добавило бы ненужных сложностей. - person Ronak Shah; 28.11.2018
comment
Хорошо спасибо. С моим реальным набором данных я получаю ошибку Column diff must be length 1 (a summary value), not 0. Я думаю, может быть, потому, что у некоторых идентификаторов нет O. У вас есть решение, или мне следует опубликовать новый вопрос? - person xhr489; 28.11.2018
comment
@David Если для группы нет O, каким должен быть результат? - person Ronak Shah; 28.11.2018
comment
@Roank Shah: Я думаю, NA или какой-то текст, говорящий об этом (например, нет O). - person xhr489; 28.11.2018
comment
Еще раз привет, теперь я получаю эту ошибку: «Столбец diff должен иметь длину 1 (итоговое значение), а не 2». Я предполагаю, что в некоторых группах у меня есть более одной строки с O. Как я могу вернуть что-то (кроме NA), чтобы я мог различить эту ситуацию. - person xhr489; 29.11.2018
comment
@David в этом случае используйте which.max, чтобы получить первое совпадение. data %>% group_by(ID) %>% summarise(diff = if (any(place == "O") & any(place == "Start")) difftime(time[which.max(place == "O")], time[which.max(place == "Start")]) else NA) - person Ronak Shah; 29.11.2018