suggestions likes hashtags for r data.table

likes - Suma de los valores más recientes en todos los grupos



hashtags instagram 2018 (3)

Para cada fila de mis datos me gustaría calcular la suma del value más reciente para cada group :

dt = data.table(group = c(''a'',''b'',''a'',''a'',''b'',''a''), value = c(10, 5, 20, 15, 15, 10), desired = c(10, 15, 25, 20, 30, 25)) # group value desired #1: a 10 10 #2: b 5 15 #3: a 20 25 # latest value of a is 20, of b is 5 #4: a 15 20 # latest value of a is 15, of b is 5 #5: b 15 30 #6: a 10 25

desired columna desired es lo que quiero lograr, y puedo hacerlo con un ciclo ingenuo, pero mis datos son bastante grandes, con muchas filas y grupos (1M + filas, más de 1000 grupos).

for (i in seq_len(nrow(dt))) { # can use `set` to make this faster, but still too slow # this is just to illustrate *a* solution dt[i, desired1 := dt[1:i, value[.N], by = group][, sum(V1)]] }


Crearía una columna para cada grupo que muestre el último valor para ese grupo. Luego solo suma esas columnas:

library(zoo) result <- rep(0, nrow(dt)) for(g in dt[, unique(group)]) { result <- result + dt[, na.fill(na.locf(ifelse(group==g, 1, NA)*value, na.rm=F), 0)] } all(dt[, desired] == result)


Incluso lógica más simple de @eddi (bajo comentarios) reduciendo la rotonda que se muestra a continuación:

dt[, incr := diff(c(0, value)), by = group][, ans := cumsum(incr)]

No estoy seguro de cómo se extiende a más grupos, pero aquí hay un ejemplo de datos con 3 grupos:

# I hope I got the desired output correctly require(data.table) dt = data.table(group = c(''a'',''b'',''c'',''a'',''a'',''b'',''c'',''a''), value = c(10, 5, 20, 25, 15, 15, 30, 10), desired = c(10, 15, 35, 50, 40, 50, 60, 55))

Agregue un rleid :

dt[, id := rleid(group)]

Extraiga la última fila para cada group, id :

last = dt[, .(value=value[.N]), by=.(group, id)]

last tendrá id única. Ahora la idea es obtener el incremento para cada id , y luego unirse + actualizar de nuevo.

last = last[, incr := value - shift(value, type="lag", fill=0L), by=group ][, incr := cumsum(incr)-value][]

Únete + actualiza ahora:

dt[last, ans := value + i.incr, on="id"][, id := NULL][] # group value desired ans # 1: a 10 10 10 # 2: b 5 15 15 # 3: c 20 35 35 # 4: a 25 50 50 # 5: a 15 40 40 # 6: b 15 50 50 # 7: c 30 60 60 # 8: a 10 55 55

Todavía no estoy seguro de dónde / si esto se rompe ... lo analizaré cuidadosamente ahora. Lo escribí inmediatamente para que haya más ojos en él.

Comparando en 500 grupos con 10,000 filas con la solución de David:

require(data.table) set.seed(45L) groups = apply(matrix(sample(letters, 500L*10L, TRUE), ncol=10L), 1L, paste, collapse="") uniqueN(groups) # 500L N = 1e4L dt = data.table(group=sample(groups, N, TRUE), value = sample(100L, N, TRUE)) arun <- function(dt) { dt[, id := rleid(group)] last = dt[, .(value=value[.N]), by=.(group, id)] last = last[, incr := value - shift(value, type="lag", fill=0L), by=group ][, incr := cumsum(incr)-value][] dt[last, ans := value + i.incr, on="id"][, id := NULL][] dt$ans } david <- function(dt) { dt[, indx := .I] res <- dcast(dt, indx ~ group) for (j in names(res)[-1L]) set(res, j = j, value = res[!is.na(res[[j]])][res, on = "indx", roll = TRUE][[j]]) rowSums(as.matrix(res)[, -1], na.rm = TRUE) } system.time(ans1 <- arun(dt)) ## 0.024s system.time(ans2 <- david(dt)) ## 38.97s identical(ans1, as.integer(ans2)) # [1] TRUE


utilizando dplyr, funciona para muchos grupos, pero los datos no deben ser tablas de datos.

library(dplyr) library(tidyr) library(zoo) dt %>% mutate(row_number = row_number()) %>% spread(group, value) %>% arrange(row_number) %>% mutate_each(funs(na.locf(., na.rm = FALSE))) %>% mutate(answer = rowSums(.[,-1:-2], na.rm = T))

Usando la función anterior en datos de ejemplo (aviso data.frame() not data.table() :

dt = data.frame(group = c(''a'',''b'',''a'',''a'',''b'',''a''), value = c(10, 5, 20, 15, 15, 10), desired = c(10, 15, 25, 20, 30, 25)) desired row_number a b answer 1 10 1 10 NA 10 2 15 2 10 5 15 3 25 3 20 5 25 4 20 4 15 5 20 5 30 5 15 15 30 6 25 6 10 15 25 dt = data.frame(group = c(''a'',''b'',''c'',''a'',''a'',''b'',''c'',''a''), value = c(10, 5, 20, 25, 15, 15, 30, 10), desired = c(10, 15, 35, 50, 40, 50, 60, 55)) desired row_number a b c answer 1 10 1 10 NA NA 10 2 15 2 10 5 NA 15 3 35 3 10 5 20 35 4 50 4 25 5 20 50 5 40 5 15 5 20 40 6 50 6 15 15 20 50 7 60 7 15 15 30 60 8 55 8 10 15 30 55