在列组上应用函数

时间:2012-05-19 00:20:21

标签: r dataframe multiple-columns apply

如何使用apply或相关函数创建一个新数据框,其中包含非常大的数据框中每对列的行平均值的结果?

我有一台仪器可以对大量样本输出n个重复测量值,其中每个单个测量值都是一个矢量(所有测量值都是相同的长度矢量)。我想计算每个样本的所有重复测量的平均值(和其他统计数据)。这意味着我需要将n个连续列组合在一起并进行逐行计算。

举一个简单的例子,对两个样本进行三次重复测量,我怎么能得到一个有两列(每个样本一个)的数据帧,一个是{{1}中每个重复行的平均值},dat$adat$b以及dat$cdat$ddat$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) )) 并且循环并且无法将它们整合在一起。我的实际数据有几百列。

6 个答案:

答案 0 :(得分:15)

这可能更适合您的情况,因为您传递了一系列索引。如果速度是一个问题(大数据框),我选择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])))

答案 1 :(得分:7)

表示来自向量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

答案 2 :(得分:7)

@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 :(得分:5)

rowMeans解决方案会更快,但为了完整起见,您可以使用apply执行此操作:

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

答案 4 :(得分:2)

受到@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等我要去现在有三个(我想这对于像我这样对索引知之甚少的人来说是一种索引)

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

主要的缺点是原始数据中的列名被替换(尽管可以通过将分组编号放在新行而不是列名中来克服)并且应用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]

感谢大家的快速和耐心的努力!

答案 5 :(得分:0)

如果您有兴趣将函数应用于每个独特的列组合,那么有一个非常简单的解决方案,即组合学。

apply

要计算三列等每个唯一组合的统计数据,只需将2更改为3.操作是矢量化的,因此比循环更快,例如上面使用的combinat::permn族函数。如果列的顺序很重要,那么您需要一个用于重现有序集的置换算法:[01:23:45] ERROR /foo/bar:42 - this is a log message [01:23:45] ERROR /foo/bar:42 - this is a ERROR log message [01:23:45] CRITICAL /foo/bar:42 - this is a log message [01:23:45] CRITICAL /foo/bar:42 - this is a CRITICAL log message