varias tres superponer sumas sumar sumandos studio resta lineas graficos graficas funcion comprobacion r performance sum data.table window-functions

tres - superponer graficas en r



Suma dentro de una ventana que se define en la columna (2)

Primero data.table el paquete base y convertimos nuestro data.table a data.frame

set.seed(100) ids <- 1:100 x <- floor(runif(100, 1, 100)) groups <- floor(runif(100, 1, 10)) * 10 window <- floor(runif(100, 1, 5)) library(''data.table'') data <- data.table(ids, x, groups, window) setkey(data, groups, ids) dd <- as.data.frame(data)

Y, básicamente, unir las filas en un marco de datos más grande que podemos usar para resumir usando su método de agregación favorito

tmp <- tapply(seq(nrow(dd)), dd$groups, function(ii) { idx <- Map(`:`, ii, ii + dd$window[ii]) out <- dd[unlist(idx), ] out$idx <- rep(dd$ids[ii], lengths(idx)) out[out$groups %in% dd$groups[ii], ] }) tmp <- do.call(''rbind'', tmp) res <- aggregate(x ~ idx + groups, tmp, sum) # idx groups x # 1 3 10 222 # 2 9 10 83 # 3 13 10 95 # 4 16 10 201 # 5 26 10 197 # 6 30 10 180 # 7 36 10 238 # 8 38 10 258 # 9 42 10 255 # 10 48 10 109 # 11 49 10 81 # 12 59 10 176 # 13 65 10 116 # 14 67 10 71 # 15 88 10 25 # 16 19 20 173 identical(table(dd$groups), table(res$group)) # [1] TRUE

Quiero implementar la sum(x) de N filas siguientes para cada fila data.table dentro de un grupo, donde N es el valor de la columna de la window .

Código para generar datos de muestra:

set.seed(100) ids <- 1:100 x <- floor(runif(100, 1, 100)) groups <- floor(runif(100, 1, 10)) * 10 window <- floor(runif(100, 1, 5)) library(''data.table'') data <- data.table(ids, x, groups, window) setkey(data, groups, ids)

Filas superiores:

ids x groups window 1: 3 55 10 4 2: 9 55 10 1 3: 13 28 10 1 4: 16 67 10 3 5: 26 17 10 3 6: 30 28 10 2 7: 36 89 10 2 8: 38 63 10 3 9: 42 86 10 3 10: 48 88 10 1 11: 49 21 10 1 12: 59 60 10 3 13: 65 45 10 4 14: 67 46 10 2 15: 88 25 10 4 16: 19 36 20 2

Por lo tanto, para la primera fila, el valor resultante se calculará en función de la suma de las 4 filas actuales y siguientes: res = 55 + 55 + 28 + 67 + 17 = 222

Para la fila 15 donde termina el grupo, solo necesito el valor de la fila actual: res = 25 + 0 (sin filas) = ​​25.

Este es un pseudo código para esta lógica:

res <- data[, .(ids, groups, x, window , result = sum(.SD[.CurrentRow:(.CurrentRow + Window)], na.rm = T)), by = groups, .SDcols = c("x")]

Espero que esto se pueda implementar a través de data.table . Quiero evitar la implementación de bucle para esto.


No estoy seguro de que sea posible hacerlo sin iterar sobre todas las filas, así que aquí hay una de esas soluciones:

data[, end := pmin(.I + window, .I[.N]), by = groups][ , res := sum(data$x[.I:end]), by = 1:nrow(data)][1:16] # ids x groups window end res # 1: 3 55 10 4 5 222 # 2: 9 55 10 1 3 83 # 3: 13 28 10 1 4 95 # 4: 16 67 10 3 7 201 # 5: 26 17 10 3 8 197 # 6: 30 28 10 2 8 180 # 7: 36 89 10 2 9 238 # 8: 38 63 10 3 11 258 # 9: 42 86 10 3 12 255 #10: 48 88 10 1 11 109 #11: 49 21 10 1 12 81 #12: 59 60 10 3 15 176 #13: 65 45 10 4 15 116 #14: 67 46 10 2 15 71 #15: 88 25 10 4 15 25 #16: 19 36 20 2 18 173

Como señala alexis_laz, puedes hacerlo mejor calculando el cumsum una vez y luego restando la parte extra, evitando así iterar explícitamente sobre las filas:

data[, res := { cs <- cumsum(x); cs[pmin(1:.N + window, .N)] - shift(cs, fill = 0)} , by = groups]

Trataré de explicar lo que sucede aquí:

  • res := {...} agrega una columna a nuestra tabla de datos con el cálculo R dentro de los corchetes;
  • cs = cumsum(x) calcula la suma en ejecución para todas las filas dentro del grupo;
  • cs[pmin(1:.N + window, .N)] toma el valor de la suma en ejecución al final de la ventana o en la última fila del grupo;
  • shift(cs, fill = 0) obtiene la suma corriente de la fila anterior;
  • la diferencia de los dos da la suma de los elementos dentro de la ventana.

Como hay varias respuestas de trabajo a esta pregunta, creo que vale la pena proporcionar una evaluación comparativa aquí:

library(microbenchmark) m <- microbenchmark( "tapply(rawr)" = tapplyWay(dd), "data.table(eddi)" = data[, end := pmin(.I + window, .I[.N]), by = groups][ , res := sum(data$x[.I:end]), by = 1:nrow(data)], "data.table(alexis_laz)" = data[, res := {cs = cumsum(x); cs[pmin(1:.N + window, .N)] - shift(cs, fill = 0)} , by = groups], times = 10) print(m) boxplot(m)

Resultado para 10 ^ 5 filas de muestra:

Unit: milliseconds expr min lq mean median uq max neval tapply(rawr) 2575.12 2761.365 2898.63 2905.77 3041.08 3127.86 10 data.table(eddi) 1418.92 1570.230 1758.70 1656.14 1977.59 2358.85 10 dt(alexis_laz) 6.82 7.73 8.78 8.09 10.37 12.37119 10