aplicar una función sobre grupos de columnas

¿Cómo puedo usar apply o una función relacionada para crear un nuevo dataframe que contenga los resultados de los promedios de fila de cada par de columnas en un dataframe muy grande?

Tengo un instrumento que produce n mediciones repetidas en una gran cantidad de muestras, donde cada medida individual es un vector (todas las medidas son los mismos vectores de longitud). Me gustaría calcular el promedio (y otras estadísticas) en todas las mediciones repetidas de cada muestra. Esto significa que necesito agrupar n columnas consecutivas y realizar cálculos en filas.

Para un ejemplo simple, con tres mediciones repetidas en dos muestras, ¿cómo puedo terminar con un dataframe que tiene dos columnas (una por muestra), una que es el promedio de cada fila de las réplicas en dat$a , dat$b y dat$c y uno que es el promedio de cada fila para dat$d , dat$e y dat$f .

Aquí hay algunos datos de ejemplo

 dat <- data.frame( a = rnorm(16), b = rnorm(16), c = rnorm(16), d = rnorm(16), e = rnorm(16), f = rnorm(16)) abcdef 1 -0.9089594 -0.8144765 0.872691548 0.4051094 -0.09705234 -1.5100709 2 0.7993102 0.3243804 0.394560355 0.6646588 0.91033497 2.2504104 3 0.2963102 -0.2911078 -0.243723116 1.0661698 -0.89747522 -0.8455833 4 -0.4311512 -0.5997466 -0.545381175 0.3495578 0.38359390 0.4999425 5 -0.4955802 1.8949285 -0.266580411 1.2773987 -0.79373386 -1.8664651 6 1.0957793 -0.3326867 -1.116623982 -0.8584253 0.83704172 1.8368212 7 -0.2529444 0.5792413 -0.001950741 0.2661068 1.17515099 0.4875377 8 1.2560402 0.1354533 1.440160168 -2.1295397 2.05025701 1.0377283 9 0.8123061 0.4453768 1.598246016 0.7146553 -1.09476532 0.0600665 10 0.1084029 -0.4934862 -0.584671816 -0.8096653 1.54466019 -1.8117459 11 -0.8152812 0.9494620 0.100909570 1.5944528 1.56724269 0.6839954 12 0.3130357 2.6245864 1.750448404 -0.7494403 1.06055267 1.0358267 13 1.1976817 -1.2110708 0.719397607 -0.2690107 0.83364274 -0.6895936 14 -2.1860098 -0.8488031 -0.302743475 -0.7348443 0.34302096 -0.8024803 15 0.2361756 0.6773727 1.279737692 0.8742478 -0.03064782 -0.4874172 16 -1.5634527 -0.8276335 0.753090683 2.0394865 0.79006103 0.5704210 

Estoy buscando algo como esto

  X1 X2 1 -0.28358147 -0.40067128 2 0.50608365 1.27513471 3 -0.07950691 -0.22562957 4 -0.52542633 0.41103139 5 0.37758930 -0.46093340 6 -0.11784382 0.60514586 7 0.10811540 0.64293184 8 0.94388455 0.31948189 9 0.95197629 -0.10668118 10 -0.32325169 -0.35891702 11 0.07836345 1.28189698 12 1.56269017 0.44897971 13 0.23533617 -0.04165384 14 -1.11251880 -0.39810121 15 0.73109533 0.11872758 16 -0.54599850 1.13332286 

lo cual hice con esto, pero obviamente no es bueno para mi dataframe mucho más grande …

 data.frame(cbind( apply(cbind(dat$a, dat$b, dat$c), 1, mean), apply(cbind(dat$d, dat$e, dat$f), 1, mean) )) 

He intentado apply y bucles y no puedo lograrlo. Mis datos reales tienen cientos de columnas.

Esto puede ser más generalizable a su situación en que pasa una lista de índices. Si la velocidad es un problema (dataframe grande) optaría por lapply con do.call lugar de sapply :

 x <- list(1:3, 4:6) do.call(cbind, lapply(x, function(i) rowMeans(dat[, i]))) 

Funciona si solo tienes nombres de colmas también:

 x <- list(c('a','b','c'), c('d', 'e', 'f')) do.call(cbind, lapply(x, function(i) rowMeans(dat[, i]))) 

EDITAR

Acabo de pensar que tal vez quieras automatizar esto para hacer cada tres columnas. Sé que hay una mejor manera, pero aquí está en un conjunto de datos de 100 columnas:

 dat <- data.frame(matrix(rnorm(16*100), ncol=100)) n <- 1:ncol(dat) ind <- matrix(c(n, rep(NA, 3 - ncol(dat)%%3)), byrow=TRUE, ncol=3) ind <- data.frame(t(na.omit(ind))) do.call(cbind, lapply(ind, function(i) rowMeans(dat[, i]))) 

EDIT 2 Todavía no está contento con la indexación. Creo que hay una manera mejor / más rápida de pasar los índices. aquí hay un segundo método que no es satisfactorio:

 n <- 1:ncol(dat) ind <- data.frame(matrix(c(n, rep(NA, 3 - ncol(dat)%%3)), byrow=F, nrow=3)) nonna <- sapply(ind, function(x) all(!is.na(x))) ind <- ind[, nonna] do.call(cbind, lapply(ind, function(i)rowMeans(dat[, i]))) 

significa para las filas de los vectores a, b, c

  rowMeans(dat[1:3]) 

significa para las filas de los vectores d, e, f

  rowMeans(dat[4:6]) 

todo en una llamada obtienes

 results<-cbind(rowMeans(dat[1:3]),rowMeans(dat[4:6])) 

si solo conoce los nombres de las columnas y no el orden, puede usar:

 rowMeans(cbind(dat["a"],dat["b"],dat["c"])) rowMeans(cbind(dat["d"],dat["e"],dat["f"])) #I dont know how much damage this does to speed but should still be quick 

Una pregunta similar fue hecha aquí por @david: promediando cada 16 columnas en r (ahora cerrado), que respondí adaptando la respuesta de @ TylerRinker anterior, siguiendo una sugerencia de @joran y @Ben. Debido a que la función resultante podría ser útil para OP o futuros lectores, estoy copiando esa función aquí, junto con un ejemplo para los datos de OP.

 # Function to apply 'fun' to object 'x' over every 'by' columns # Alternatively, 'by' may be a vector of groups byapply <- function(x, by, fun, ...) { # Create index list if (length(by) == 1) { nc <- ncol(x) split.index <- rep(1:ceiling(nc / by), each = by, length.out = nc) } else # 'by' is a vector of groups { nc <- length(by) split.index <- by } index.list <- split(seq(from = 1, to = nc), split.index) # Pass index list to fun using sapply() and return object sapply(index.list, function(i) { do.call(fun, list(x[, i], ...)) }) } 

Luego, para encontrar la media de las repeticiones:

 byapply(dat, 3, rowMeans) 

O, quizás la desviación estándar de las repeticiones:

 byapply(dat, 3, apply, 1, sd) 

Actualizar

by también se puede especificar como un vector de grupos:

 byapply(dat, c(1,1,1,2,2,2), rowMeans) 

La solución de rowMeans será más rápida, pero para completarla, puede hacer esto con apply :

 t(apply(dat,1,function(x){ c(mean(x[1:3]),mean(x[4:6])) })) 

Inspirado por la sugerencia de @ joran, se me ocurrió esto (en realidad un poco diferente de lo que él sugirió, aunque la sugerencia de transposición fue especialmente útil):

Haga un dataframe de datos de ejemplo con p cols para simular un conjunto de datos realista (siguiendo la respuesta de @ TylerRinker anterior y, a diferencia de mi pobre ejemplo en la pregunta)

 p <- 99 # how many columns? dat <- data.frame(matrix(rnorm(4*p), ncol = p)) 

Cambie el nombre de las columnas en este dataframe para crear grupos de n columnas consecutivas, de modo que si estoy interesado en los grupos de tres columnas obtengo nombres de columna como 1,1,1,2,2,2,3,3,3 , etc. o si quisiera grupos de cuatro columnas sería 1,1,1,1,2,2,2,2,3,3,3,3, etc. Voy con tres por ahora (supongo este es un tipo de indexación para personas como yo que no saben mucho sobre indexación)

 n <- 3 # how many consecutive columns in the groups of interest? names(dat) <- rep(seq(1:(ncol(dat)/n)), each = n, len = (ncol(dat))) 

Ahora use apply y tapply para obtener los medios de fila para cada uno de los grupos

 dat.avs <- data.frame(t(apply(dat, 1, tapply, names(dat), mean))) 

Las principales desventajas son que los nombres de las columnas en los datos originales son reemplazados (aunque esto podría superarse colocando los números de agrupación en una nueva fila en lugar de los colnames) y que los nombres de las columnas son devueltos por la función apply-tapply de una manera poco útil orden.

Además de la sugerencia de @ joran, aquí hay una solución data.table :

 p <- 99 # how many columns? dat <- data.frame(matrix(rnorm(4*p), ncol = p)) dat.t <- data.frame(t(dat)) n <- 3 # how many consecutive columns in the groups of interest? dat.t$groups <- as.character(rep(seq(1:(ncol(dat)/n)), each = n, len = (ncol(dat)))) library(data.table) DT <- data.table(dat.t) setkey(DT, groups) dat.av <- DT[, lapply(.SD,mean), by=groups] 

¡Gracias a todos por sus esfuerzos rápidos y pacientes!

Existe una solución maravillosamente simple si está interesado en aplicar una función a cada combinación única de columnas, en lo que se conoce como combinatoria.

 combinations <- combn(colnames(df),2,function(x) rowMeans(df[x])) 

Para calcular las estadísticas de cada combinación única de tres columnas, etc., simplemente cambie el 2 a 3. La operación se vectoriza y, por lo tanto, es más rápida que los bucles, como las funciones de familia de apply utilizadas anteriormente. Si el orden de las columnas es importante, entonces necesita un algoritmo de permutación diseñado para reproducir conjuntos ordenados: combinat::permn

Intereting Posts