Executando dplyr mutate no subconjunto de colunas

Eu tenho um data.frame como este (o dataset real tem muito mais linhas e colunas)

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 

Eu gostaria de poder escrever uma instrução dplyr onde eu possa selecionar um subconjunto de colunas e alterá-las. (Estou tentando fazer algo semelhante ao uso de .SDcols em data.table).

Para um exemplo simplificado, aqui está a function que eu gostaria de poder escrever para adicionar colunas para as sums e médias das colunas “X” pares, preservando todas as outras colunas. A saída desejada usando a base R é

 (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 

mas eu queria usar uma corrente parecida com dplyr para fazer a mesma coisa. No caso geral, gostaria de poder usar qualquer uma das funções auxiliares select() como starts_with , ends_with , matches , etc e qualquer function. Aqui está o que eu tentei

 library(dplyr) partial_mutate1 % transmute_(.dots=lazyeval::lazy_dots(...)) %>% cbind(x,.) } dd %>% partial_mutate1(num_range("X", c(2,4)), evensum=rowSums(.), evenmean=rowMeans(.)) 

No entanto, isso gera um erro que diz

 Error in rowSums(.) : 'x' must be numeric 

Qual parece ser porque . parece estar se referindo ao date.frame inteiro em vez do subconjunto selecionado. (mesmo erro como rowSums(dd) ). No entanto, observe que isso produz a saída desejada

 partial_mutate2 % transmute(evensum=rowSums(.), evenmean=rowMeans(.)) %>% cbind(x,.) } dd %>% partial_mutate2(seq(2,ncol(dd),2)) 

Eu estou supondo que isso é algum tipo de problema de ambiente? Qualquer sugestão sobre como passar os argumentos para partial_mutate1 para que o . irá obter corretamente os valores do dataset “select () – ed”?

Estou faltando alguma coisa ou isso funcionaria como esperado:

 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 

Ou você está especificamente procurando por uma function personalizada para fazer isso?


Não é exatamente o que você está procurando, mas se você quiser fazê-lo dentro de um pipe você pode usar select explicitamente dentro de mutate assim:

 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 

No entanto, é um pouco mais complicado se você quiser aplicar várias funções. Você poderia usar uma function auxiliar ao longo das linhas de (..não totalmente testado ..):

 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) } 

E então aplique-o assim:

 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 

Uma abordagem agnóstica de número de colunas 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