Hice una pregunta similar antes y obtuve una gran ayuda: R: Agregando Historial por ID por fecha

La diferencia fue que para la publicación anterior estaba interesado en agregar TODA la información histórica, pero ahora espero especificar solo 90 días atrás.

Aquí hay un ejemplo de cómo se verían mis datos:

strDates <- c("09/09/16", "5/7/16", "5/6/16", "2/13/16", "2/11/16","1/7/16",
          "11/8/16","6/8/16", "5/8/16","2/13/16","1/3/16", "1/1/16")
Date<-as.Date(strDates, "%m/%d/%y")
ID <- c("A", "A", "A", "A","A", "A", "B","B","B","B","B", "B")
Event <- c(1,0,1,0,1,1, 0,1,1,1,0, 1)
sample_df <- data.frame(Date,ID,Event)

Además de la salida:

enter image description here

Información general

Quiero mantener toda la información adjunta por encuentro, pero luego agregar la siguiente información histórica por identificación a 90 días.

  1. Número de encuentros anteriores en los últimos 90 días
  2. Número de eventos anteriores en los últimos 90 días

Ejemplo

Como ejemplo, veamos la fila 2.

La fila 2 es ID A, por lo que haría referencia a las filas 3-6 (que ocurrió antes del encuentro de la fila 2). Dentro de este grupo de filas, vemos que las filas 3,4,5 y todas ocurrieron en los últimos 90 días, con la fila 6 sucediendo fuera del tiempo de interés.

Número de encuentros anteriores en los últimos 90 días desde la fila 2: 3 encuentros

Número de eventos anteriores en los últimos 90 días desde la fila 2: 2 eventos (5/6/16 y 2/11/16)

Salida deseada

Idealmente, obtendría el siguiente resultado:

enter image description here

4
EntryLevelR 8 ene. 2017 a las 22:56

4 respuestas

La mejor respuesta

Aquí hay una solución alternativa data.table que debería ser muy eficiente. Esto utiliza las nuevas uniones no equi que se introdujeron en v 1.10.0 combinadas con by = .EACHI que le permite hacer cálculos por unión mientras se une

library(data.table) #v1.10.0
setDT(sample_df)[, Date2 := Date - 90] # Set range (Maybe in future this could be avoided)
sample_df[sample_df, # Binary join with itself
          .(Enc90D = .N, Ev90D = sum(Event, na.rm = TRUE)), # Make calculations
          on = .(ID = ID, Date < Date, Date > Date2), # Join by
          by = .EACHI] # Do calculations per each match
#     ID       Date       Date Enc90D Ev90D
#  1:  A 2016-09-09 2016-06-11      0     0
#  2:  A 2016-05-07 2016-02-07      3     2
#  3:  A 2016-05-06 2016-02-06      2     1
#  4:  A 2016-02-13 2015-11-15      2     2
#  5:  A 2016-02-11 2015-11-13      1     1
#  6:  A 2016-01-07 2015-10-09      0     0
#  7:  B 2016-11-08 2016-08-10      0     0
#  8:  B 2016-06-08 2016-03-10      1     1
#  9:  B 2016-05-08 2016-02-08      1     1
# 10:  B 2016-02-13 2015-11-15      2     1
# 11:  B 2016-01-03 2015-10-05      1     1
# 12:  B 2016-01-01 2015-10-03      0     0
9
David Arenburg 8 ene. 2017 a las 21:38

Una solución dplyr bastante larga que usa más filas de las que realmente se requieren. La idea es crear una tabla completamente unida para cada fecha, luego usar funciones de ventana. Esto puede ser útil si se necesitan diferentes cálculos de ventana.

library(dplyr)

dates <- data.frame(Date = seq(from = -90 + min(sample_df$Date), to = max(sample_df$Date), by=1)) 
extended_df <- data.frame(ID = unique(sample_df$ID)) %>%
  merge(dates) %>% 
  left_join(sample_df, by=(c("ID", "Date"))) %>% 
  arrange(ID, desc(Date)) %>%
  mutate(Encounter = as.integer(!is.na(Event)),
         Event = ifelse(is.na(Event), 0, Event)) %>%
  group_by(ID) %>%
  mutate(PrevEnc90D   = rollsum(lead(Encounter), k=90, fill=0, align="left"),
        PrevEvent90D  = rollsum(lead(Event),     k=90, fill=0, align="left")) %>%
  inner_join(sample_df[,c("ID", "Date")]) %>%
  arrange(ID, desc(Date))

extended_df

Fuente: marco de datos local [12 x 6] Grupos: ID [2]

       ID       Date Event Encounter PrevEnc90D PrevEvent90D
   <fctr>     <date> <dbl>     <int>      <dbl>        <dbl>
1       A 2016-09-09     1         1          0            0
2       A 2016-05-07     0         1          3            2
3       A 2016-05-06     1         1          2            1
4       A 2016-02-13     0         1          2            2
5       A 2016-02-11     1         1          1            1
6       A 2016-01-07     1         1          0            0
7       B 2016-11-08     0         1          0            0
8       B 2016-06-08     1         1          1            1
9       B 2016-05-08     1         1          1            1
10      B 2016-02-13     1         1          2            1
11      B 2016-01-03     0         1          1            1
12      B 2016-01-01     1         1          0            0
2
Andrew Lavers 8 ene. 2017 a las 23:47

Una solución dplyr parcialmente vectorizada, donde puede combinar la operación do (para recorrer los grupos) y la operación rowwise (para que pueda referir la Fecha como la Fecha en cada fila, y {{X3 }} como toda la columna Date dentro de cada grupo):

sample_df %>% 
    group_by(ID) %>% 
    do(rowwise(.) %>% 
        mutate(PrevEnc90D = sum(Date - .$Date < 90 & Date - .$Date > 0), 
               PrevEvent90D = sum(.$Event[Date - .$Date < 90 & Date - .$Date > 0])))

#Source: local data frame [12 x 5]
#Groups: ID [2]

#         Date     ID Event PrevEnc90D PrevEvent90D
#       <date> <fctr> <dbl>      <int>        <dbl>
#1  2016-09-09      A     1          0            0
#2  2016-05-07      A     0          3            2
#3  2016-05-06      A     1          2            1
#4  2016-02-13      A     0          2            2
#5  2016-02-11      A     1          1            1
#6  2016-01-07      A     1          0            0
#7  2016-11-08      B     0          0            0
#8  2016-06-08      B     1          1            1
#9  2016-05-08      B     1          1            1
#10 2016-02-13      B     1          2            1
#11 2016-01-03      B     0          1            1
#12 2016-01-01      B     1          0            0
2
Psidom 8 ene. 2017 a las 20:29

Y otra idea que intenta evitar sumas repetitivas y operaciones relacionales cuando sea posible:

do.call(rbind, 
        lapply(split(sample_df, sample_df$ID), 
               function(x) {
                   i = nrow(x) - findInterval(x$Date - 90, rev(x$Date))
                   cs = cumsum(x$Event)
                   cbind(x, PrevEnc90D = i - (1:nrow(x)), PrevEvent90D = cs[i] - cs)
               }))
#           Date ID Event PrevEnc90D PrevEvent90D
#A.1  2016-09-09  A     1          0            0
#A.2  2016-05-07  A     0          3            2
#A.3  2016-05-06  A     1          2            1
#A.4  2016-02-13  A     0          2            2
#A.5  2016-02-11  A     1          1            1
#A.6  2016-01-07  A     1          0            0
#B.7  2016-11-08  B     0          0            0
#B.8  2016-06-08  B     1          1            1
#B.9  2016-05-08  B     1          1            1
#B.10 2016-02-13  B     1          2            1
#B.11 2016-01-03  B     0          1            1
#B.12 2016-01-01  B     1          0            0

Lo anterior supone que la "Fecha" se ordena de forma decreciente dentro de cada "ID" (lo cual es bastante sencillo si no es el caso). La idea principal, aquí, es (i) localizar el día 90 anterior para cada fecha, (ii) calcular una suma acumulativa una vez y por adelantado y, (iii) restar los respectivos índices / cumsum s para obtener la salida . Utilicé la ruta split / lapply, aquí, para agrupar por "ID", pero supongo que es fácilmente transferible a cualquier herramienta preferible.

1
alexis_laz 9 ene. 2017 a las 12:39