R聚合所有可能的组合,包括“不在乎”

时间:2014-06-26 15:23:24

标签: r aggregate

假设我们有一个包含3列代表3种不同情况的数据框,每个数据框可以是0或1状态。第4列包含测量值。

set.seed(123)
df <- data.frame(round(runif(25)),
                 round(runif(25)),
                 round(runif(25)),
                 runif(25))
colnames(df) <- c("V1", "V2", "V3", "x")
head(df)

  V1 V2 V3         x
1  0  1  0 0.2201189
2  1  1  0 0.3798165
3  0  1  1 0.6127710

aggregate(df$x, by=list(df$V1, df$V2, df$V3), FUN=mean)

  Group.1 Group.2 Group.3         x
1       0       0       0 0.1028646
2       1       0       0 0.5081943
3       0       1       0 0.4828984
4       1       1       0 0.5197925
5       0       0       1 0.4571073
6       1       0       1 0.3219217
7       0       1       1 0.6127710
8       1       1       1 0.6029213

聚合函数计算所有可能组合的均值。但是,在我的研究中,我还需要知道组合的结果,其中某些列可能具有任何状态。例如,V1 == 1&amp;的所有观测值的平均值。 V2 == 1,无论V3的内容如何。结果应如下所示,星号代表&#34;不关心&#34;:

  Group.1 Group.2 Group.3         x
1       *       *       * 0.1234567 (this is the mean of all rows)
2       0       *       * 0.1234567
3       1       *       * 0.1234567
4       *       0       * 0.1224567
5       *       1       * 0.1234567
[ all other possible combinations follow, should be total of 27 rows ]

有没有简单的方法来实现这一目标?

3 个答案:

答案 0 :(得分:2)

以下是ldply - ddply方法:

library(plyr)
ldply(list(.(V1,V2,V3),.(V1),.(V2),.()), function(y) ddply(df,y,summarise,x=mean(x)))
   V1 V2 V3         x  .id
1   0  0  0 0.1028646 <NA>
2   0  0  1 0.4571073 <NA>
3   0  1  0 0.4828984 <NA>
4   0  1  1 0.6127710 <NA>
5   1  0  0 0.5081943 <NA>
6   1  0  1 0.3219217 <NA>
7   1  1  0 0.5197925 <NA>
8   1  1  1 0.6029213 <NA>
9   0 NA NA 0.4436400 <NA>
10  1 NA NA 0.4639997 <NA>
11 NA  0 NA 0.4118793 <NA>
12 NA  1 NA 0.5362985 <NA>
13 NA NA NA 0.4566702 <NA>

基本上,您创建了一个您感兴趣的所有变量组合的列表,并使用ldply并使用ddply进行迭代以执行聚合。 plyr的神奇之处在于为您提供紧凑的数据框架。剩下的就是删除由平均值(.id)引入的虚假.()列,并在需要时用NA替换组中的"*"

要获得所有组合,您可以使用combnlapply生成包含相关组合的列表,以插入ldply

all.combs <- unlist(lapply(0:3,combn,x=c("V1","V2","V3"),simplify=FALSE),recursive=FALSE)
ldply(all.combs, function(y) ddply(df,y,summarise,x=mean(x)))
    .id         x V1 V2 V3
1  <NA> 0.4566702 NA NA NA
2  <NA> 0.4436400  0 NA NA
3  <NA> 0.4639997  1 NA NA
4  <NA> 0.4118793 NA  0 NA
5  <NA> 0.5362985 NA  1 NA
6  <NA> 0.4738541 NA NA  0
7  <NA> 0.4380543 NA NA  1
8  <NA> 0.3862588  0  0 NA
9  <NA> 0.5153666  0  1 NA
10 <NA> 0.4235250  1  0 NA
11 <NA> 0.5530440  1  1 NA
12 <NA> 0.3878900  0 NA  0
13 <NA> 0.4882400  0 NA  1
14 <NA> 0.5120604  1 NA  0
15 <NA> 0.4022073  1 NA  1
16 <NA> 0.4502901 NA  0  0
17 <NA> 0.3820042 NA  0  1
18 <NA> 0.5013455 NA  1  0
19 <NA> 0.6062045 NA  1  1
20 <NA> 0.1028646  0  0  0
21 <NA> 0.4571073  0  0  1
22 <NA> 0.4828984  0  1  0
23 <NA> 0.6127710  0  1  1
24 <NA> 0.5081943  1  0  0
25 <NA> 0.3219217  1  0  1
26 <NA> 0.5197925  1  1  0
27 <NA> 0.6029213  1  1  1

答案 1 :(得分:1)

首先,让我定义一个辅助函数来创建所有可能的列组合

allcomb<-function(x, addnone=T) {
    x<-do.call(c, lapply(length(v):1, function(n) combn(v,n,simplify=F)))
    if(addnone) x<-c(x,0)
    x
}

现在我们可以使用它来聚合不同的子集

v<-names(df)[1:3]
vv<-allcomb(v)
dd<-lapply(vv, function(cols) aggregate(df$x, df[, cols, drop=F], mean))

这实际上返回了所有不同组合的data.frames列表,将它们全部合并在一起,我们可以使用rbind.fill中的plyr

library(plyr)
dd<-do.call(rbind.fill, dd)

这实际上将“any”值保留为NA而不是“*”。如果想将它们变成星号(并因此将您的组列转换为字符串而不是数值),您可以这样做

dd[1:3]<-lapply(dd[1:3], function(x) {x[is.na(x)]<-"*";x})

最终给出了

   V1 V2 V3         x
1   0  0  0 0.1028646
2   1  0  0 0.5081943
3   0  1  0 0.4828984
4   1  1  0 0.5197925
5   0  0  1 0.4571073
6   1  0  1 0.3219217
7   0  1  1 0.6127710
8   1  1  1 0.6029213
9   0  0  * 0.3862588
10  1  0  * 0.4235250
11  0  1  * 0.5153666
12  1  1  * 0.5530440
13  0  *  0 0.3878900
14  1  *  0 0.5120604
15  0  *  1 0.4882400
16  1  *  1 0.4022073
17  *  0  0 0.4502901
18  *  1  0 0.5013455
19  *  0  1 0.3820042
20  *  1  1 0.6062045
21  0  *  * 0.4436400
22  1  *  * 0.4639997
23  *  0  * 0.4118793
24  *  1  * 0.5362985
25  *  *  0 0.4738541
26  *  *  1 0.4380543
27  *  *  * 0.4566702

答案 2 :(得分:1)

(很好的可重现代码,顺便说一句,陈述得很好的问题。)

攻击这个的最好办法也许就是创造(以及后来 丢弃)另一列表示分组。从你的开始 数据:

set.seed(123)
df <- data.frame(round(runif(25)),
                 round(runif(25)),
                 round(runif(25)),
                 runif(25))
colnames(df) <- c("V1", "V2", "V3", "x")

让我们首先使用第四个形成一个包含所有可能的data.frame 列提供唯一的组ID。

allpossibles <- expand.grid(V1=unique(df$V1), V2=unique(df$V2), V3=unique(df$V3))
allpossibles$id <- 1:nrow(allpossibles)
head(allpossibles, n=3)
##    V1 V2 V3 id
##  1  0  1  0  1
##  2  1  1  0  2
##  3  0  0  0  3

使用此data.frame,更改所需行的id 共性。例如,以下两种组合(1,1,0)和 (1,1,1)尽可能相同,因此请将id变量设置为 是一样的:

subset(allpossibles, V1==1 & V2==1)
##    V1 V2 V3 id
##  2  1  1  0  2
##  6  1  1  1  6
allpossibles$id[6] <- 2

从此处合并两个data.frames,以便合并id 原文:

df2 <- merge(df, allpossibles, by=c('V1','V2','V3'))
head(df2, n=3)
##    V1 V2 V3         x id
##  1  0  0  0 0.1028646  3
##  2  0  0  1 0.1750527  7
##  3  0  0  1 0.3435165  7

从这里开始,汇总数据和重新汇总就是一件简单的事情 使用allpossibles(重新获得V1V2V3):

df3 <- aggregate(df2$x, by=list(df2$id), FUN=mean)
colnames(df3) <- c('id','x')
(df4 <- merge(allpossibles, df3, by='id'))
##    id V1 V2 V3         x
##  1  1  0  1  0 0.4828984
##  2  2  1  1  0 0.5530440
##  3  2  1  1  1 0.5530440
##  4  3  0  0  0 0.1028646
##  5  4  1  0  0 0.5081943
##  6  5  0  1  1 0.6127710
##  7  7  0  0  1 0.4571073
##  8  8  1  0  1 0.3219217

如果您可以接受包含半重复行的数据(请参阅第2行和第3行) 上面),然后只需删除$id列并获取它。如果你必须 unique-ify行,如下所示:

df5 <- do.call(rbind, by(df4, df4$id, function(ldf) {
    if (nrow(ldf) > 1) {
        uniqlen <- apply(ldf, 2, function(x) length(unique(x)))
        ldf[,which(uniqlen > 1)] <- NA
        ldf <- ldf[1,]
    }
    ldf
}))
df5 <- df5[, ! 'id' == names(df5)]
df5
##    V1 V2 V3         x
##  1  0  1  0 0.4828984
##  2  1  1 NA 0.5530440
##  3  0  0  0 0.1028646
##  4  1  0  0 0.5081943
##  5  0  1  1 0.6127710
##  7  0  0  1 0.4571073
##  8  1  0  1 0.3219217

(如果你更换,可以使用看起来更清晰的代码 使用do.call(rbind, by(ddply( plyr ddply该 内部功能及其结果是一样的。在这种情况下{{1}} 有点慢,但可能会有更好的改善 内部功能。)