Агрегировать по категориям, которые содержат NA с ddply и lapply? - PullRequest
3 голосов
/ 18 января 2012

Я хотел бы объединить data.frame по 3 категориям, одна из которых варьируется.К сожалению, эта одна изменяющаяся категория содержит 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 или мне нужен цикл или среда здесь?

РЕДАКТИРОВАТЬ: Обратите внимание, что я хочу, чтобы ЛЮБАЯ категория ИЛИ категория A добавлялась в микс.На самом деле у меня есть около 15 взаимоисключающих категориальных переменных.

Ответы [ 4 ]

4 голосов
/ 18 января 2012

Я думаю, что вы, возможно, делаете это очень тяжело для себя, если я правильно понимаю ваш вопрос.

Если вы хотите объединить 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),])
3 голосов
/ 18 января 2012

Я знаю, что вопрос явно требует решения 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
1 голос
/ 19 января 2012

Наконец, я нашел решение, которое может быть не таким гладким, как у Джоша, но оно работает без темных сил (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)

}
0 голосов
/ 19 января 2012

Это больше похоже на то, что вы имеете в виду?Я считаю ваш пример чрезвычайно сложным для понимания.В приведенном ниже коде метод может взять любой столбец и затем агрегировать его.Может возвращать несколько функций агрегации someValue.Затем я нахожу все имена столбцов, по которым вы хотите агрегировать, и затем применяю функцию к этому вектору.

...