Tengo datos como este:

library(data.table)
id <- c("1232","1232","1232","4211","4211","4211")
conversion <- c(0,0,0,1,1,1)
DT <- data.table(id, conversion)

id   date         conversion
1232 2018-01-01   0
1232 2018-01-03   0
1232 2018-01-04   0
4211 2018-04-01   1
4211 2018-04-04   1
4211 2018-04-06   1

Me gustaría crear un valor binario para solo la última fila de cada grupo en función de la fila de identificación. El binario sería 1 solo cuando la conversión sea 1 para el grupo.

id   date         conversion  lastconv
1232 2018-01-01   0           0
1232 2018-01-03   0           0 
1232 2018-01-04   0           0
4211 2018-04-01   1           0
4211 2018-04-04   1           0
4211 2018-04-06   1           1

He intentado usar algunos ejemplos con el parámetro "mult" en data.table, pero solo he devuelto errores.

DT[unique(id), lastconv := 1, mult = "last"]
8
ericbrownaustin 11 may. 2019 a las 01:03

5 respuestas

La mejor respuesta

Tiempos de referencia:

library(data.table)
#data.table 1.12.3 IN DEVELOPMENT built 2019-05-12 17:04:48 UTC; root using 4 threads (see ?getDTthreads).  Latest news: r-datatable.com
set.seed(0L)
nid <- 3e6L
DT <- data.table(id=rep(1L:nid, each=3L))[,
    conversion := sample(c(0L,1L), 1L, replace=TRUE), by=.(id)]
DT0 <- copy(DT)
DT1 <- copy(DT)
DT2 <- copy(DT)
DT3 <- copy(DT)

mtd0 <- function() {
    DT0[DT0[, .I[.N], by=id]$V1, lastconv := conversion]
    DT0[is.na(lastconv), lastconv := 0L]
}

mtd1 <- function() {
    DT1[DT1[, .I[.N], by=id]$V1, lastconv := conversion]
    setnafill(DT1, cols = "lastconv", fill = 0L)
}

mtd2 <- function() {
    DT2[, v := 0]
    DT2[.(DT2[conversion == 1, unique(id)]), on=.(id), mult="last", v := 1]

    #or also
    #DT2[, v := 0L][
    #    DT2[,.(cv=last(conversion)), id], on=.(id), mult="last", v := cv]
}

mtd3 <- function() {
    DT3[ , lastconv := as.integer(.I == .I[.N] & conversion == 1), by = id]
}

library(microbenchmark)
microbenchmark(mtd0(), mtd1(), mtd2(), mtd3(), times=1L)

Tiempos:

Unit: milliseconds
   expr       min        lq      mean    median        uq       max neval cld
 mtd0() 1363.1783 1416.1867 1468.9256 1469.1952 1521.7992 1574.4033     3  b 
 mtd1() 1349.5333 1365.4653 1378.9350 1381.3974 1393.6358 1405.8743     3  b 
 mtd2()  511.5615  515.4728  552.9133  519.3841  573.5892  627.7944     3 a  
 mtd3() 3966.8867 4009.1128 4048.9607 4051.3389 4089.9977 4128.6564     3   c
4
2 revs 13 may. 2019 a las 02:32

Modificación del código del OP para unirse en la última fila de cada grupo:

DT[, v := 0]
DT[.(DT[conversion == 1, unique(id)]), on=.(id), mult="last", v := 1]

     id conversion v
1: 1232          0 0
2: 1232          0 0
3: 1232          0 0
4: 4211          1 0
5: 4211          1 0
6: 4211          1 1

Esto solo es diferente en el sentido de que selecciona qué id s editar según la condición deseada.

5
Frank 11 may. 2019 a las 11:25

Filtre para la última fila por grupo y establezca lastconv igual a conversion.

DT[DT[, .I[.N], by=id]$V1, lastconv := conversion]

Luego reemplace NA s con 0

DT[is.na(lastconv), lastconv := 0L]

Resultado

DT
#     id conversion lastconv
#1: 1232          0        0
#2: 1232          0        0
#3: 1232          0        0
#4: 4211          1        0
#5: 4211          1        0
#6: 4211          1        1

Si data.table v1.12.3 está instalado, también podríamos usar la nueva función setnafill para reemplazar NA s en el segundo paso

DT[DT[, .I[.N], by=id]$V1, lastconv := conversion]
setnafill(DT, cols = "lastconv", fill = 0L)
4
markus 11 may. 2019 a las 10:15

¿Has probado algo como lo siguiente?

library(tidyverse)

final_conversion_dat <- DT %>% 
  group_by(id) %>% 
  mutate(date = as.Date(date),
         final_conversion = ifelse(date == max(date, na.rm = T) & conversion == 1, 1, 0))
3
Felix T. 10 may. 2019 a las 22:19

Para cada ID, verifique si el número de fila es el último número de fila del grupo y si 'conversión' es 1. Convierta el resultado lógico en entero.

DT[ , lastconv := as.integer(.I == .I[.N] & conversion == 1), by = id]
5
Henrik 12 may. 2019 a las 11:32