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