una - Realizar dplyr mutate en el subconjunto de columnas
función filter en r (4)
¿Me estoy perdiendo algo o funcionaría como se esperaba?
cols <- paste0("X", c(2,4))
dd %>% mutate(evensum = rowSums(.[cols]), evenmean = rowMeans(.[cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
¿O está buscando específicamente una función personalizada para hacer esto?
No es exactamente lo que está buscando, pero si desea hacerlo dentro de una tubería, puede usar
select
explícitamente dentro de
mutate
esta manera:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% rowSums)
# id X1 X2 X3 X4 X5 xy
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535
Sin embargo, es un poco más complicado si desea aplicar varias funciones. Podrías usar una función auxiliar en la línea de (... no probado exhaustivamente ...):
f <- function(x, ...) {
n <- nrow(x)
x <- lapply(list(...), function(y) if (length(y) == 1L) rep(y, n) else y)
matrix(unlist(x), nrow = n, byrow = FALSE)
}
Y luego aplicarlo así:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% f(., rowSums(.), max(.)))
# id X1 X2 X3 X4 X5 xy.1 xy.2
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.9888592
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.9888592
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.9888592
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.9888592
Tengo un data.frame como este (el conjunto de datos real tiene muchas más filas y columnas)
set.seed(15)
dd <- data.frame(id=letters[1:4], matrix(runif(5*4), nrow=4))
# id X1 X2 X3 X4 X5
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125
Me gustaría poder escribir una declaración dplyr donde pueda seleccionar un subconjunto de columnas y mutarlas. (Estoy tratando de hacer algo similar a usar .SDcols en data.table).
Para un ejemplo simplificado, aquí está la función que me gustaría poder escribir para agregar columnas para las sumas y los medios de las columnas "X" pares mientras se conservan todas las demás columnas. La salida deseada usando la base R es
(cols<-paste0("X", c(2,4)))
# [1] "X2" "X4"
cbind(dd,evensum=rowSums(dd[,cols]),evenmean=rowMeans(dd[,cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
pero quería usar una cadena tipo dplyr para hacer lo mismo.
En el caso general, me gustaría poder usar cualquiera de las funciones auxiliares de
select()
como
starts_with
,
ends_with
,
matches
, etc. y cualquier función.
Esto es lo que probé
library(dplyr)
partial_mutate1 <- function(x, colspec, ...) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute_(.dots=lazyeval::lazy_dots(...)) %>%
cbind(x,.)
}
dd %>% partial_mutate1(num_range("X", c(2,4)),
evensum=rowSums(.), evenmean=rowMeans(.))
Sin embargo, esto arroja un error que dice
Error in rowSums(.) : ''x'' must be numeric
Lo cual parece ser porque
.
parece estar refiriéndose a todo el marco de fecha en lugar del subconjunto seleccionado.
(mismo error que
rowSums(dd)
).
Sin embargo, tenga en cuenta que esto produce la salida deseada
partial_mutate2 <- function(x, colspec) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute(evensum=rowSums(.), evenmean=rowMeans(.)) %>%
cbind(x,.)
}
dd %>% partial_mutate2(seq(2,ncol(dd),2))
¿Supongo que esto es algún tipo de problema ambiental?
Cualquier sugerencia sobre cómo pasar los argumentos a
partial_mutate1
para que el
.
tomará correctamente los valores del conjunto de datos "select () - ed"?
En las versiones más recientes de dplyr, puede usar el nuevo
mutate_at()
función
mutate_at(dd, vars(starts_with("X")), somefunction)
Un enfoque agnóstico de varias columnas usando dplyr:
dd %>%
select(-id) %>%
mutate(evensum = rowSums(.[,1:length(.[1,])%%2==0]),
evenmean = rowMeans(.[,1:length(.[1,])%%2==0])) %>%
cbind(id=dd[,1],.)
id X1 X2 X3 X4 X5 evensum evenmean
1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380812
2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478767
tidyr::nest()
entiende la misma sintaxis de selector que
dplyr::select()
, por lo que un enfoque sería consolidar las columnas de interés en una sola columna de marcos de datos, realizar las operaciones necesarias en esa columna de marcos de datos , y ansioso por recuperar un marco de datos plano:
library( tidyverse )
dd %>% nest( X2, X4, .key="Slice" ) %>%
mutate( evensum = map(Slice, rowSums),
evenmean = map(Slice, rowMeans),
evensd = map(Slice, pmap_dbl, lift_vd(sd)) ) %>%
unnest
# id X1 X3 X5 evensum evenmean evensd X2 X4
# 1 a 0.602 0.687 0.447 0.876 0.438 0.100 0.367 0.509
# 2 b 0.195 0.831 0.965 1.70 0.848 0.200 0.989 0.707
# 3 c 0.966 0.105 0.141 1.68 0.839 0.0333 0.815 0.862
# 4 d 0.651 0.646 0.777 1.10 0.548 0.416 0.254 0.842
Dado que los marcos de datos son básicamente listas, este enfoque es naturalmente adecuado para aplicar funciones arbitrarias (como
sd
arriba) para arbitrar un conjunto de columnas usando la familia de funciones
purrr::pmap()
.
Nota al
purrr::lift_vd
: dado que
sd
funciona en vectores, usamos
purrr::lift_vd
para convertir su interfaz para que sea adecuada para
pmap
:
sd( c(0.367, 0.509) ) # 0.100
lift_vd(sd)( 0.367, .509 ) # 0.100