你能让 dplyr::mutate 和 dplyr::lag 默认 = 自己的输入值吗?
Can you make dplyr::mutate and dplyr::lag default = its own input value?
这类似于 this dplyr lag post, and ,但它们都没有询问有关默认输入值的问题。我正在使用 dplyr 来改变一个新字段,该字段是另一个字段的滞后偏移量(我已转换为 POSIXct)。目标是,对于给定的 ip,我想知道它出现在我的列表中的所有时间之间的增量的一些摘要统计信息。我也有大约 1200 万行。
数据看起来像这样(突变前)
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
'hour'是整数,但应该是时间戳。
'snap' 是代表 15 分钟增量的 4 个 'snapshot' 值之一。
这是 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 来
- a) 用前导 0 填充 0,
- b) 将两个整数 'date' 字段合并为一个字段,
- c) 将合并的整数 'date' 字段转换为 POSIX 日期,
- d) 按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
而且如果我可以延迟到其原始值的默认值,则 'delta-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
如果我使用 fulldate1,它确实有效,但我失去了 group_by(ip)
结果,这是必要的。是否可以在 dplyr 中使滞后参考成为其自己的输入?
注意:如果可能的话,我真的更喜欢使用 dplyr 而不是 data.table 的答案,因为我一直在使用 dplyr 作为我们的主要数据处理库,而且因为我想建议Wickham 先生,如果在现有的 dplyr 库中确实没有解决方案,他会考虑这一点。
怎么样
ifelse(is.na(lag(value)), value, lag(value))
在 OP 的代码中...
...
d) group_by(ip) %>%
e) mutate(shifted = dplyr::lag(fulldate, default=fulldate)) %>%
...
default=
参数的长度应为 1。在这种情况下,用 default = first(fulldate)
替换 OP 的代码应该可行(因为第一个元素不会有滞后,因此我们需要应用默认值)。
相关案例:
- 同样,对于 "lead",我们需要
dplyr::lead(x, default=last(x))
。
- 如果滞后或领先超过一步(
n
大于 1),default=
无法做到这一点,我们可能需要切换到 if_else
或 case_when
或类似的。 (我不确定当前的 tidyverse 习语。)
我认为Frank的解决方案效果很好。这是完整的示例:
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
这类似于 this dplyr lag post, and
数据看起来像这样(突变前)
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
'hour'是整数,但应该是时间戳。
'snap' 是代表 15 分钟增量的 4 个 'snapshot' 值之一。
这是 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 来
- a) 用前导 0 填充 0,
- b) 将两个整数 'date' 字段合并为一个字段,
- c) 将合并的整数 'date' 字段转换为 POSIX 日期,
- d) 按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
而且如果我可以延迟到其原始值的默认值,则 'delta-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
如果我使用 fulldate1,它确实有效,但我失去了 group_by(ip)
结果,这是必要的。是否可以在 dplyr 中使滞后参考成为其自己的输入?
注意:如果可能的话,我真的更喜欢使用 dplyr 而不是 data.table 的答案,因为我一直在使用 dplyr 作为我们的主要数据处理库,而且因为我想建议Wickham 先生,如果在现有的 dplyr 库中确实没有解决方案,他会考虑这一点。
怎么样
ifelse(is.na(lag(value)), value, lag(value))
在 OP 的代码中...
... d) group_by(ip) %>% e) mutate(shifted = dplyr::lag(fulldate, default=fulldate)) %>% ...
default=
参数的长度应为 1。在这种情况下,用 default = first(fulldate)
替换 OP 的代码应该可行(因为第一个元素不会有滞后,因此我们需要应用默认值)。
相关案例:
- 同样,对于 "lead",我们需要
dplyr::lead(x, default=last(x))
。 - 如果滞后或领先超过一步(
n
大于 1),default=
无法做到这一点,我们可能需要切换到if_else
或case_when
或类似的。 (我不确定当前的 tidyverse 习语。)
我认为Frank的解决方案效果很好。这是完整的示例:
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