使用聚合

时间:2016-12-19 09:53:27

标签: r aggregate

我有一个像这样的矩阵

  P   A   B  C 
  1   2   0  5
  2   1   1  3
  3   0   4  7
  1   1   1  0
  3   1   1  0
  3   0   2  1
  2   3   3  4

我想按P和每个列合并/排序行。因此,每个P值为每列一次,并且每列中每个P的值相加。结果应该是:

 P  A  B  C
 1  3  0  0 
 1  0  1  0 
 1  0  0  5
 2  4  0  0
 2  0  4  0
 2  0  0  7
 3  1  0  0
 3  0  7  0
 3  0  0  8

我已经尝试了aggregate但它只能帮助我总结所有列的每个P值,以便每个P只有一行。

4 个答案:

答案 0 :(得分:4)

一个想法是在P上拆分数据框并应用自定义函数(fun1),该函数创建一个0的矩阵,并用列的总和替换对角线。即。

fun1 <- function(x){
m1 <- matrix(0, ncol = ncol(x) - 1, nrow = ncol(x) - 1)
diag(m1) <- sapply(x[-1], sum)
return(m1)
       }

l1 <- split(df, df$P)
do.call(rbind, lapply(l1, fun1))

#       [,1] [,2] [,3]
# [1,]    3    0    0
# [2,]    0    1    0
# [3,]    0    0    5
# [4,]    4    0    0
# [5,]    0    4    0
# [6,]    0    0    7
# [7,]    1    0    0
# [8,]    0    7    0
# [9,]    0    0    8

或者为了得到你想要的输出,那么

final_df <- as.data.frame(cbind(rep(names(l1), each = ncol(df)-1), 
                                             do.call(rbind, lapply(l1, fun1))))
names(final_df) <- names(df)

final_df
#  P A B C
#1 1 3 0 0
#2 1 0 1 0
#3 1 0 0 5
#4 2 4 0 0
#5 2 0 4 0
#6 2 0 0 7
#7 3 1 0 0
#8 3 0 7 0
#9 3 0 0 8

答案 1 :(得分:3)

我们从&#39; P&#39;中获得频率计数的最大值。列(&#39; i1&#39;),aggregate按&#39; P&#39;分组的列获取sum(&#39; df2&#39;),复制&#39; df2&#39;的行。通过&#39; i1&#39;,split数据集按&#39; P&#39;并将其他列中的非对角元素更改为0并将其作为data.frameorder返回,并将行名称更改为NULL。

i1 <- max(table(df1$P))
df2 <- aggregate(.~P, df1, sum)
df3 <-  df2[rep(1:nrow(df2), i1)]
res <- unsplit(lapply(split(df3, df3$P), function(x) {
         x[-1] <- diag(3)*x[-1]
         x}), df3$P)
res1 <- res[order(res$P),]
row.names(res1) <- NULL
res1
#  P A B C
#1 1 3 0 0
#2 1 0 1 0
#3 1 0 0 5
#4 2 4 0 0
#5 2 0 4 0
#6 2 0 0 7
#7 3 1 0 0
#8 3 0 7 0
#9 3 0 0 8

或使用data.table,转换&#39; data.frame&#39;到&#39; data.table&#39; (setDT(df1)),遍历Data.table(.SD)的子集,获取sum,按&#39; P&#39;分组,复制汇总数据集的行并将非对角元素更改为0(如第一个解决方案中所述)。

library(data.table)
setDT(df1)[, lapply(.SD, sum), by = P
           ][rep(1:.N, i1)
            ][, .SD*diag(ncol(df1)-1), by = P]
#   P A B C
#1: 1 3 0 0
#2: 1 0 1 0
#3: 1 0 0 5
#4: 2 4 0 0
#5: 2 0 4 0
#6: 2 0 0 7
#7: 3 1 0 0
#8: 3 0 7 0
#9: 3 0 0 8

或使用dplyr

library(dplyr)
library(purrr)
d1 <- as.data.frame(diag(i1))
df2 <-  df1 %>% 
             group_by(P) %>% 
             summarise_each(funs(sum)) %>% 
             replicate(i1, ., simplify = FALSE) %>%
             bind_rows() %>% 
             arrange(P)
df2[-1] <- map2(df2[-1], d1, ~.x * .y)
df2
# A tibble: 9 × 4
#      P     A     B     C
#   <int> <dbl> <dbl> <dbl>
#1     1     3     0     0
#2     1     0     1     0
#3     1     0     0     5
#4     2     4     0     0
#5     2     0     4     0
#6     2     0     0     7
#7     3     1     0     0
#8     3     0     7     0
#9     3     0     0     8

答案 2 :(得分:3)

另一个想法是使用diag函数本身来创建矩阵。然后你可以将这些矩阵组合在一起。

xx=aggregate(. ~ P, df, sum)
yy=xx[,-1]
yy=as.data.frame(t(yy))
cbind(rep(1:ncol(yy),nrow(yy)),do.call("rbind", lapply(yy, function(xx) diag(xx, nrow = nrow(yy), ncol = nrow(yy)))))

      [,1] [,2] [,3] [,4]
 [1,]    1    3    0    0
 [2,]    2    0    1    0
 [3,]    3    0    0    5
 [4,]    1    4    0    0
 [5,]    2    0    4    0
 [6,]    3    0    0    7
 [7,]    1    1    0    0
 [8,]    2    0    7    0
 [9,]    3    0    0    8

答案 3 :(得分:1)

除非我遗漏了某些东西,否则以下内容看起来也是有效的。 首先计算每个“P”的总和:

s = as.matrix(rowsum(dat[-1], dat$P))

创建最终矩阵:

k = s[rep(1:nrow(s), each = ncol(s)), ]

计算索引以替换为“0”:

k[col(k) != (row(k) - 1) %% ncol(k) + 1] = 0
k
#  A B C
#1 3 0 0
#1 0 1 0
#1 0 0 5
#2 4 0 0
#2 0 4 0
#2 0 0 7
#3 1 0 0
#3 0 7 0
#3 0 0 8

数据:

dat = structure(list(P = c(1L, 2L, 3L, 1L, 3L, 3L, 2L), A = c(2L, 1L, 
0L, 1L, 1L, 0L, 3L), B = c(0L, 1L, 4L, 1L, 1L, 2L, 3L), C = c(5L, 
3L, 7L, 0L, 0L, 1L, 4L)), .Names = c("P", "A", "B", "C"), class = "data.frame", row.names = c(NA, 
-7L))

计算出s后,user20650更直接的选择:

matrix(diag(ncol(s)), nrow(s) * ncol(s), ncol(s), byrow = TRUE) * c(t(s))

或者,同样,在同一个想法上搞乱其他有趣的选择:

kronecker(rep_len(1, nrow(s)), diag(ncol(s))) * c(t(s))

diag(ncol(s))[rep(1:ncol(s), nrow(s)), ] * s[rep(1:nrow(s), each = ncol(s)), ]