2013-07-27 43 views
12

Tôi muốn tổng các cột riêng lẻ theo nhóm và suy nghĩ đầu tiên của tôi là sử dụng tapply. Tuy nhiên, tôi không thể nhận được tapply để hoạt động. Có thể sử dụng tapply để tổng hợp nhiều cột không? Nếu không, tại sao không?tổng hợp nhiều cột theo nhóm với tapply

Tôi đã tìm kiếm trên Internet rộng rãi và tìm thấy nhiều câu hỏi tương tự được đăng từ năm 2008. Tuy nhiên, không có câu hỏi nào được trả lời trực tiếp. Thay vào đó, các câu trả lời luôn đề xuất sử dụng một hàm khác.

Dưới đây là tập hợp dữ liệu mẫu mà tôi muốn tổng hợp táo theo tiểu bang, quả anh đào theo tiểu bang và mận theo tiểu bang. Dưới đây tôi đã biên soạn nhiều lựa chọn thay thế cho tapply rằng hoạt động.

Ở dưới cùng, tôi hiển thị sửa đổi đơn giản cho mã nguồn tapply cho phép tapply thực hiện thao tác mong muốn.

Tuy nhiên, có lẽ tôi đang xem một cách đơn giản để thực hiện thao tác mong muốn với tapply. Tôi không tìm kiếm các chức năng thay thế, mặc dù các lựa chọn thay thế khác được hoan nghênh.

Do sự đơn giản của việc sửa đổi của tôi thành mã nguồn tapply Tôi tự hỏi tại sao nó, hoặc một cái gì đó tương tự, chưa được triển khai.

Cảm ơn bạn đã được tư vấn. Nếu câu hỏi của tôi là bản sao, tôi sẽ sẵn lòng đăng câu hỏi của tôi dưới dạng câu trả lời cho câu hỏi khác đó.

Dưới đây là tập hợp dữ liệu ví dụ:

df.1 <- read.table(text = ' 

    state county apples cherries plums 
     AA  1  1   2  3 
     AA  2  10   20  30 
     AA  3  100  200  300 
     BB  7  -1   -2  -3 
     BB  8  -10  -20  -30 
     BB  9  -100  -200 -300 

', header = TRUE, stringsAsFactors = FALSE) 

này không hoạt động:

tapply(df.1, df.1$state, function(x) {colSums(x[,3:5])}) 

Các trang trợ giúp nói:

tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE) 

X  an atomic object, typically a vector. 

tôi đã nhầm lẫn bởi những cụm từ typically a vector mà làm cho tôi tự hỏi liệu có thể sử dụng khung dữ liệu không. Tôi chưa bao giờ rõ ràng về những gì có nghĩa là atomic object.

Dưới đây là một số lựa chọn thay thế cho tapply hoạt động. Giải pháp thay thế đầu tiên là công việc xung quanh kết hợp tapply với apply.

apply(df.1[,c(3:5)], 2, function(x) tapply(x, df.1$state, sum)) 

# apples cherries plums 
# AA 111  222 333 
# BB -111  -222 -333 

with(df.1, aggregate(df.1[,3:5], data.frame(state), sum)) 

# state apples cherries plums 
# 1 AA 111  222 333 
# 2 BB -111  -222 -333 

t(sapply(split(df.1[,3:5], df.1$state), colSums)) 

# apples cherries plums 
# AA 111  222 333 
# BB -111  -222 -333 

t(sapply(split(df.1[,3:5], df.1$state), function(x) apply(x, 2, sum))) 

# apples cherries plums 
# AA 111  222 333 
# BB -111  -222 -333 

aggregate(df.1[,3:5], by=list(df.1$state), sum) 

# Group.1 apples cherries plums 
# 1  AA 111  222 333 
# 2  BB -111  -222 -333 

by(df.1[,3:5], df.1$state, colSums) 

# df.1$state: AA 
# apples cherries plums 
#  111  222  333 
# ------------------------------------------------------------ 
# df.1$state: BB 
# apples cherries plums 
#  -111  -222  -333 

with(df.1, 
    aggregate(x = list(apples = apples, 
         cherries = cherries, 
         plums = plums), 
       by = list(state = state), 
       FUN = function(x) sum(x))) 

# state apples cherries plums 
# 1 AA 111  222 333 
# 2 BB -111  -222 -333 

lapply(split(df.1, df.1$state), function(x) {colSums(x[,3:5])}) 

# $AA 
# apples cherries plums 
#  111  222  333 
# 
# $BB 
# apples cherries plums 
#  -111  -222  -333 

Đây là mã nguồn cho tapply ngoại trừ việc tôi đã thay đổi dòng:

nx <- length(X) 

tới:

nx <- ifelse(is.vector(X), length(X), dim(X)[1]) 

Phiên bản này được sửa đổi của tapply thực hiện các hoạt động mong muốn:

my.tapply <- function (X, INDEX, FUN = NULL, ..., simplify = TRUE) 
{ 
    FUN <- if (!is.null(FUN)) match.fun(FUN) 
    if (!is.list(INDEX)) INDEX <- list(INDEX) 
    nI <- length(INDEX) 
    if (!nI) stop("'INDEX' is of length zero") 
    namelist <- vector("list", nI) 
    names(namelist) <- names(INDEX) 
    extent <- integer(nI) 
    nx  <- ifelse(is.vector(X), length(X), dim(X)[1]) # replaces nx <- length(X) 
    one <- 1L 
    group <- rep.int(one, nx) #- to contain the splitting vector 
    ngroup <- one 
    for (i in seq_along(INDEX)) { 
    index <- as.factor(INDEX[[i]]) 
    if (length(index) != nx) 
     stop("arguments must have same length") 
    namelist[[i]] <- levels(index)#- all of them, yes ! 
    extent[i] <- nlevels(index) 
    group <- group + ngroup * (as.integer(index) - one) 
    ngroup <- ngroup * nlevels(index) 
    } 
    if (is.null(FUN)) return(group) 
    ans <- lapply(X = split(X, group), FUN = FUN, ...) 
    index <- as.integer(names(ans)) 
    if (simplify && all(unlist(lapply(ans, length)) == 1L)) { 
    ansmat <- array(dim = extent, dimnames = namelist) 
    ans <- unlist(ans, recursive = FALSE) 
    } else { 
    ansmat <- array(vector("list", prod(extent)), 
      dim = extent, dimnames = namelist) 
    } 
    if(length(index)) { 
     names(ans) <- NULL 
     ansmat[index] <- ans 
    } 
    ansmat 
} 

my.tapply(df.1$apples, df.1$state, function(x) {sum(x)}) 

# AA BB 
# 111 -111 

my.tapply(df.1[,3:4] , df.1$state, function(x) {colSums(x)}) 

# $AA 
# apples cherries 
#  111  222 
# 
# $BB 
# apples cherries 
#  -111  -222 

Trả lời

16

tapply công trình trên một vector, đối với một data.frame bạn có thể sử dụng by (mà là một wrapper cho tapply, hãy nhìn vào mã):

> by(df.1[,c(3:5)], df.1$state, FUN=colSums) 
df.1$state: AA 
    apples cherries plums 
    111  222  333 
------------------------------------------------------------------------------------- 
df.1$state: BB 
    apples cherries plums 
    -111  -222  -333 
6

Bạn đang tìm kiếm by. Nó sử dụng INDEX theo cách bạn giả định tapply, theo hàng.

by(df.1, df.1$state, function(x) colSums(x[,3:5])) 

Vấn đề với việc bạn sử dụng tapply là bạn được lập chỉ mục data.frame bởi cột. (Bởi vì data.frame thực sự chỉ là một list của cột.) Vì vậy, tapply phàn nàn rằng chỉ số của bạn không phù hợp với chiều dài của data.frame của bạn mà là 5.

0

tôi nhìn vào mã nguồn cho by, như EDI gợi ý. Mã đó phức tạp hơn nhiều so với thay đổi của tôi đối với một dòng trong tapply. Bây giờ tôi đã thấy rằng my.tapply không hoạt động với trường hợp phức tạp hơn bên dưới, trong đó applescherries được tổng hợp bằng statecounty. Nếu tôi nhận được my.tapply để làm việc với trường hợp này, tôi có thể đăng mã tại đây sau:

df.2 <- read.table(text = ' 

    state county apples cherries plums 
     AA  1  1   2  3 
     AA  1  1   2  3 
     AA  2  10   20  30 
     AA  2  10   20  30 
     AA  3  100  200  300 
     AA  3  100  200  300 

     BB  7  -1   -2  -3 
     BB  7  -1   -2  -3 
     BB  8  -10  -20  -30 
     BB  8  -10  -20  -30 
     BB  9  -100  -200 -300 
     BB  9  -100  -200 -300 

', header = TRUE, stringsAsFactors = FALSE) 

# my function works 

    tapply(df.2$apples , list(df.2$state, df.2$county), function(x) {sum(x)}) 
my.tapply(df.2$apples , list(df.2$state, df.2$county), function(x) {sum(x)}) 

# my function works 

    tapply(df.2$cherries, list(df.2$state, df.2$county), function(x) {sum(x)}) 
my.tapply(df.2$cherries, list(df.2$state, df.2$county), function(x) {sum(x)}) 

# my function does not work 

my.tapply(df.2[,3:4], list(df.2$state, df.2$county), function(x) {colSums(x)}) 
Các vấn đề liên quan