Применить функцию по группам столбцов
Как я могу использовать 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