2013-07-18 2 views
0

Я потратил немало времени, пытаясь исчислить различия даты между конкретными событиями. Например, у меня есть набор данных ниже, где t - событие datetime, e - событие («A» или «R»), а id - идентификатор.Разница даты между конкретными событиями в R

 
t     e id 
2013-06-15 20:59:17 A 086 
2013-06-26 18:02:09 R 086 
2013-06-27 03:17:07 A 086 
2013-06-01 11:34:09 R 115 
2013-06-16 19:59:08 R 522 
2013-06-16 20:05:04 R 522 
2013-06-07 09:31:15 A 524 
2013-06-09 16:24:04 R 524 
2013-06-14 03:38:09 A 524 
2013-06-16 15:49:09 R 524 
2013-06-21 03:54:19 A 524 
2013-06-12 12:34:37 A 638 
2013-06-12 13:15:27 A 638 
2013-06-15 16:12:23 R 638 
2013-06-18 22:05:03 A 638 
2013-06-28 13:30:20 R 638 
2013-06-28 22:20:12 A 638 
2013-06-01 18:34:46 A 836 
2013-06-01 18:44:53 A 836 
2013-06-03 14:35:09 R 836 
2013-06-03 20:25:51 A 836 
2013-06-03 20:27:25 A 836 
2013-06-03 20:32:17 A 836 
2013-06-08 16:22:07 R 836 
2013-06-12 13:12:21 A 836 
2013-06-12 13:15:16 A 836 
2013-06-12 13:18:53 A 836 
2013-06-12 18:59:24 A 836 
2013-06-23 21:14:12 R 836 
2013-06-24 20:16:11 R 836 
2013-06-25 03:34:29 A 836 
2013-06-26 20:33:11 R 836 

Хотела сделать новый набор данных, где для каждого идентификатора, я бы разницу DATETIME (в днях) между событиями «А» и события «R».

Для каждого идентификатора: если нет событий A до E, тогда ничего не вычисляйте. Если есть три события A перед двумя событиями E, тогда вычислите дни diff с первым событием A и первым событием E. Если есть только события A или E, тогда ничего не вычисляйте. Тогда для id = 086, 115, 638 я хотел бы получить эти двойники (id, days) (086, 10.9) (638, 3.2) (638, 9.6). Идентификатор = 115 не появляется или может быть NaN, поскольку он имеет только одно событие R и никаких событий A перед ним.

 
id days 
086 10.9 (Ex. 2013-06-26 18:02:09 - 2013-06-15 20:59:17) 
524 2.3 
524 2.5 
638 3.2 
638 9.6 
836 1.8 
836 4.8 (Ex. 2013-06-08 16:22:07 - 2013-06-03 20:25:51) 
836 11.3 
836 1.7 

Я использую этот код в качестве первого подхода:

агрегат (as.POSIXct (ДФ $ т), список (ДФ $ ID), дифференциал)

Все идеи как это сделать? Заранее спасибо.

dput из dataframe является:

 
> dput(df) 
structure(list(t = c("2013-06-15 20:59:17", "2013-06-26 18:02:09", 
"2013-06-27 03:17:07", "2013-06-01 11:34:09", "2013-06-16 19:59:08", 
"2013-06-16 20:05:04", "2013-06-07 09:31:15", "2013-06-09 16:24:04", 
"2013-06-14 03:38:09", "2013-06-16 15:49:09", "2013-06-21 03:54:19", 
"2013-06-12 12:34:37", "2013-06-12 13:15:27", "2013-06-15 16:12:23", 
"2013-06-18 22:05:03", "2013-06-28 13:30:20", "2013-06-28 22:20:12", 
"2013-06-01 18:34:46", "2013-06-01 18:44:53", "2013-06-03 14:35:09", 
"2013-06-03 20:25:51", "2013-06-03 20:27:25", "2013-06-03 20:32:17", 
"2013-06-08 16:22:07", "2013-06-12 13:12:21", "2013-06-12 13:15:16", 
"2013-06-12 13:18:53", "2013-06-12 18:59:24", "2013-06-23 21:14:12", 
"2013-06-24 20:16:11", "2013-06-25 03:34:29", "2013-06-26 20:33:11" 
), e = c("A", "R", "A", "R", "R", "R", "A", "R", "A", "R", "A", 
"A", "A", "R", "A", "R", "A", "A", "A", "R", "A", "A", "A", "R", 
"A", "A", "A", "A", "R", "R", "A", "R"), id = c("086", "086", 
"086", "115", "522", "522", "524", "524", "524", "524", "524", 
"638", "638", "638", "638", "638", "638", "836", "836", "836", 
"836", "836", "836", "836", "836", "836", "836", "836", "836", 
"836", "836", "836")), .Names = c("t", "e", "id"), row.names = c(855945L, 
1481100L, 1508045L, 16944L, 920490L, 921005L, 349201L, 494172L, 
746450L, 904442L, 1163757L, 653045L, 654357L, 834901L, 1047932L, 
1583218L, 1613753L, 36421L, 37178L, 139968L, 162274L, 162417L, 
162804L, 430725L, 654254L, 654350L, 654453L, 670726L, 1333676L, 
1384583L, 1401293L, 1491782L), class = "data.frame") 
+0

вы можете 'dput' ваши данные, чтобы его легче попасть в R –

+0

@JakeBurkhead: я добавил. благодаря! – MSS

+0

На вашем выходе иногда отображается несколько временных интервалов для заданного идентификатора. Как это соотносится с «первым событием A до первого R»? –

ответ

3

Вот решение в 1 линию с использованием ddply функции из пакета plyr и lubridate пакета для разбора даты.

Код:

library(plyr) 
library(lubridate) 

new_df <- ddply(.data=df, .variables=c('id'), summarize, 
       days=round(ymd_hms(t[match('R',e)])-ymd_hms(t[match('A',e)]),1)) 
new_df 

Выход:

id  days 
1 086 10.9 days 
2 115 NA days 
3 522 NA days 
4 524 2.3 days 
5 638 3.2 days 
6 836 1.8 days 

Обратите внимание, что есть 2 предупреждения, потому что id s 115 и 522 не имеют значения для переменной e.

Если вы хотите разницу дат быть десятичным значение, вы можете использовать функцию as.double, например, так:

В основном, я использую функцию match найти первое вхождение A и R, разбор date с функцией ymd_hms из пакета lubridate, а затем найти разницу двух дат. Я округлю его до 1 десятичного знака, а затем преобразую его в double для отображения.

EDIT

После прочтения OPS комментариев, здесь довольно уродливый способ, чтобы получить желаемый результат. Простите меня, это рано утром, и это может быть не изящно или эффективно, но, похоже, дает желаемый результат.

Код:

grouper <- function(var, group) { 
    num <- 1 
    res <- c(1:length(var)) 
    for(i in 1:length(var)) { 
    res[i] <- num 
    if(var[i]==group) { 
     num <- num+1 
    } 
    } 
    return(res) 
} 

df2 <- df 
df2$group <- ddply(.data=df, .variables='id', summarize, group=grouper(e,'R'))$group 

df3 <- ddply(.data=df2, .variables=c('id','group'), summarize, 
      days=round(ymd_hms(t[match('R',e)])-ymd_hms(t[match('A',e)]),1)) 

df3[complete.cases(df3),-2] 

Выход:

id  days 
1 086 10.9 days 
6 524 2.3 days 
7 524 2.5 days 
9 638 3.2 days 
10 638 9.6 days 
12 836 1.8 days 
13 836 4.8 days 
14 836 11.3 days 
16 836 1.7 days 

Идея заключается в том, чтобы добавить еще один столбец, что группы строк по наступлении события «R», так что я могу подмножество данных установленных как ID, так и событием «R». Это своего рода хаки, и я уверен, что есть более элегантные способы сделать это.

Теперь я ухожу, чтобы выпить кофе.

+0

Спасибо. Он частично работает, потому что id 524 должен появиться 2 раза (2,3 и 2,5 дня), и он появляется 1 раз (2,3 дня). Я имею в виду 2013-06-09 16:24:04 - 2013-06-07 09:31:15 = 2.3 и 2013-06-16 15:49:09 - 2013-06-14 03:38:09 = 2.5 – MSS

+0

Хмм хорошо. Когда я прочитал вопрос, я подумал, что вам нужно только различие между первым событием R и первым событием A для каждого идентификатора. – ialm

+0

@MSV Проверить мои изменения! – ialm

2

Вот один подход

df <- transform(df, t=as.POSIXct(t)) 
sp <- split(df, df$id) 
calc_diff <- function(x) { 
    start <- min(subset(x, e=="A")$t) 
    end <- min(subset(x, e=="R")$t) 
    return(end-start) 
} 
sapply(sp, FUN=calc_diff) 
+0

Спасибо. Здесь происходит то же, что и предыдущий код. Он частично работает, потому что id 524 должен появиться 2 раза (2,3 и 2,5 дня), и он появляется 1 раз (2,3 дня). Я имею в виду 2013-06-09 16:24:04 - 2013-06-07 09:31:15 = 2.3 и 2013-06-16 15:49:09 - 2013-06-14 03:38:09 = 2.5 – MSS

+0

Возможно, вы можете изменить функцию 'calc_diff' в соответствии с вашими потребностями. У меня все еще есть трудности с пониманием ваших потребностей, и я думаю, что он слишком специализирован, чтобы быть полезным для других читателей. –

3

Нет необходимости что-либо, но основные R. Заказать ваш data.frame, выбрать «первые» выступления и, наконец, использовать агрегат похож на то, что вы используете:

df <- df[do.call(order, df), ] 
df <- df[!duplicated(df[, c("id", "e")]), ] 
tdiff <- function(x) { 
    if(length(x) == 2) { 
    rv <- as.numeric(difftime(strptime(x[2], format="%Y-%m-%d %H:%M:%S"), 
           strptime(x[1], format="%Y-%m-%d %H:%M:%S"), 
           units = "days")) 
    } else { 
    rv <- NA 
    } 
    rv 
} 

rv <- aggregate(df$t, by = list(id = df$id), tdiff) 

Просто ради закрытия, поскольку вам это больше не нужно, вот версия, которая работает так, как вы хотите.

df <- df[do.call(order, df), ] 
df_a <- subset(df, e == "A") 
df_a <- df_a[!duplicated(df_a[, c("id", "e")]), ] 
df_r <- subset(df, e == "R") 
df_r[, 'A'] <- df_a[match(df_r$id, df_a$id), 't'] 
df_r[, 'R_A'] <- as.numeric(difftime(strptime(df_r[, 't'], format="%Y-%m-%d %H:%M:%S"), 
          strptime(df_r[, 'A'], format="%Y-%m-%d %H:%M:%S"), 
          units = "days")) 
rv <- df_r[, c('id', 'R_A')] 
rv[!is.na(rv$R_A) & rv$R_A < 0, 'R_A'] <- NA 
rv <- rv[!duplicated(rv), ] 
+0

Ошибка в df $ t: $ оператор недействителен для атомных векторов – MSS

+0

У этого есть ошибка и не работает – MSS

+0

@MSV, извините за ошибку. У меня был доступ к R, чтобы проверить, что я написал. Исправление происходит по мере ввода. –

Смежные вопросы