2016-09-25 15 views
5

Chciałbym zmutować ramkę danych, stosując funkcję, która wywołuje inną ramkę danych. Potrafię to osiągnąć na kilka różnych sposobów, ale chciałbym wiedzieć, jak to zrobić "właściwie".dplyr mutate wywoływanie innej ramki danych

Oto przykład tego, co próbuję zrobić. Mam ramkę danych z pewnymi czasami rozpoczęcia, a drugą z pewnymi obserwacjami w czasie. Chciałbym zwrócić ramkę danych z czasem rozpoczęcia i liczbą obserwacji, które pojawią się w pewnym oknie po czasie rozpoczęcia. na przykład

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))) 

Najlepszym mam tak daleko z dplyr jest następujące (ale to traci zmiennych tożsamości):

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

wyjściowa:

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 

Spodziewałem się w stanie to zrobić:

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)))) 

, ale to zwraca błąd:

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

Jaki jest sposób na zrobienie tego?

Odpowiedz

2

Innym nieco odmienne podejście używając 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 

wierzę można użyć length i which policzyć liczbę wystąpień dla który twój warunek jest prawdziwy dla każdego id w df1. Następnie grupuj według id i użyj tego do summarise.


Jeśli istnieją prawdopodobnie więcej niż jeden start_time za id, można użyć tej samej funkcji, ale rowwise iz mutate:

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

Działa to również, jeśli użyjemy 'mutate' zamiast" streszczenia ", a zaletą jest to, że jeśli istnieją dodatkowe zmienne do grupy przez vary, nie są one pomijane – kungfujam

+0

@kungfujam: Tak, ale jeśli istnieje są więcej niż jednym 'start_time' na' id', wtedy musimy użyć funkcji 'rowwise' zamiast' group_by (id) '. Zobacz moją edycję. – aichao

+0

To prawda, dziękuję. – kungfujam

3

Oto jedna opcja z data.table gdzie możemy użyć non-equi dołącza

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 

który daje taką samą liczbę jak w PO za base R metody

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

Jeżeli potrzebujemy " zmienna id 'również jako wyjście w dplyr, możemy zmodyfikować kod 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 

Albo inna opcja jest map z purrr z dplyr

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

okrzyki. Dodano nasiona, aby umożliwić dokładne odtworzenie – kungfujam

+0

@kungfujam dzięki, zaktualizowałem dane wyjściowe na podstawie tego nasiona – akrun

+1

Drugie rozwiązanie (i trzecie) robi dokładnie to, co chcę. – kungfujam