我有一個多維陣列,其中第三維代表時間。出于這個問題的目的,讓我們使用包中的ozone資料集plyr:
> str(ozone)
num [1:24, 1:24, 1:72] 260 258 258 254 252 252 250 248 248 248 ...
- attr(*, "dimnames")=List of 3
..$ lat : chr [1:24] "-21.2" "-18.7" "-16.2" "-13.7" ...
..$ long: chr [1:24] "-113.8" "-111.3" "-108.8" "-106.3" ...
..$ time: chr [1:72] "1" "2" "3" "4" ...
從檔案中:
這些資料是 1995 年 1 月至 2000 年 12 月覆寫中美洲的一個非常粗略的 24 x 24 網格上的月臭氧平均值。資料存盤在 3d 區域中,前兩個維度代表緯度和經度,第三個維度代表時間。
我想做的是為每個緯度/經度單元格創建每月平均值。我可以對單個緯度/經度組合執行此操作,tapply如下所示:
> tapply(ozone[1,1,], rep(1:12, 6), mean)
1 2 3 4 5 6 7 8 9 10 11 12
264.6667 257.6667 255.0000 251.3333 257.6667 265.0000 274.0000 275.3333 277.0000 285.0000 283.0000 273.3333
但我很難一次在整個陣列上這樣做。apply讓我選擇要操作的維度(MARGIN),tapply讓我使用一個因子來選擇切片(INDEX),但我需要兩者。
我愿意接受建議,但由于資料的大小和復雜性,我更喜歡使用陣列而不是資料框。
下面來自GKi和G. Grothendieck的兩個出色的答案,非常感謝兩者。我已經完成了它們microbenchmark,這些是結果:
> microbenchmark(
GKi1 = apply(array(ozone, c(dim(ozone)[1:2], 12, dim(ozone)[3]/12), c(dimnames(ozone)[1:2], list(month=1:12, year=1995:2000))), 1:3, mean),
GKi2 = simplify2array(lapply(split(dimnames(plyr::ozone)[[3]], 1:12), \(x) apply(plyr::ozone[,,x], 1:2, mean))),
Grothendieck = apply(ozone, 1:2, month_mean),
times=100)
Unit: milliseconds
expr min lq mean median uq max neval
GKi1 21.67954 22.99889 25.73446 23.89190 27.26137 46.26843 100
GKi2 20.90931 22.90361 26.64572 23.88572 30.76128 45.16404 100
Grothendieck 40.98800 43.26854 49.51313 44.73214 52.28114 266.12759 100
uj5u.com熱心網友回復:
您可以將時間維度劃分為月份和年份,然后使用 then apply。
x <- plyr::ozone
x <- array(x, c(dim(x)[1:2], 12, dim(x)[3]/12),
c(dimnames(x)[1:2], list(month=1:12, year=1995:2000)))
#dim(x) <- c(dim(x)[1:2], 12, dim(x)[3]/12) #Alternative without names
. <- apply(x, 1:3, mean)
.[1,1,]
# 1 2 3 4 5 6 7 8
#264.6667 257.6667 255.0000 251.3333 257.6667 265.0000 274.0000 275.3333
# 9 10 11 12
#277.0000 285.0000 283.0000 273.3333
另一種選擇可以是。
. <- simplify2array(lapply(split(dimnames(plyr::ozone)[[3]], 1:12), \(x)
apply(plyr::ozone[,,x], 1:2, mean)))
.[1,1,]
# 1 2 3 4 5 6 7 8
#264.6667 257.6667 255.0000 251.3333 257.6667 265.0000 274.0000 275.3333
# 9 10 11 12
#277.0000 285.0000 283.0000 273.3333
或使用tapplyinside apply(基于@g-grothendieck 的 grat answer)
. <- apply(plyr::ozone, 1:2, tapply, rep(1:12, 6), mean)
.[,1,1]
#aperm(., c(2,3,1))[1,1,] #Alternative
# 1 2 3 4 5 6 7 8
#264.6667 257.6667 255.0000 251.3333 257.6667 265.0000 274.0000 275.3333
# 9 10 11 12
#277.0000 285.0000 283.0000 273.3333
rowMeans如果可以使用種子物質。
. <- rowMeans(`dim<-`(ozone, c(dim(plyr::ozone)[1:2], 12, dim(ozone)[3]/12)), dims=3)
.[1,1,]
# [1] 264.6667 257.6667 255.0000 251.3333 257.6667 265.0000 274.0000 275.3333
# [9] 277.0000 285.0000 283.0000 273.3333
uj5u.com熱心網友回復:
使用最后在注釋中可重復定義的陣列,我們有以下 using apply。未定義精確的輸出,但如果您想要不同的維度順序,請使用aperm.
month_mean <- function(x) c(tapply(x, rep(1:12, each = 6), mean))
aa <- apply(a, 1:2, month_mean)
現在檢查結果
# check
all.equal(aa[, 4, 9], month_mean(a[4, 9, ]))
## [1] TRUE
# another check
for(i in 1:24) for(j in 1:24) {
check <- all.equal(aa[, i, j], month_mean(a[i, j, ]))
if (!check) stop("not equal")
}
筆記
set.seed(123)
a <- array(runif(24 * 24 * 72), c(24, 24, 72))
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/480403.html
標籤:r
