¿Cómo puedo utilizar apply o en una función para crear un nuevo marco de datos que contiene los resultados de la fila de los promedios de cada par de columnas en un gran marco de datos?

Tengo un instrumento que mida n de mediciones repetidas en un gran número de muestras, donde cada medición individual es un vector (todas las medidas están en la misma longitud de los vectores). Me gustaría calcular el promedio (y otras estadísticas) en todas las mediciones repetidas de cada muestra. Esto significa que se necesitan para el grupo de n las columnas consecutivas juntos y hacer fila cálculos.

Para un ejemplo simple, con tres mediciones repetidas en dos muestras, ¿cómo puedo acabar con un marco de datos que tiene dos columnas (una por cada muestra), que es el promedio de cada fila de la replica 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.

He aquí 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))

            a          b            c          d           e          f
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 después de 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

que me hice con este, pero obviamente no es bueno para mi mucho más grande que el marco de datos…

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 puede hacerlo juntos. Mis datos reales tiene algunos cientos de columnas.

Es que siempre cada tres columnas? Está alimentando a un vector de vectores de nombres o vectorial de un vector de índices? Si el usuario user1317221_G la respuesta no es lo que buscas, tal vez usted necesita para dar más info.
Para la posteridad, la pregunta anterior parece ser la transposición de esta más reciente de la pregunta acerca de la aplicación de una función a los grupos de filas (y tiene diferentes enfoques): stackoverflow.com/q/10837258/1036500

OriginalEl autor Ben | 2012-05-19

6 Comentarios

  1. 14

    Esto puede ser más generalizable a su situación en la que usted pase una lista de índices. Si la velocidad es un problema (gran marco de datos) me gustaría optar por lapply con do.call en lugar de sapply:

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

    Funciona si sólo tienes col nombres:

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

    EDITAR

    Sólo ocurrió pensar que tal vez usted desee automatizar este para hacer cada tres columnas. Yo sé que hay un mejor camino, pero aquí es en un 100 columna del conjunto de datos:

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

    EDITAR 2
    Todavía no está contento con la indexación. Creo que hay una mejor y más rápida manera de pasar los índices. he aquí un segundo a pesar de no satisfacer método:

    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])))
    Esto deja fuera la última columna porque no tiene tres columnas a unir.
    Voy a pedir una mejor manera de crear los índices y vincular de nuevo aquí.
    Aquí hay un enlace a esa pregunta por el futuro de los buscadores ENLACE
    Algún otro método para los índices: split(1:n,rep(1:n,cada uno=3,longitud=n)). Aquí n es el número de columnas.
    puedes publicar la respuesta para el enlace de arriba 9though usted tendrá que quitar el último índice de la lista, ya que no es de longitud 3.

    OriginalEl autor Tyler Rinker

  2. 7

    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 de obtener

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

    si sólo se conocen los nombres de las columnas y no a la orden, a continuación, puede utilizar:

    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
    ¿Y qué acerca de una trama de datos con cientos de columnas? ¿Cómo se puede generalizar esto?
    tienes razón, yo era demasiado apresurada en la preparación de mi pregunta, lo siento por la ambigüedad. Tyler Rinker edición tiene el código que hace lo que yo busco.

    OriginalEl autor user1317221_G

  3. 7

    Una pregunta similar se le preguntó por aquí @david: promediando cada 16 columnas en r (ahora cerrado), el cual me respondió mediante la adaptación de @TylerRinker la respuesta anterior, siguiendo una sugerencia de @joran y @Ben. Porque el resultado de la función podría ser de ayuda para la operación o el futuro de los lectores, yo soy la copia de la función aquí, junto con un ejemplo de OP de datos.

    # 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], ...))
                })
    }

    A continuación, para encontrar la media de la replica:

    byapply(dat, 3, rowMeans)

    O, tal vez, la desviación estándar de la replica:

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

    Actualización

    by también puede ser especificado como un vector de grupos:

    byapply(dat, c(1,1,1,2,2,2), rowMeans)
    +1 gracias, esto es muy útil también.

    OriginalEl autor jthetzel

  4. 5

    La rowMeans solución será más rápido, pero para la integridad he aquí cómo usted puede hacer esto con apply:

    t(apply(dat,1,function(x){ c(mean(x[1:3]),mean(x[4:6])) }))
    Cómo acerca de la fila significa para cada serie consecutiva de tres columnas en una trama de datos con varios cientos de columnas?
    Reducirlo a un problema que ya he resuelto: (1) transpose (2) el uso de plyr o datos.la tabla, (3) la transposición de la espalda. (Suponiendo que todo lo que es numérico.)
    Te voy a dar que un tiro y ver si puedo llegar a algo más eficiente que Tyler la solución anterior (raro, pero vale la pena intentarlo!)
    gracias por los consejos, me he encontrado con dos enfoques sobre la base de sus sugerencias (aunque tal vez no exactamente lo que tenía en mente…), ver arriba.

    OriginalEl autor joran

  5. 2

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

    Hacer un marco de datos de datos de ejemplo con p cols para simular un realista conjunto de datos (siguiendo a @TylerRinker la respuesta anterior y a diferencia de mi mal ejemplo en la pregunta)

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

    Cambiar el nombre de las columnas en este marco de datos para crear grupos de n las columnas consecutivas, de modo que si estoy interesado en los grupos de tres columnas puedo obtener los nombres de columna como 1,1,1,2,2,2,3,3,3, etc o si quería 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 que esto es un tipo de indización para la gente como yo que no sabe mucho acerca de la 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 uso se aplican y tapply para obtener fila significa para cada uno de los grupos

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

    Los principales inconvenientes son que los nombres de columna en los datos originales son reemplazados (a pesar de que esto podría ser superado por poner la agrupación de números en una fila nueva en lugar de la colnames) y que los nombres de las columnas devueltas por la aplica-tapply de la función en una ineficiente orden.

    Seguir a @joran la sugerencia, he aquí una data.table solución:

    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 su rápida y pacientes esfuerzos!

    Para añadir un puntero que la lapply(.SD,mean) lenguaje debe llegar mucho más rápido en v1.8.1 gracias a: i) un descubrimiento en la esta pregunta y ii) automático .Interna()ción de mean() (wiki, punto 3, ya no es necesario). También, .SDcols a menudo es útil pero no necesario.
    gracias por tu nota! Es bueno saber acerca de .SDcols, no estaba familiarizado con, y gran saber data.table se pone cada vez más rápido!

    OriginalEl autor Ben

  6. 0

    Hay una forma sencilla solución si usted está interesado en aplicar una función a cada combinación única de las columnas, en lo que se conoce como la combinatoria.

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

    Para el cálculo de estadísticas para cada combinación única de tres columnas, etc., cambiar sólo el 2 a 3. La operación es vectorizados y por lo tanto más rápido que los bucles, tales como la apply de la familia de las funciones anteriores. Si el orden de las columnas asuntos, entonces, en lugar de necesitar una permutación algoritmo diseñado para reproducir los conjuntos ordenados: combinat::permn

    ¿qué quieres decir con «si el orden de los asuntos» y lo que es la planta::permn función? Se puede editar el código por favor?
    Las combinaciones no son la misma cosa como permutaciones: youtube.com/watch?v=s2W6Bce_T30 Si el orden de las entradas de materia, entonces es la permutación que usted busca. En este caso, ‘orden’, en referencia a la orden de las columnas.

    OriginalEl autor Adam Erickson

Dejar respuesta

Please enter your comment!
Please enter your name here