根据按列名分组的列计算矩阵每一行的汇总统计信息

Calculate summary statistics for each row of a matrix based on columns grouped by column names

我有一个矩阵,其中列名显示分组信息(下例中的组 A 和 B)。我想要的是对于每一行,使用属于每个组的列计算给定的汇总统计信息,例如平均值。示例代码如下:

#input data
data = matrix(1:100, 10, 10, byrow = T)
colnames(data) = rep(c("A", "B"), each = 5)
data

#calculation
result = t(apply(data, 1, function(x, label){aggregate(x, by=list(label), FUN=mean)$x}, label = colnames(data)))
result

有不同的方法可以做到这一点(比如 for 循环或像上面的实现一样使用 apply)。但是,当我在具有数百行和至少 10k 列的矩阵上使用它时,此实现不是很有效。我想知道是否有更快更有效的方法来实现这个?我输入的数据格式是矩阵,所以任何基于其他数据类型实现的方法也需要考虑数据类型转换的时间。

这是使用的最佳方案tapply:

tapply(t(data), list(col(data), array(colnames(data), dim(t(data)))), mean)
    A  B
1   3  8
2  13 18
3  23 28
4  33 38
5  43 48
6  53 58
7  63 68
8  73 78
9  83 88
10 93 98

tapply(data, list(t(colnames(data))[rep(1,nrow(data)), ], row(data)), mean)
  1  2  3  4  5  6  7  8  9 10
A 3 13 23 33 43 53 63 73 83 93
B 8 18 28 38 48 58 68 78 88 98

 tapply(t(data), interaction(colnames(data), col(data)), mean)
 A.1  B.1  A.2  B.2  A.3  B.3  A.4  B.4  A.5  B.5  A.6  B.6  A.7  B.7  A.8  B.8  A.9  B.9 A.10 B.10 
   3    8   13   18   23   28   33   38   43   48   53   58   63   68   73   78   83   88   93   98 

更多基础 R 解决方案:

sapply(split.default(data.frame(data), colnames(data)), rowMeans)
       A  B
 [1,]  3  8
 [2,] 13 18
 [3,] 23 28
 [4,] 33 38
 [5,] 43 48
 [6,] 53 58
 [7,] 63 68
 [8,] 73 78
 [9,] 83 88
[10,] 93 98

data.frame(data) |>
  reshape(split(1:ncol(data), colnames(data)),  dir = 'long') |>
  (\(x)aggregate(.~id, x, mean))()

   id time  A  B
1   1    3  3  8
2   2    3 13 18
3   3    3 23 28
4   4    3 33 38
5   5    3 43 48
6   6    3 53 58
7   7    3 63 68
8   8    3 73 78
9   9    3 83 88
10 10    3 93 98

我们可以在base R

中使用aggregate
aggregate(Freq ~ ., as.data.frame.table(data), FUN = mean)

或遍历 unique 列名,对数据进行子集化并得到 rowMeans

sapply(unique(colnames(data)), function(nm)
          rowMeans(data[, colnames(data) == nm]))

或使用 dapply 来自 collapse

library(collapse)
dapply(data, MARGIN = 1, FUN = function(x)  fmean(x, g = colnames(data)))
       A  B
 [1,]  3  8
 [2,] 13 18
 [3,] 23 28
 [4,] 33 38
 [5,] 43 48
 [6,] 53 58
 [7,] 63 68
 [8,] 73 78
 [9,] 83 88
[10,] 93 98