在列组上应用函数
Posted
技术标签:
【中文标题】在列组上应用函数【英文标题】:apply a function over groups of columns 【发布时间】:2012-05-26 12:56:52 【问题描述】:如何使用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
和循环,但无法完全组合在一起。我的实际数据有数百列。
【问题讨论】:
总是每三列吗?您是在输入名称向量的向量还是索引向量的向量?如果用户 user1317221_G 的答案不是您想要的,也许您需要提供更多信息。 对于后代,上面的问题似乎是这个关于将函数应用于行组的最近问题的转置(并且有一些不同的方法):***.com/q/10837258/1036500 【参考方案1】:这可能更适用于您传递索引列表的情况。如果速度是一个问题(大数据框),我会选择lapply
和do.call
而不是sapply
:
x <- list(1:3, 4:6)
do.call(cbind, lapply(x, function(i) rowMeans(dat[, i])))
如果您也只有 col 名称也可以使用:
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])))
【讨论】:
这省略了最后一列,因为它没有三列可以绑定在一起。 我将寻求一种更好的方法来创建索引并返回此处。 这是该问题的链接,供未来的搜索者使用 LINK 其他索引方法:split(1:n,rep(1:n,each=3,length=n))。这里 n 是列数。 @WojciechSobala 你能把答案发布到上面的链接 9 虽然你必须删除最后一个列表索引,因为它的长度不是 3。【参考方案2】:@david 在这里提出了一个类似的问题:averaging every 16 columns in r(现已关闭),我根据@joran 和@Ben 的建议改编了上面@TylerRinker 的答案来回答这个问题。因为生成的函数可能对 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)
【讨论】:
【参考方案3】:向量 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
【讨论】:
那么对于具有数百列的数据框呢?您如何概括这一点? @joran,你说得对,我在准备我的问题时太仓促了,抱歉含糊不清。 Tyler Rinker 的编辑有我所追求的代码。【参考方案4】:rowMeans
解决方案会更快,但为了完整起见,您可以使用apply
执行此操作:
t(apply(dat,1,function(x) c(mean(x[1:3]),mean(x[4:6])) ))
【讨论】:
数百列数据框中每连续三列集合的行均值如何? @Ben 将其简化为您已经解决的问题:(1)转置(2)使用 plyr 或 data.table,( 3)转回。 (假设一切都是数字。) 我会试一试,看看我能不能想出比上面 Tyler 的解决方案更有效的方法(不太可能,但值得一试!) 感谢您的建议,我根据您的建议提出了两种方法(尽管可能不完全是您的想法......),见上文。【参考方案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 的建议,这里有一个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]
感谢大家的快速和耐心的努力!
【讨论】:
只是添加一个指针,指出lapply(.SD,mean)
成语应该在 v1.8.1 中变得更快,这要归功于:i)this question 中的发现和 ii)@987654330 的自动 .Internal()isation @(不再需要维基点 3)。此外,.SDcols
通常很有用,但在这里不需要。
@MatthewDowle 感谢您的留言!很高兴知道 .SDcols
,我不熟悉,很高兴听到 data.table
越来越快!【参考方案6】:
如果您有兴趣将函数应用于每个独特的列组合,有一个非常简单的解决方案,即所谓的组合。
combinations <- combn(colnames(df),2,function(x) rowMeans(df[x]))
要计算三列的每个唯一组合的统计信息等,只需将 2 更改为 3。该操作是矢量化的,因此比循环更快,例如上面使用的 apply
系列函数。如果列的顺序很重要,那么您需要一个置换算法来重现有序集:combinat::permn
【讨论】:
“如果订单很重要”是什么意思,combinat::permn 函数是什么?请问可以修改代码吗? 组合与排列不是一回事:youtube.com/watch?v=s2W6Bce_T30 如果输入的顺序很重要,那么您需要的就是排列。在这种情况下,“顺序”是指列的顺序。以上是关于在列组上应用函数的主要内容,如果未能解决你的问题,请参考以下文章