我試圖優化代碼并想使用的.Internal實作vapply并得到一個我不明白的錯誤。(現在我會?.Internal認真對待警告,“只有真正的 R 向導才應該考慮使用此功能”并使用可見的用戶vapply,但我想更好地理解錯誤。)
test <- rnorm(10000)
test_l <- as.list(test)
test_2 <- .Internal(vapply(test_l, \(x) x^2, numeric(1), FALSE))
# Error: '...' used in an incorrect context
將此與用戶可見的代碼進行比較vapply:
function (X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X))
X <- as.list(X)
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}
有人可以向我解釋為什么這不起作用嗎?
我的一些假設:
它是否在用戶可見的函式主體之外的另一個命名空間中運行,vapply或者我是否.Internal出于其他原因呼叫用戶可見函式而不是?
是因為內部vapply是一個特殊的內部,它的作業方式與其他內部不同嗎?
R-ints 手冊 [1] 指出:
2.2 特殊內部
還有一些特殊的 .Internal 函式:NextMethod、Recall、withVisible、cbind、rbind(允許使用 deparse.level 引數)、eapply、lapply 和 vapply。
但沒有給出更多細節。
有人可以給我更多關于那些“特殊內部”的細節嗎?
[1] https://cran.r-project.org/doc/manuals/R-ints.html#g_t_002eInternal-vs-_002ePrimitive
uj5u.com熱心網友回復:
C 代碼指的是,R_DotsSymbol即省略號。呼叫范圍沒有省略號。它不能,因為它不是函式呼叫。
您可以使用標準 R 代碼重現錯誤,如下所示:
foo <- function() {
return(...)
}
foo()
#Error in foo() : '...' used in an incorrect context
或者像這樣.Internal呼叫vapply:
bar <- function(X, FUN, FUN.VALUE, USE.NAMES) {
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}
bar(test_l, \(x) x^2, numeric(1), FALSE)
#Error in bar(test_l, function(x) x^2, numeric(1), FALSE) :
# '...' used in an incorrect context
這是有效的,因為...存在于呼叫范圍內:
baz <- function(X, FUN, FUN.VALUE, USE.NAMES, ...) {
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}
x <- baz(test_l, \(x) x^2, numeric(1), FALSE)
通過跳過前幾行vapply. 他們不是你的瓶頸。它可能有助于實作使用 Rcpp 重復呼叫的函式,vapply但只有通過使用 Rcpp 實作整個回圈才能實作真正的性能提升。對 R 閉包的呼叫很昂貴,并且您希望在具有多次迭代的回圈中避免它們。
uj5u.com熱心網友回復:
羅蘭的分析在這里是正確的。有一個 hack 可以讓你在全域環境中得到一個省略號,但它需要你傳遞給的函式vapply來獲取一個額外的未使用的引數:
`...` <- (function(...) get("..."))(y = 2)
所以現在你可以這樣做:
test <- rnorm(10)
test_l <- as.list(test)
.Internal(vapply(test_l, \(x, y) x^2, numeric(1), FALSE))
#> [1] 1.49370808 0.02969854 4.80764382 2.96895104 0.69506047 1.53488883
#> [7] 0.12566700 1.27180579 0.08399010 0.02366073
但是,不建議這樣做。正如 Roland 所說,盡管從閉包內部呼叫的開銷非常小.Internal,但這不會成為代碼中的速率限制因素。
如果我們測量它:
microbenchmark::microbenchmark(
hack = {`...` <- (function(...) get("..."))(y = 2);
.Internal(vapply(test_l, \(x, y) x^2, numeric(1), FALSE))},
standard = vapply(test_l, \(x) x^2, numeric(1), USE.NAMES = FALSE))
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> hack 9.0 9.3 9.690 9.6 9.8 17.8 100 a
#> standard 6.7 7.0 15.261 7.1 7.2 817.9 100 a
我們可以看到,雖然 hack 平均速度稍快(僅由于標準版本中偶爾出現例外值),但每次呼叫大約為 5 微秒,因此如果您將此例程稱為 1000,您可能會節省 5 毫秒次。當您考慮除錯這種方法的不透明性和難度時,這根本不值得。
使用reprex v2.0.2創建于 2022-11-08
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/529920.html
標籤:r
上一篇:將每日資料匯總為數周
