Можете ли вы сделать dplyr :: mutate и dplyr :: lag default = своим собственным входным значением?

Это похоже на этот пост о задержке dplyr и это сообщение о задержке изменения dplyr, но ни один из них не задает этот вопрос о том, что по умолчанию входное значение. Я использую dplyr для изменения нового поля, которое представляет собой запаздывающее смещение другого поля (которое я преобразовал в POSIXct). Цель состоит в том, что для данного IP-адреса я хотел бы получить некоторую сводную статистику по разнице между моментами, когда он появляется в моем списке. У меня тоже около 12 миллионов строк.

Данные выглядят так (до мутации)

ip             hour         snap
192.168.1.2    2017070700    0
192.168.1.2    2017070700   15
192.168.1.4    2017070700    0
192.168.1.4    2017070701   45
192.168.1.4    2017070702   30
192.168.1.7    2017070700   15

«час» - целое число, но должно быть отметкой времени.

«моментальный снимок» - это одно из 4 значений «моментального снимка», которые соответствуют 15-минутному приращению.

Вот код создания data.frame:

test <- data.frame(ip=c("192.168.1.2","192.168.1.2","192.168.1.4","192.168.1.4","192.168.1.4","192.168.1.7"), hour=c(2017070700,2017070700,2017070700,2017070701,2017070702,2017070700), snap=c(0,15,0,45,30,15))

На каждый IP-адрес приходится сотни, а иногда и тысячи временных меток. В приведенном ниже коде используется dplyr для

  • а) дополнить 0 ведущим 0,
  • б) объединить два целочисленных поля "дата" в одно поле,
  • c) преобразовать объединенное целочисленное поле 'date' в дату POSIX,
  • г) группировать по ip,
  • e) изменить новый столбец, который отстает от старой метки времени на 1, и, если значение равно NA, вернуться к исходному значению (ЭТО ТОЧКА, КОТОРАЯ НЕ РАБОТАЕТ), и
  • f) изменить новый столбец, который принимает разницу текущего времени и предыдущего времени (по ip).

Эти шаги относятся к комментариям в конце каждой строки.

timedelta <- test %>% 
  mutate(snap = formatC(snap, width=2, flag=0)) %>%                      # a) 
  mutate(fulldateint = paste(hour, snap, sep="")) %>%                    # b) 
  mutate(fulldate = as.POSIXct(strptime(fulldateint, "%Y%m%d%H%M"))) %>% # c) 
  group_by(ip) %>%                                                       # d) 
  mutate(shifted = dplyr::lag(fulldate, default=fulldate)) %>%           # e) 
  mutate(diff = fulldate-shifted)                                        # f) 

После мутации данные должны выглядеть так:

           ip       hour  snap  fulldateint            fulldate             shifted      diff
       <fctr>      <dbl> <chr>        <chr>              <dttm>              <dttm>    <time>
1 192.168.1.2 2017070700    00 201707070000 2017-07-07 00:00:00 2017-07-07 00:00:00    0 secs
2 192.168.1.2 2017070700    15 201707070015 2017-07-07 00:15:00 2017-07-07 00:00:00  900 secs
3 192.168.1.4 2017070700    00 201707070000 2017-07-07 00:00:00 2017-07-07 00:00:00    0 secs
4 192.168.1.4 2017070701    45 201707070145 2017-07-07 01:45:00 2017-07-07 00:00:00 6300 secs
5 192.168.1.4 2017070702    30 201707070230 2017-07-07 02:30:00 2017-07-07 01:45:00 2700 secs
6 192.168.1.7 2017070700    15 201707070015 2017-07-07 00:15:00 2017-07-07 00:15:00    0 secs

И если бы я мог установить для лага исходное значение по умолчанию, дельта-T всегда была бы равна 0, если у него нет предыдущего значения (что является желаемым результатом).

Однако dplyr::lag(fulldate, default=fulldate) выдает ошибку

Error in mutate_impl(.data, dots) : 
Column `shifted` must be length 2 (the group size) or one, not 3

Это сработает, если я использую Fulldate 1, но тогда я теряю group_by(ip) результат, который нужен. Можно ли сделать ссылку на задержку своим собственным вводом в dplyr?

Примечание: я бы предпочел, чтобы ответ был с использованием dplyr, а не data.table, если это возможно, поскольку я использовал dplyr в качестве нашей основной библиотеки для изменения данных, но также я хотел бы предложить мистеру Уикхему, чтобы он взял это находится на рассмотрении, если у него действительно нет решения в существующей библиотеке dplyr.


person TheProletariat    schedule 18.08.2017    source источник
comment
Судя по названию, вы могли бы спросить это гораздо короче ... Думаю, вы хотите dplyr::lag(fulldate, default = first(fulldate))?   -  person Frank    schedule 19.08.2017
comment
Дело принято. Сначала работает вместе с group_by ()?   -  person TheProletariat    schedule 19.08.2017
comment
Ok. Для справки в будущем я имел в виду, что если ваш вопрос не касается formatC, as.POSIXct и др., Вы можете заранее настроить эти данные, а затем использовать dput; см. stackoverflow.com/questions/5963269/, если вы с ним не знакомы. Кстати, я думаю, что аргументу default нужно одно значение, но вместо этого вы дали ему вектор - я думаю, вы упустили этот момент, поэтому я просто пытаюсь его прояснить. Да, first внутри mutate после group_by будет работать над каждой группой отдельно.   -  person Frank    schedule 19.08.2017
comment
Спасибо за советы. Очень хороший материал. И да, если first () работает с каждой группой отдельно, то это именно то, что я искал. Не могли бы вы сделать репост в качестве ответа, чтобы я мог согласиться? Кроме того, не могли бы вы предложить обрезать роман до более удобочитаемого формата для потомков?   -  person TheProletariat    schedule 19.08.2017
comment
Хорошо, я отправлю. Нет, я думаю, не нужно его редактировать; просто полезно иметь в виду в следующий раз. Название хорошее, поэтому, надеюсь, люди смогут его найти и пропустить роман, чтобы прочитать ответы.   -  person Frank    schedule 19.08.2017
comment
Кроме того, я должен упомянуть, что я пробовал dplyr :: lag (fulldate, default = fulldate [1])) по указанным вами причинам, но это не сработало, потому что он не выбирает первый элемент для каждой группы, а только первый элемент всего столбца.   -  person TheProletariat    schedule 19.08.2017
comment
О, это интересно и неожиданно.   -  person Frank    schedule 19.08.2017


Ответы (3)


В коде OP ...

...
d) group_by(ip) %>%
e) mutate(shifted = dplyr::lag(fulldate, default=fulldate)) %>%
...

Аргумент default= должен иметь длину один. В этом случае должна сработать замена кода OP на default = first(fulldate) (поскольку у первого элемента не будет задержек, и поэтому нам нужно применить значение по умолчанию).

Связанные случаи:

  • Точно так же с «ведущим» нам нужно dplyr::lead(x, default=last(x)).
  • С запаздыванием или опережением более чем на один шаг (n больше 1) default= не сможет этого сделать, и нам, вероятно, придется переключиться на if_else или case_when или аналогичный. (Я не уверен насчет нынешней идиомы тидиверса.)
person Frank    schedule 18.08.2017

Я думаю, что решение Фрэнка работает очень хорошо. Вот полный пример:


library(dplyr, warn.conflicts = F)

test <- data.frame(ip=c("192.168.1.2","192.168.1.2","192.168.1.4","192.168.1.4","192.168.1.4","192.168.1.7"),
                   hour=c(2017070700,2017070700,2017070700,2017070701,2017070702,2017070700),
                   snap=c(0,15,0,45,30,15))


test %>%
  mutate(snap = formatC(snap, width = 2, flag = 0)) %>%
  mutate(fulldateint = paste(hour, snap, sep = "")) %>%
  mutate(fulldate = as.POSIXct(strptime(fulldateint, "%Y%m%d%H%M"))) %>%
  group_by(ip) %>%
  mutate(shifted = lag(fulldate, default = first(fulldate))) %>%
  mutate(diff = fulldate - shifted) %>% 
  ungroup() %>% 
  select(ip, fulldate, shifted, diff)

#> # A tibble: 6 x 4
#>            ip            fulldate             shifted      diff
#>        <fctr>              <dttm>              <dttm>    <time>
#> 1 192.168.1.2 2017-07-07 00:00:00 2017-07-07 00:00:00    0 secs
#> 2 192.168.1.2 2017-07-07 00:15:00 2017-07-07 00:00:00  900 secs
#> 3 192.168.1.4 2017-07-07 00:00:00 2017-07-07 00:00:00    0 secs
#> 4 192.168.1.4 2017-07-07 01:45:00 2017-07-07 00:00:00 6300 secs
#> 5 192.168.1.4 2017-07-07 02:30:00 2017-07-07 01:45:00 2700 secs
#> 6 192.168.1.7 2017-07-07 00:15:00 2017-07-07 00:15:00    0 secs
person LVG77    schedule 18.08.2017

Как насчет

ifelse(is.na(lag(value)), value, lag(value))
person mikeck    schedule 18.08.2017
comment
Я должен был упомянуть, что я пробовал некоторую условную замену NA, но dplyr явно капризничает с условными операторами, как в первой ссылке, которую я опубликовал выше ... независимо от того, ваша идея работает, но она каким-то образом преобразуется обратно в двойную, что очень странно . Я использовал этот синтаксис: mutate (shift = ifelse (is.na (lag (lag (fulldate)), fulldate, lag (fulldate))), но вместо таких смещенных значений ‹dttm› (2017-07-07 00:00: 00), он дал мне такие значения ‹dbl›: (1499407200). Не думаю, что допустил синтаксическую ошибку или ошибку в названии, но счастлив за 2-ю (сотню) пару глаз. - person TheProletariat; 18.08.2017
comment
@TheProletariat if_else () - это dplyr альтернатива ifelse (), которая поддерживает формат - person MBorg; 30.03.2021