2015-10-07 8 views
11

Problem, który próbuję rozwiązać, polega na tym, że mam ramkę danych z posortowaną w niej zmienną POSIX. Każdy wiersz jest podzielony na kategorie i chcę uzyskać różnice czasowe między każdym wierszem dla każdego poziomu i dodać te dane z powrotem do nowej zmiennej. Powtarzalny problem jest jak poniżej. Poniższa funkcja służy tylko do tworzenia przykładowych danych z losowymi czasami dla celów tego pytania.Jak obliczyć różnicę czasu między wierszem a poprzednim wierszem danych.frame według poziomów

random.time <- function(N, start, end) { 
    st <- as.POSIXct(start) 
    en <- as.POSIXct(end) 
    dt <- as.numeric(difftime(en, st, unit="sec")) 
    ev <- sort(runif(N, 0, dt)) 
    rt <- st + ev 
    return(rt) 
} 

Kod dla symulowania tego problemu jest, jak poniżej:

set.seed(123) 
category <- sample(LETTERS[1:5], 20, replace=TRUE) 
randtime <- random.time(20, '2015/06/01 08:00:00', '2015/06/01 18:00:00') 
df <- data.frame(category, randtime) 

Oczekiwany Uzyskana ramka danych jest poniżej

>category randtime timediff (secs) 
>A 2015-06-01 09:05:00 0 
>A 2015-06-01 09:06:30 90 
>A 2015-06-01 09:10:00 210 
>B 2015-06-01 10:18:58 0 
>B 2015-06-01 10:19:58 60 
>C 2015-06-01 08:14:00 0 
>C 2015-06-01 08:16:30 150 

Każda podgrupa w produkcji będzie miał pierwszy rząd z wartością czasową równą 0, ponieważ nie ma poprzedniego wiersza. Udało mi się pogrupować według kategorii i wywołać następującą funkcję, aby obliczyć różnice, ale nie udało mi się zebrać końcowego wyniku dla wszystkich grup kategorii.

getTimeDiff <- function(x) { 
    no_rows <- nrow(x) 
    if(no_rows > 1) { 
    for(i in 2:no_rows) { 
     t <- x[i, "randtime"] - x[i-1, "randtime"] 
    } 
    } 
} 

Jestem w tym od dwóch dni bez powodzenia, więc bardzo doceniam każdą pomoc. Dzięki.

Odpowiedz

10

Spróbuj tego:

library(dplyr) 
df %>% 
    arrange(category, randtime) %>% 
    group_by(category) %>% 
    mutate(diff = randtime - lag(randtime), 
     diff_secs = as.numeric(diff, units = 'secs')) 

# category   randtime    diff diff_secs 
#  (fctr)    (time)   (dfft)  (dbl) 
# 1  A 2015-06-01 11:10:54   NA hours   NA 
# 2  A 2015-06-01 15:35:04 4.402785 hours 15850.027 
# 3  A 2015-06-01 17:01:22 1.438395 hours 5178.222 
# 4  B 2015-06-01 08:14:46   NA hours   NA 
# 5  B 2015-06-01 16:53:43 518.955379 hours 1868239.364 
# 6  B 2015-06-01 17:37:48 44.090950 hours 158727.420 

Można również dodać replace(is.na(.), 0) do łańcucha.

+0

dzięki za szybką odpowiedź. Rozwiązuje to to częściowo w ustawianiu wydajności zgodnie z wymaganiami. Jednak lag() zwraca poprzedni wiersz jako wartość czasową, a nie faktyczną różnicę. – Mntester

+0

Naprawiono @Mntester. – JasonAizkalns

9

w bazie R można użyć:

# creating an ordered data.frame 
df <- data.frame(category, randtime) 
df <- df[order(df$category, df$randtime),] 
# calculating the timedifference 
df$tdiff <- unlist(tapply(df$randtime, INDEX = df$category, 
          FUN = function(x) c(0, `units<-`(diff(x), "secs")))) 

co daje:

> df 
    category   randtime  tdiff 
6   A 2015-06-01 11:10:54  0.0000 
15  A 2015-06-01 15:35:04 15850.0271 
18  A 2015-06-01 17:01:22 5178.2223 
1   B 2015-06-01 08:14:46  0.0000 
17  B 2015-06-01 16:53:43 31137.3227 
19  B 2015-06-01 17:37:48 2645.4570 
3   C 2015-06-01 10:09:50  0.0000 
7   C 2015-06-01 12:46:40 9409.9693 
9   C 2015-06-01 13:56:29 4188.4578 
10  C 2015-06-01 14:24:18 1669.1326 
12  C 2015-06-01 14:54:25 1807.1447 
14  C 2015-06-01 15:05:07 641.7068 
2   D 2015-06-01 09:28:16  0.0000 
13  D 2015-06-01 14:55:40 19644.8313 
4   E 2015-06-01 10:18:58  0.0000 
5   E 2015-06-01 10:53:29 2071.2223 
8   E 2015-06-01 13:26:26 9176.6263 
11  E 2015-06-01 14:33:25 4019.0319 
16  E 2015-06-01 15:57:16 5031.4183 
20  E 2015-06-01 17:56:33 7156.8849 

Jeśli chcesz minut lub godzin, można użyć "mins" lub "hours" zamiast "secs".


Alternatywnym z pakietem data.table:

library(data.table) 
# creating an ordered/keyed data.table 
dt <- data.table(category, randtime, key = c("category", "randtime")) 
# calculating the timedifference 
dt[, tdiff := difftime(randtime, shift(randtime, fill=randtime[1L]), units="secs"), by=category] 
# or: 
dt[, tdiff := c(0, `units<-`(diff(randtime), "secs")), by = category] 

co skutkuje:

> dt 
    category   randtime   tdiff 
1:  A 2015-06-01 11:10:54  0.0000 secs 
2:  A 2015-06-01 15:35:04 15850.0271 secs 
3:  A 2015-06-01 17:01:22 5178.2223 secs 
4:  B 2015-06-01 08:14:46  0.0000 secs 
5:  B 2015-06-01 16:53:43 31137.3227 secs 
6:  B 2015-06-01 17:37:48 2645.4570 secs 
7:  C 2015-06-01 10:09:50  0.0000 secs 
8:  C 2015-06-01 12:46:40 9409.9693 secs 
9:  C 2015-06-01 13:56:29 4188.4578 secs 
10:  C 2015-06-01 14:24:18 1669.1326 secs 
11:  C 2015-06-01 14:54:25 1807.1447 secs 
12:  C 2015-06-01 15:05:07 641.7068 secs 
13:  D 2015-06-01 09:28:16  0.0000 secs 
14:  D 2015-06-01 14:55:40 19644.8313 secs 
15:  E 2015-06-01 10:18:58  0.0000 secs 
16:  E 2015-06-01 10:53:29 2071.2223 secs 
17:  E 2015-06-01 13:26:26 9176.6263 secs 
18:  E 2015-06-01 14:33:25 4019.0319 secs 
19:  E 2015-06-01 15:57:16 5031.4183 secs 
20:  E 2015-06-01 17:56:33 7156.8849 secs 
+0

Tak, oba te rozwiązania działają doskonale. Wielkie dzięki, wszyscy. – Mntester

+0

@Mntester rozszerzył odpowiedź bazowym rozwiązaniem R – Jaap

+0

Chciałbym mieć więcej niż jedno rozwiązanie, takie ładne, czyste rozwiązanie 'data.table'! Uwaga: użyj 'first_removed <- dt [dt [, -.I [1], by = category] $ V1]' aby usunąć pierwszy wiersz na kategorię z wynikowego pliku data.table w razie potrzeby (w moim przypadku używam różnica jako cecha, więc nie chcę zer). – Bar

Powiązane problemy