Применить функцию по группам столбцов

Как я могу использовать apply или связанную функцию для создания нового фрейма данных, который содержит результаты средних значений строк каждой пары столбцов в очень большом кадре данных?

У меня есть инструмент, который выводит n реплицирует измерения на большое количество образцов, где каждое отдельное измерение является вектором (все измерения являются одинаковыми векторами длины). Я бы хотел рассчитать среднюю (и другую статистику) по всем репликационным измерениям каждого образца. Это означает, что мне нужно группировать столбцы n последовательных столбцов и выполнять вычисления по строке.

Для простого примера, с тремя репликационными измерениями на двух образцах, как я могу получить кадр данных, который имеет два столбца (по одному на образец), который является средним значением каждой строки реплик в dat$a, dat$b и dat$c и значение, среднее из каждой строки для dat$d, dat$e и dat$f.

Вот некоторые примеры данных

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

Я за чем-то вроде этого

            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

который я сделал с этим, но, очевидно, не годится для моего гораздо большего кадра данных...

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

Я пробовал apply и петли и не могу получить его вместе. Мои фактические данные содержат несколько сотен столбцов.

Ответы

Ответ 1

Это может быть более общим для вашей ситуации, поскольку вы передаете список индексов. Если скорость является проблемой (большой фрейм данных), я бы выбрал lapply с do.call, а не sapply:

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

Работает, если у вас есть только имена столбцов:

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

ИЗМЕНИТЬ

Просто случилось, что, может быть, вы хотите автоматизировать это, чтобы делать каждые три столбца. Я знаю, что есть лучший способ, но здесь он находится в наборе данных из 100 столбцов:

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

РЕДАКТИРОВАТЬ 2 Все еще недовольны индексацией. Я думаю, что есть лучший/быстрый способ передать индексы. здесь второй, хотя и не удовлетворяющий метод:

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

Ответ 2

для строк из векторов a, b, c

 rowMeans(dat[1:3])

означает для строк из векторов d, e, f

 rowMeans(dat[4:6])

все за один звонок вы получаете

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

если вы знаете только имена столбцов, а не порядок, который вы можете использовать:

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

Ответ 3

Аналогичный вопрос задал @david: усреднение каждые 16 столбцов в r (теперь закрыто), на что я ответил, адаптировав ответ @TylerRinker выше, следуя предложение @joran и @Ben. Поскольку полученная функция может помочь OP или будущим читателям, я копирую эту функцию здесь, а также пример для данных 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], ...))
            })
}

Затем, чтобы найти среднее из репликатов:

byapply(dat, 3, rowMeans)

Или, возможно, стандартное отклонение репликатов:

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

Обновление

by также может быть задан как вектор групп:

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

Ответ 4

Решение rowMeans будет быстрее, но для полноты здесь вы можете сделать это с помощью apply:

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

Ответ 5

Вдохновленный предложением @joran, я придумал это (фактически немного отличающееся от того, что он предложил, хотя предложение транспонирования было особенно полезно):

Создайте кадр данных из примерных данных с p cols для имитации реалистичного набора данных (после ответа @TylerRinker выше и в отличие от моего плохого примера в вопросе)

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

Переименуйте столбцы в этом фрейме данных, чтобы создать группы из n последовательных столбцов, так что, если меня интересуют группы из трех столбцов, я получаю имена столбцов, такие как 1,1,1,2,2,2, 3,3,3 и т.д., Или если мне нужны группы из четырех столбцов, это будет 1,1,1,1,2,2,2,2,3,3,3,3 и т.д. Я собираюсь с тремя на данный момент (я думаю, это своего рода индексация для таких людей, как я, которые мало знают об индексации)

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

Теперь используйте apply и tapply, чтобы получить средства строк для каждой из групп.

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

Основными недостатками являются то, что имена столбцов в исходных данных заменяются (хотя это можно было бы устранить, поставив числа группировки в новую строку, а не в имена столбцов) и чтобы имена столбцов возвращались функцией apply-tapply в бесполезном порядке.

В дополнение к предложению @joran, здесь a data.table solution:

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]

Спасибо всем за ваши быстрые и терпеливые усилия!

Ответ 6

Существует очень простое решение, если вы заинтересованы в применении функции к каждой уникальной комбинации столбцов, в том, что называется комбинаторика.

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

Чтобы вычислить статистику для каждой уникальной комбинации из трех столбцов и т.д., просто измените значение 2 на 3. Операция векторизована и, следовательно, быстрее, чем петли, такие как семейные функции apply, используемые выше. Если порядок столбцов имеет значение, то вместо этого вам нужен алгоритм перестановок, предназначенный для воспроизведения упорядоченных множеств: combinat::permn