sistema - Aplicar la función en un subconjunto de columnas(.SDcols) mientras se aplica una función diferente en otra columna(dentro de los grupos)
lección de swirl 8 parámetros en el sistema de gráficos (2)
Esto es muy similar a una pregunta que aplica una función común a varias columnas de una data.table
datos .SDcols
Unidades .SDcols
contestó detalladamente aquí .
La diferencia es que me gustaría aplicar simultáneamente una función diferente en otra columna que no es parte del subconjunto .SD
. Publico un ejemplo simple a continuación para mostrar mi intento de resolver el problema:
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
v1 = rnorm(100),
v2 = rnorm(100),
v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]
Da el siguiente error:
Error in `[.data.table`(dt, , list(v1 = sum(v1), lapply(.SD, mean)), by = grp,
: object ''v1'' not found
Ahora esto tiene sentido porque la columna v1
no está incluida en el subconjunto de columnas que debe evaluarse primero. Así que exploré más a fondo incluyéndolo en mi subconjunto de columnas:
sd.cols = c("v1","v2", "v3")
dt.out = dt[, list(sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]
Ahora esto no causa un error, pero proporciona una respuesta que contiene 9 filas (para 3 grupos), con la suma repetida tres veces en la columna V1
y los medios para las 3 columnas (como se esperaba pero no se desea) colocadas en V2
como se muestra a continuación:
> dt.out
grp V1 V2
1: c -1.070608 -0.0486639841313638
2: c -1.070608 -0.178154270921521
3: c -1.070608 -0.137625003604012
4: b -2.782252 -0.0794929150464099
5: b -2.782252 -0.149529237116445
6: b -2.782252 0.199925178109264
7: a 6.091355 0.141659419355985
8: a 6.091355 -0.0272192037753071
9: a 6.091355 0.00815760216214876
Solución de solución utilizando 2 pasos
Claramente, es posible resolver el problema en varios pasos al calcular la mean
por grupo para el subconjunto de columnas y unirlo a la sum
por grupo para la columna única de la siguiente manera:
dt.out1 = dt[, sum(v1), by = grp]
dt.out2 = dt[, lapply(.SD,mean), by = grp, .SDcols = sd.cols]
dt.out = merge(dt.out1, dt.out2, by = "grp")
> dt.out
grp V1 v2 v3
1: a 6.091355 -0.0272192 0.008157602
2: b -2.782252 -0.1495292 0.199925178
3: c -1.070608 -0.1781543 -0.137625004
Estoy seguro de que me falta algo, gracias de antemano por cualquier orientación.
Prueba esto:
dt[,list(sum(v1), mean(v2), mean(v3)), by=grp]
En data.table
, usar list()
en el segundo argumento le permite describir un conjunto de columnas que dan como resultado la data.table
final.
Para lo que vale, .SD
puede ser bastante lento [^ 1], por lo que es posible que desee evitarlo a menos que realmente necesite todos los datos suministrados en la tabla de datos data.table
como lo data.table
para una función más sofisticada.
Otra opción, si tiene muchas columnas para .SDcols
sería hacer la combinación en una línea usando la sintaxis de combinación data.table
.
Por ejemplo:
dt[, sum(v1), by=grp][dt[,lapply(.SD,mean), by=grp, .SDcols=sd.cols]]
Para usar la merge
de data.table
, primero debe usar setkey()
en su data.table
para que sepa cómo hacer coincidir las cosas.
Así que realmente, primero necesitas:
setkey(dt, grp)
Luego puedes usar la línea de arriba para producir un resultado equivalente.
[^ 1]: Encuentro que esto es especialmente cierto a medida que su número de grupos se acerca al número de filas totales. Por ejemplo, esto puede suceder cuando su clave es una identificación individual y muchas personas solo tienen una o dos observaciones.
Actualización: el problema #495 se resolvió ahora con este reciente compromiso , ahora podemos hacerlo bien:
require(data.table) # v1.9.7+
set.seed(1L)
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
v1 = rnorm(100),
v2 = rnorm(100),
v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]
Sin embargo, tenga en cuenta que en este caso, v2
se devolvería como una lista. Eso es porque estás haciendo la list(val, list())
efectiva. Lo que pretendes hacer tal vez sea:
dt[, c(list(v1=sum(v1)), lapply(.SD, mean)), by=grp, .SDcols = sd.cols]
# grp v1 v2 v3
# 1: a -6.440273 0.16993940 0.2173324
# 2: b 4.304350 -0.02553813 0.3381612
# 3: c 0.377974 -0.03828672 -0.2489067
Ver historia para mayor respuesta.