我想在3个类别中聚合data.frame,其中一个类别不同。不幸的是,这一个不同的类别包含NA(实际上它是它需要改变的原因)。因此,我创建了一个data.frames
列表。此列表中的每个data.frame仅包含三个变量的完整案例(只有一个变量)。
让我们再现一遍:
library(plyr)
mydata <- warpbreaks
names(mydata) <- c("someValue","group","size")
mydata$category <- c(1,2,3)
mydata$categoryA <- c("A","A","X","X","Z","Z")
# add some NA
mydata$category[c(8,10,19)] <- NA
mydata$categoryA[c(14,1,20)] <- NA
# create a list of dfs that contains TRUE FALSE
noNAList <- function(vec){
res <- !is.na(vec)
return(res)
}
testTF <- lapply(mydata[,c("category","categoryA")],noNAList)
# create a list of data.frames
selectDF <- function(TFvec){
res <- mydata[TFvec,]
return(res)
}
# check x and see that it may contain NAs as long
# as it's not in one of the 3 categories I want to aggregate over
x <-lapply(testTF,selectDF)
## let's ddply get to work
doddply <- function(df){
ddply(df,.(group,size),summarize,sumTest = sum(someValue))
}
y <- lapply(x, doddply);y
y
非常接近我想要的内容
$category
group size sumTest
1 A L 375
2 A M 198
3 A H 185
4 B L 254
5 B M 259
6 B H 169
$categoryA
group size sumTest
1 A L 375
2 A M 204
3 A H 200
4 B L 254
5 B M 259
6 B H 169
但是我需要在第三个变量变量上实现聚合,在这种情况下category
和categoryA
。就像:
group size category sumTest sumTestTotal
1 A H 1 46 221
2 A H 2 46 221
3 A H 3 93 221
等等。如何将名称(x)添加到lapply中,或者我是否需要循环或环境?
编辑: 请注意,我希望在混合中添加EITHER类别或categoryA。实际上,我有大约15个相互排斥的分类变量。
答案 0 :(得分:4)
如果我理解你的问题,我认为你可能会对自己造成很大困难。
如果您想通过三个(或四个)变量聚合data.frame'myData',您只需执行此操作:
aggregate(someValue ~ group + size + category + categoryA, sum, data=mydata)
group size category categoryA someValue
1 A L 1 A 51
2 B L 1 A 19
3 A M 1 A 17
4 B M 1 A 63
aggregate
会自动删除任何类别中包含NA
的行。如果someValue有时为NA
,则可以添加参数na.rm = T.
我还注意到你在函数中添加了许多不必要的代码。例如:
# create a list of data.frames
selectDF <- function(TFvec){
res <- mydata[TFvec,]
return(res)
}
可以写成:
selectDF <- function(TFvec) mydata[TFvec,]
此外,使用lapply
创建没有NA
的两个数据框的列表是过度的。试试这段代码:
x = list(mydata[!is.na(mydata$category),],mydata[!is.na(mydata$categoryA),])
答案 1 :(得分:3)
我知道明确请求ddply()/lapply()
解决方案的问题。
但是......如果你愿意接受黑暗的一面,这里有一个基于data.table()
的函数可以解决这个问题:
# Convert mydata to a data.table
library(data.table)
dt <- data.table(mydata, key = c("group", "size"))
# Define workhorse function
myfunction <- function(dt, VAR) {
E <- as.name(substitute(VAR))
dt[i = !is.na(eval(E)),
j = {n <- sum(.SD[,someValue])
.SD[, list(sumTest = sum(someValue),
sumTestTotal = n,
share = sum(someValue)/n),
by = VAR]
},
by = key(dt)]
}
# Test it out
s1 <- myfunction(dt, "category")
s2 <- myfunction(dt, "categoryA")
在编辑时添加
以下是如何针对不同分类变量的向量运行此操作:
catVars <- c("category", "categoryA")
ll <- lapply(catVars,
FUN = function(X) {
do.call(myfunction, list(dt, X))
})
names(ll) <- catVars
lapply(ll, head, 3)
# $category
# group size category sumTest sumTestTotal share
# [1,] A H 2 46 185 0.2486486
# [2,] A H 3 93 185 0.5027027
# [3,] A H 1 46 185 0.2486486
#
# $categoryA
# group size categoryA sumTest sumTestTotal share
# [1,] A H A 79 200 0.395
# [2,] A H X 68 200 0.340
# [3,] A H Z 53 200 0.265
答案 2 :(得分:1)
最后,我找到了一个可能不像Josh那样光滑的解决方案,但它没有黑暗力量(data.table)。你可能会笑 - 这是我可重复的例子,使用与问题中相同的样本数据。
qual <- c("category","categoryA")
# get T / F vectors
noNAList <- function(vec){
res <- !is.na(vec)
return(res)
}
selectDF <- function(TFvec) mydata[TFvec,]
NAcheck <- lapply(mydata[,qual],noNAList)
# create a list of data.frames
listOfDf <- lapply(NAcheck,selectDF)
workhorse <- function(charVec,listOfDf){
dfs <- list2env(listOfDf)
# create expression list
exlist <- list()
for(i in 1:length(qual)){
exlist[[qual[i]]] <- parse(text=paste("ddply(",qual[i],
",.(group,size,",qual[i],"),summarize,sumTest = sum(someValue))",
sep=""))
}
res <- lapply(exlist,eval,envir=dfs)
return(res)
}
答案 3 :(得分:0)
这更像你的意思吗?我发现你的例子非常难以理解。在下面的代码中,该方法可以接受任何列,然后通过它进行聚合。它可以返回someValue的多个聚合函数。然后,我找到您想要聚合的所有列名,然后将该函数应用于该向量。
# Build a method to aggregate by column.
agg.by.col = function (column) {
by.list=list(mydata$group,mydata$size,mydata[,column])
names(by.list) = c('group','size',column)
aggregate(mydata$someValue, by=by.list, function(x) c(sum=sum(x),mean=mean(x)))
}
# Find all the column names you want to aggregate by
cols = names(mydata)[!(names(mydata) %in% c('someValue','group','size'))]
# Apply the method to each column name.
lapply (cols, agg.by.col)