superponer - ¿Cómo resumo solo una parte de una tabla?
superponer graficas en r (3)
Tengo dos casos de uso relacionados en los que necesito resumir solo partes de una tabla, especificadas de una manera similar a la del filter
.
En pocas palabras, quiero algo como esto:
iris %>%
use_only(Species == ''setosa'') %>%
summarise_each(funs(sum), -Species) %>%
mutate(Species = ''setosa_sum'') %>%
use_all()
Para ceder esto:
Source: local data frame [101 x 5]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 250.3 171.4 73.1 12.3 setosa_sum
2 7.0 3.2 4.7 1.4 versicolor
3 6.4 3.2 4.5 1.5 versicolor
4 6.9 3.1 4.9 1.5 versicolor
5 5.5 2.3 4.0 1.3 versicolor
…
Entonces, en lugar de agrupar por el valor de una columna, uso un criterio de filtrado para operar en una vista de la tabla, sin perder realmente el resto de la tabla (a diferencia del filtro).
¿Cómo implemento inteligentemente use_only
/ use_all
? Aún mejor, ¿esta funcionalidad ya está contenida en dplyr
y cómo la uso?
Por supuesto, es bastante fácil generar el resultado anterior, pero necesito hacer algo similar para muchos casos diferentes, con criterios complejos y variables para el filtrado.
Creo que su enfoque de buscar una función para satisfacer esa sintaxis en particular es demasiado restrictivo. Esto es lo que haría con data.table
(no estoy seguro si dplyr
permite filas variables como esta todavía, sé que ha sido un FR por un tiempo):
library(data.table)
dt = as.data.table(iris)
dt[, if (Species == ''setosa'') lapply(.SD, sum) else .SD, by = Species]
# Species Sepal.Length Sepal.Width Petal.Length Petal.Width
# 1: setosa 250.3 171.4 73.1 12.3
# 2: versicolor 7.0 3.2 4.7 1.4
# 3: versicolor 6.4 3.2 4.5 1.5
# 4: versicolor 6.9 3.1 4.9 1.5
# 5: versicolor 5.5 2.3 4.0 1.3
# ---
También puede agregar [Species == ''setosa'', Species := ''setosa_sum'']
al final para modificar el nombre en el lugar. Debe ser sencillo extenderse a múltiples criterios / cualquier función.
Implementé esto con el enfoque de tener use_only
guardar el resto de la tabla en una opción global dplyr_use_only_rest
, y tener use_all
nuevo.
use_only <- function(.data, ...) {
if (!is.null(.data$.index)) {
stop("data cannot already have .index column, would be overwritten")
}
filt <- .data %>%
mutate(.index = row_number()) %>%
filter(...)
rest <- .data %>% slice(-filt$.index)
options(dplyr_use_only_rest = rest)
select(filt, -.index)
}
use_all <- function(.data, ...) {
rest <- getOption("dplyr_use_only_rest")
if (is.null(rest)) {
stop("called use_all() without earlier use_only()")
}
options(dplyr_use_only_rest = NULL)
bind_rows(.data, rest)
}
Reconozco que la configuración de opciones globales es inferior al diseño ideal para la programación funcional, pero no creo que haya otra manera de garantizar que el resto del marco de datos pase a través de las funciones intermedias que no se hayan tocado. Agregar un atributo adicional al objeto no sobreviviría a funciones como do
o summarize
.
En este punto,
iris %>%
use_only(Species == ''setosa'') %>%
summarise_each(funs(sum), -Species) %>%
mutate(Species = ''setosa_sum'') %>%
use_all()
devuelve, como se desee:
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 250.3 171.4 73.1 12.3 setosa_sum
2 7.0 3.2 4.7 1.4 versicolor
3 6.4 3.2 4.5 1.5 versicolor
4 6.9 3.1 4.9 1.5 versicolor
5 5.5 2.3 4.0 1.3 versicolor
...
Cualquier paso intermedio se podría usar en lugar de summarize_each
and mutate
( do
, filter
, etc.) y solo sucedería en las filas especificadas. Incluso podría agregar o eliminar columnas (el resto se completaría con NA
s).
Puede crear una nueva columna para agrupar por:
iris %>%
mutate( group1 = ifelse(Species == "setosa", "", row_number())) %>%
group_by( group1, Species ) %>%
summarise_each(funs(sum), -Species, -group1) %>%
ungroup() %>%
select(-group1)
Actualización - como solución más general
library(lazyeval)
use_only_ <- function(x, condition, ...) {
condition <- as.lazy(condition, parent.frame())
mutate_(x, .group = condition) %>%
group_by_(".group", ...)
}
use_only <- function(x, condition, ...) {
use_only_(x, lazy(condition), ...)
}
use_all <- function(x) {
ungroup(x) %>%
select(- .group)
}
Utilice use_only
con cualquier condición en el contexto del marco de datos y el entorno de llamada. En este caso:
iris %>%
use_only( ifelse(Species == "setosa", "", row_number()), "Species") %>%
summarise_each(funs(sum), -Species, -.group) %>%
use_all()
El use_only_
se puede usar con una fórmula o cadena. Por ejemplo:
condition <- ~ifelse(Species == "setosa", "", row_number())
o
condition <- "ifelse(Species == ''setosa'' , "", row_number())"
Y llama:
iris %>%
use_only_(condition, "Species") %>%
summarise_each(funs(sum), -Species, -.group) %>%
use_all()
Cuando se mute entre las llamadas use_only
y use_all
, debe tener cuidado de cambiar solo los valores dentro del grupo marcado.