2016-09-25 74 views
5

我想通过应用调用另一个数据帧的函数来变更数据帧。我可以通过几种不同的方式实现这一点,但想知道如何“正确地”做到这一点。dplyr mutate调用另一个数据帧

这是我想要做的一个例子。我有一个数据框,有一些开始时间,另一个有一些时间观察。我想返回一个包含开始时间的数据帧,以及在开始时间之后的某个窗口内发生的观察次数。例如

set.seed(1337) 
df1 <- data.frame(id=LETTERS[1:3], start_time=1:3*10) 
df2 <- data.frame(time=runif(100)*100) 
lapply(df1$start_time, function(s) sum(df2$time>s & df2$time<(s+15))) 

我已经走到这一步,与dplyr最好的是以下(但丢失了身份变量):

df1 %>% 
    rowwise() %>% 
    do(count = filter(df2, time>.$start_time, time < (.$start_time + 15))) %>% 
    mutate(n=nrow(count)) 

输出:

Source: local data frame [3 x 2] 
Groups: <by row> 

# A tibble: 3 × 2 
        count  n 
       <list> <int> 
1 <data.frame [17 × 1]> 17 
2 <data.frame [18 × 1]> 18 
3 <data.frame [10 × 1]> 10 

我期待的是能够做到这一点:

df1 <- data.frame(id=LETTERS[1:3], start_time=1:3*10) 
df2 <- data.frame(time=runif(100)*100) 
df1 %>% 
    group_by(id) %>% 
    mutate(count = nrow(filter(df2, time>start_time, time<(start_time+15)))) 

但是这返回错误:

Error: comparison (6) is possible only for atomic and list types 

这是干什么的dplyr方式?

回答

2

另一种略有不同的方法使用dplyr有:

result <- df1 %>% group_by(id) %>% 
        summarise(count = length(which(df2$time > start_time & 
               df2$time < (start_time+15)))) 

print(result) 
### A tibble: 3 x 2 
##  id count 
## <fctr> <int> 
##1  A 17 
##2  B 18 
##3  C 10 

我相信你可以使用lengthwhich计算出现的次数为对于df1中的每个id,您的条件是正确的。然后,通过id进行分组,并将其用于summarise


如果有可能不止一个start_timeid,那么你可以使用相同的功能,但rowwisemutate

result <- df1 %>% rowwise() %>% 
        mutate(count = length(which(df2$time > start_time & 
               df2$time < (start_time+15)))) 
print(result) 
##Source: local data frame [3 x 3] 
##Groups: <by row> 
## 
### A tibble: 3 x 3 
##  id start_time count 
## <fctr>  <dbl> <int> 
##1  A   10 17 
##2  B   20 18 
##3  C   30 10 
+0

这也适用,如果我们使用'mutate'来代替'summarise',这样做的好处是,如果组中有额外变量由变量赋值,它们不会被丢弃 – kungfujam

+0

@kungfujam:是的,但如果有我们需要使用'rowwise'函数来代替'group_by(id)'。请参阅我的编辑。 – aichao

+0

非常真实,谢谢。 – kungfujam

3

这里是data.table一个选项,我们可以使用non-equi加入

library(data.table)#1.9.7+ 
setDT(df1)[, start_timeNew := start_time + 15] 
setDT(df2)[df1, .(id, .N), on = .(time > start_time, time < start_timeNew), 
      by = .EACHI][, c('id', 'N'), with = FALSE] 
# id N 
#1: A 17 
#2: B 18 
#3: C 10 

可以得到相同的数作为OP的base R方法

sapply(df1$start_time, function(s) sum(df2$time>s & df2$time<(s+15))) 
#[1] 17 18 10 

如果我们需要' id'变量也作为dplyr的输出,我们可以修改OP的代码

df1 %>% 
    rowwise() %>% 
    do(data.frame(., count = filter(df2, time>.$start_time, 
           time < (.$start_time + 15)))) %>% 
    group_by(id) %>% 
    summarise(n = n()) 
#  id  n 
# <fctr> <int> 
#1  A 17 
#2  B 18 
#3  C 10 

或者另一种选择是从purrrmapdplyr

library(purrr) 
df1 %>% 
    split(.$id) %>% 
    map_df(~mutate(., N = sum(df2$time >start_time & df2$time < start_time + 15))) %>% 
    select(-start_time) 
# id N 
#1 A 17 
#2 B 18 
#3 C 10 
+0

欢呼。添加一个种子,以允许准确再现 – kungfujam

+0

@kungfujam谢谢,我更新了基于该种子的输出 – akrun

+1

第二种解决方案(和第三种)完全符合我的要求。 – kungfujam