【问题标题】:Calculate groups of column means and standard deviations计算列均值和标准差组
【发布时间】:2017-02-10 15:37:45
【问题描述】:

主要是在 stdevs 方面遇到一些问题,也可能是最佳方法解决方案。

dat <- data.frame(matrix(rnorm(16*100), ncol=100)) # data

在这个例子中,我有一个 100 列的数据集,我需要以 25 个样本为一组获取每行的均值和标准差

我首先找到了可以单独执行此操作的代码

as.data.frame(rowMeans(dat[,1:25]))     # mean of columns 1:25
as.data.frame(apply(dat[,1:25],1,mean)) # mean of columns 1:25
as.data.frame(apply(dat[,1:25],1,sd))   # sd of columns 1:25

最初我使用 rowMeans 并通过下面的循环完成这项工作:

dat.means <- list() # create empty list for means
# mean of every 25 cols
count <- 1
for(i in seq(1,length(dat),25)){
  dat.means[[count]] <- cbind(rowMeans(as.data.frame(dat[,i:i+24])))
  count=count+1
}

此时我找不到 rowMeans 的等价物来计算标准差,因此回溯到尝试使用 apply 代替。然而,我对如何以这种方式使用它的知识非常缺乏,而且我现在只得到了错误。

for(i in seq(1,length(dat),25)){
  dat.means[[count]] <- cbind(apply(dat[,i:i+24],1,mean))
  count=count+1
}

#Error in apply(dat[, i:i + 24], 1, mean) : 
# dim(X) must have a positive length

我已经尝试了上述循环的其他一些迭代,但我仍然收到发布的错误。

我也有一种感觉,循环可能不是最好的方法,但我不知所措。感谢任何帮助。

回复疑似重复问题here 在具有 NA 值的数据框中计算 sd 和均值不是这里的问题,问题是如何将函数有效地应用于更大数据框中的列组

【问题讨论】:

标签: r


【解决方案1】:

使用data.table 包:

# load 'data.table'
library(data.table)

# melt into long format and add 'row.id' variable with number of each row
dat2 <- melt(setDT(dat)[, row.id := .I], id = 'row.id')

# create a grouping variable for each block of 25 values
dat2[, grp := rep(1:4, each = 25), by = row.id]

# summarise
dat2[, .(mn = mean(value), std = sd(value) ), by = .(row.id,grp)]

给出:

    row.id grp          mn       std
 1:      1   1 -0.30388554 1.0307631
 2:      2   1  0.04381967 0.7939788
 3:      3   1  0.03106169 0.8581719
 4:      4   1 -0.15215035 0.8200987

....

15:     15   1 -0.23641918 0.7024393
16:     16   1  0.09745967 1.0253811
17:      1   2 -0.16414997 0.8695713
18:      2   2 -0.06763887 1.0294245

....

31:     15   2  0.06034238 0.7756055
32:     16   2  0.16387033 0.9285894
33:      1   3  0.32860736 1.0802055
34:      2   3  0.51183174 0.9562819

....

47:     15   3  0.16075275 1.0335789
48:     16   3 -0.43298467 1.1010562
49:      1   4  0.24918962 0.9580600
50:      2   4 -0.13005426 1.1693455

....

62:     14   4  0.02436604 0.7341284
63:     15   4 -0.19614383 0.7039496
64:     16   4  0.01182338 0.8465747

这是如何工作的:

  • 使用setDT(dat),数据帧被转换为data.table(这是data.frame的增强形式)
  • [, row.id := .I] 添加带有行号的变量
  • 然后使用melt 将数据转换为以行号为标识符的长格式。
  • 接下来,对于每个row.id,使用rep(1:4, each = 25) 创建一个分组变量,该变量创建一个由25 个1 组成的向量,然后是25 个2 的向量,依此类推。例如,row.id == 1 的前 25 个值(对应于原始 dat-dataframe 的前 25 列)获取组 ID 1,第二个 25 个值获取组 ID 2,依此类推.
  • 接下来,您使用dat2[, .(mn = mean(value), std = sd(value) ), by = .(row.id,grp)] 进行汇总,其中使用row.idgrp 作为分组变量。

结果是每行每组列的平均值和标准差。


另一种选择是使用dcastmelt 的组合以及在dcast 中指定多个聚合函数的可能性:

dcast(melt(setDT(dat)[, row.id := .I], id = 'row.id')[, grp := rep(1:4, each = 25), by = row.id],
      row.id ~ grp, fun.aggregate = list(mean, sd))

给出:

    row.id value_mean_1 value_mean_2 value_mean_3 value_mean_4 value_sd_1 value_sd_2 value_sd_3 value_sd_4
 1:      1  -0.30388554  -0.16414997   0.32860736   0.24918962  1.0307631  0.8695713  1.0802055  0.9580600
 2:      2   0.04381967  -0.06763887   0.51183174  -0.13005426  0.7939788  1.0294245  0.9562819  1.1693455
 3:      3   0.03106169  -0.07250312   0.21619928   0.13092043  0.8581719  1.1439506  0.9441762  1.0006230
 4:      4  -0.15215035  -0.08417522  -0.27278714  -0.04190002  0.8200987  0.9008114  1.0394255  1.2063465
 5:      5   0.21871123   0.08029101  -0.04965507  -0.15279897  0.9593703  0.8409534  0.8878550  1.0157824
 6:      6   0.22335221   0.27142844   0.14032413   0.09975956  1.1154142  1.0896226  0.8587636  1.1147968
 7:      7   0.16725794  -0.03462013   0.14675249  -0.15678569  0.9991910  0.9236954  1.1258560  1.0250408
 8:      8  -0.12872236   0.03884649  -0.48565736  -0.30525278  1.0118579  1.0266040  1.1284902  0.9048042
 9:      9   0.25986114   0.25181718   0.07673463  -0.11521187  1.0509685  0.8352278  1.0952720  1.0706587
10:     10  -0.32670802  -0.04590547   0.22610217   0.09406650  1.0674699  0.8378048  0.8128130  0.9126611
11:     11  -0.16219092  -0.24172025  -0.14231462   0.03671087  1.1617784  1.0522955  0.8899262  0.8982543
12:     12   0.21109682   0.19735885  -0.03901236  -0.19283362  0.9064956  0.9530479  1.0422911  0.8323033
13:     13   0.11926882   0.29611127  -0.37648849  -0.08673776  1.0739078  0.7220276  0.9455307  0.9623676
14:     14   0.26478861   0.16054927  -0.03315950   0.02436604  1.0555501  1.0713119  0.9112082  0.7341284
15:     15  -0.23641918   0.06034238   0.16075275  -0.19614383  0.7024393  0.7756055  1.0335789  0.7039496
16:     16   0.09745967   0.16387033  -0.43298467   0.01182338  1.0253811  0.9285894  1.1010562  0.8465747

dplyr/tidyr:

library(dplyr)
library(tidyr)
dat %>% 
  mutate(id = row_number()) %>% 
  gather(k, v, 1:100) %>% 
  group_by(id) %>% 
  mutate(grp = rep(1:4, each = 25)) %>% 
  group_by(id, grp) %>% 
  summarise(mn = mean(v), std = sd(v))

或者使用基础 R:

dat2 <- reshape(data = dat, ids = rownames(dat), direction = 'long', varying = list(names(dat)), times = names(dat))
dat2 <- transform(dat2, grp = ave(id, id, FUN = function(i) rep(1:4, each = 25)))
aggregate(X1 ~ id + grp, dat2, FUN = function(x) c(std = sd(x), mn = mean(x)))

【讨论】:

  • 我不确定这是否正确,我试图获得每 25 列的均值/标准差,这应该导致总共 4 列。不过,我仍在浏览代码。
  • @user3564760 我已经用解释更新了答案
【解决方案2】:

在 Base R 中,您可以将 tapply 与行长度相同的向量一起使用。

t(apply(dat, 1, function(row){
  tapply(row, INDEX=rep(1:4,c(25,25,25,25)), mean) # or sd
 })
)

因此,我们在您的数据集上为每一行运行apply。这被传递给tapply,其中行中每个元素的索引用数字 1、2、3 等分类(在这种情况下与row 的长度相同)。并将根据需要应用该功能。

输出:

                 1           2           3            4
[1,] -0.121142260  0.09109255  0.14969065 -0.008491494
[2,]  0.100938120  0.05852706  0.01694019  0.142837311
[3,] -0.270040421 -0.13509216 -0.02526398  0.176398683
[4,] -0.098860947 -0.02428447  0.34782123 -0.113218821
[5,]  0.058705197  0.25760489  0.30359424  0.457067044
[6,] -0.004329987  0.16322551 -0.20793649 -0.100291690
[7,]  0.146482094  0.08483679  0.16754837 -0.027107295
[8,]  0.013796914 -0.09084366  0.23347784 -0.194043232
[9,] -0.292440563  0.03362355  0.03668636 -0.113120322
[10,] -0.083525957 -0.04704885  0.21239136  0.378796710
[11,]  0.355684510 -0.34531764 -0.17021181 -0.293445102
[12,]  0.165324616 -0.32272002 -0.28986401 -0.135609262
[13,]  0.134330325 -0.04966847  0.22928705  0.012515783
[14,] -0.117367280  0.14220143  0.03655234 -0.175041681
[15,]  0.313223877  0.29656269 -0.14042955 -0.173458094
[16,]  0.062781966  0.09551260 -0.05704605  0.048142911

【讨论】:

  • 这看起来不错。知道如何自动化索引吗?例如。变量为replicates = 25; uniques = 4。我刚刚尝试了tapply(row, INDEX=rep(1:uniques,rep(replicates, uniques), mean)) 之类的方法,但没有成功。
  • 嗯好吧,我想我一定把代码弄乱了,在上面。我再次尝试了我评论中的代码,它成功了。谢谢!