假设我们有一个包含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 ]
有没有简单的方法来实现这一目标?
答案 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
替换组中的"*"
。
要获得所有组合,您可以使用combn
和lapply
生成包含相关组合的列表,以插入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
(重新获得V1
,V2
和V3
):
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}}
有点慢,但可能会有更好的改善
内部功能。)