你能让 dplyr::mutate 和 dplyr::lag 默认 = 自己的输入值吗?
Posted
技术标签:
【中文标题】你能让 dplyr::mutate 和 dplyr::lag 默认 = 自己的输入值吗?【英文标题】:Can you make dplyr::mutate and dplyr::lag default = its own input value? 【发布时间】:2018-01-27 14:33:58 【问题描述】:这类似于this dplyr lag post 和this dplyr mutate lag post,但它们都没有问这个关于默认输入值的问题。我正在使用 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) 将两个整数“日期”字段合并为一个字段, c) 将合并的整数“日期”字段转换为 POSIX 日期, d) 按 ip 分组, e) 改变一个比旧时间戳滞后 1 的新列,如果值为 NA,则参考原始值(这是不起作用的位),并且 f) 改变一个新列,该列采用当前时间和上一次时间的差值(通过 ip)。这些步骤指的是每行末尾的 cmets。
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 库中确实没有解决方案,他会考虑这一点。
【问题讨论】:
从你的标题来看,你可以用更短的方式问这个问题......我猜你想要dplyr::lag(fulldate, default = first(fulldate))
?
点了。首先与 group_by() 一起工作吗?
好的。为了将来参考,我的意思是除非您的问题是关于 formatC、as.POSIXct 等,否则您可以事先进行这些数据调整,然后使用 dput
;如果您不熟悉它,请参阅***.com/questions/5963269/…。顺便说一句,我认为 default
参数需要一个单一的值,但是你给了它一个向量——我认为你错过了这一点,所以我只是想澄清一下。是的,first
在 mutate
之后 group_by
将分别作用于每个组。
好的,我会发帖的。不,我认为不需要编辑它;只是有用,下次要记住。标题很好,所以人们希望能够找到它并选择跳过小说阅读答案。
另外,我应该提到我已经尝试 dplyr::lag(fulldate, default=fulldate[1])) 出于您所说的原因,但它没有工作,因为它没有选择每组的第一个元素,只是整个列的第一个元素。
【参考方案1】:
在 OP 的代码中 ...
... d) group_by(ip) %>% e) mutate(shifted = dplyr::lag(fulldate, default=fulldate)) %>% ...
default=
参数的长度应为 1。在这种情况下,用 default = first(fulldate)
替换 OP 的代码应该可以工作(因为第一个元素不会有滞后,所以我们需要应用默认值)。
相关案例:
类似地,如果有“线索”,我们需要dplyr::lead(x, default=last(x))
。
如果滞后或领先超过一步(n
大于 1),default=
无法做到这一点,我们可能需要切换到 if_else
或 case_when
或类似的。 (我不确定当前的 tidyverse 成语。)
【讨论】:
【参考方案2】:我认为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
【讨论】:
【参考方案3】:怎么样
ifelse(is.na(lag(value)), value, lag(value))
【讨论】:
我应该提到我尝试了一些有条件的 NA 替换,但 dplyr 显然对条件语句很古怪,就像我在上面发布的第一个链接中一样......无论如何,你的想法有效,但它以某种方式转换回来到一个双,这是很奇怪的。我使用了这种语法:mutate(shifted = ifelse(is.na(lag(fulldate)), fulldate, lag(fulldate))),但不是像这样移位以上是关于你能让 dplyr::mutate 和 dplyr::lag 默认 = 自己的输入值吗?的主要内容,如果未能解决你的问题,请参考以下文章
在 R 中:将列名作为参数传递,并在 dplyr::mutate() 和 lazyeval::interp() 的函数中使用它
使用 dplyr::mutate 对数据帧进行 Fisher 的测试统计