個々の列をグループごとに合計したかったので、tapply
を使用することを最初に考えました。ただし、tapply
を機能させることができません。 tapply
を使用して複数の列を合計できますか?そうでない場合、なぜそうではないのですか?
私はインターネットを広範囲に検索し、2008年までさかのぼって投稿された同様の質問を数多く見つけました。しかし、それらの質問のどれも直接回答されていません。代わりに、応答は常に異なる関数の使用を提案します。
以下は、州別のリンゴ、州別のサクランボ、州別のプラムを合計するデータセットの例です。その下で、機能するtapply
の代替案を数多くコンパイルしました。
下部に、tapply
が目的の操作を実行できるようにするtapply
ソースコードの簡単な変更を示します。
それでも、tapply
を使用して目的の操作を実行する簡単な方法を見落としている可能性があります。追加の代替機能を歓迎しますが、私は代替機能を探していません。
私のtapply
ソースコードへの変更が単純であることを考えると、なぜそれが、または類似した何かがまだ実装されていないのでしょうか。
アドバイスありがとうございます。私の質問が重複している場合、他の質問への回答として質問を投稿させていただきます。
以下はデータセットの例です。
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)
これは動作しません:
tapply(df.1, df.1$state, function(x) {colSums(x[,3:5])})
ヘルプページは言う:
tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
X an atomic object, typically a vector.
typically a vector
というフレーズに戸惑い、データフレームを使用できるかどうか疑問に思いました。 atomic object
の意味がはっきりしていません。
動作するtapply
のいくつかの代替案を次に示します。最初の代替策は、tapply
と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
行を変更した以外は、tapply
のソースコードは次のとおりです。
nx <- length(X)
に:
nx <- ifelse(is.vector(X), length(X), dim(X)[1])
このtapply
の変更されたバージョンは、目的の操作を実行します。
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
tapply
はベクトルで機能し、data.frameにはby
を使用できます(これはtapply
のラッパーです。コードを見てください)。
> 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
by
を探しています。これは、INDEX
を、tapply
が行ごとに想定した方法で使用します。
_by(df.1, df.1$state, function(x) colSums(x[,3:5]))
_
tapply
を使用する際の問題は、_data.frame
_をcolumnで索引付けしていたことです。 (_data.frame
_は実際には列のlist
にすぎないため。)したがって、tapply
は、インデックスが_data.frame
_の長さ(5)と一致しないと不平を言いました。
EDiが示唆したように、by
のソースコードを確認しました。そのコードは、tapply
の1行に対する私の変更よりもかなり複雑でした。 my.tapply
は、apples
とcherries
がstate
とcounty
によって合計される、以下のより複雑なシナリオでは機能しません。私が取得する場合 my.tapply
このケースを処理するには、後でコードをここに投稿できます。
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)})