2016-09-25 72 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