Швидша альтернатива deparse ()


9

Я підтримую пакет, на який покладаються неодноразові дзвінки deparse(control = c("keepNA", "keepInteger")). controlзавжди однакова, і вираз змінюється. deparse()здається, витрачає багато часу на повторне тлумачення одного і того ж набору варіантів .deparseOpts().

microbenchmark::microbenchmark(
    a = deparse(identity, control = c("keepNA", "keepInteger")),
    b = .deparseOpts(c("keepNA", "keepInteger"))
)
# Unit: microseconds
# expr min  lq  mean median  uq  max neval
#    a 7.2 7.4 8.020    7.5 7.6 55.1   100
#    b 3.0 3.2 3.387    3.4 3.5  6.0   100

У деяких системах надлишкові .deparseOpts()дзвінки фактично займають більшу частину часу виконання deparse()( графік полум'я тут ).

Я дуже хотів би просто зателефонувати .deparseOpts()один раз, а потім надати цифровий код deparse(), але це видається неможливим без виклику .Internal()або виклику коду С безпосередньо, жоден з яких не є оптимальним з точки зору розробки пакету.

deparse
# function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
#     c("call", "expression", "(", "function"), 
#     control = c("keepNA", "keepInteger", "niceNames", 
#         "showAttributes"), nlines = -1L) 
# .Internal(deparse(expr, width.cutoff, backtick, .deparseOpts(control), 
#     nlines))
# <bytecode: 0x0000000006ac27b8>
# <environment: namespace:base>

Чи є зручне рішення?

Відповіді:


4

1) Визначте функцію, яка генерує копію deparse, середовище якої було скинуто, щоб знайти змінену версію .deparseOpts, яка була встановлена ​​рівною функції ідентичності. В Runми потім почати його використання , щоб створити deparse2і виконати це. Це дозволяє уникнути .Internalпрямого запуску .

make_deparse <- function() {
  .deparseOpts <- identity
  environment(deparse) <- environment()
  deparse
}

Run <- function() {
  deparse2 <- make_deparse()
  deparse2(identity, control = 65)
}

# test
Run()

2) Ще один спосіб зробити це - визначити функцію конструктора, яка створює середовище, в яке можна помістити модифіковану копію deparseта додати слід до цієї копії, переопределению .deparseOptsяк функцію ідентичності. Потім поверніть це середовище. Потім у нас є деяка функція, яка використовує її, і для цього прикладу ми створюємо функцію, Runщоб продемонструвати її, а потім просто виконати Run. Це дозволяє уникнути використання.Internal

make_deparse_env <- function() {
  e <- environment()
  deparse <- deparse
  suppressMessages(
    trace("deparse", quote(.deparseOpts <- identity), print = FALSE, where = e)
  )
  e
}

Run <- function() {
  e <- make_deparse_env()
  e$deparse(identity, control = 65)
}

# test
Run()

3) Третім підходом є переосмислення deparse, додавши новий аргумент, який встановлює .deparseOptsзначення за замовчуванням identityта встановлює значення control65 за замовчуванням 65.

make_deparse65 <- function() {
  deparse2 <- function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
    c("call", "expression", "(", "function"), 
    control = 65, nlines = -1L, .deparseOpts = identity) {}
  body(deparse2) <- body(deparse)
  deparse2
}

Run <- function() {
  deparse65 <- make_deparse65()
  deparse65(identity)
}

# test
Run()

Нічого собі, це так розумно !!! Я ніколи б не подумав про це! Дуже сподіваюся на тестування його на всіх моїх системах!
ландо

Коли я застосовую (1) і попередньо обчислюю backtickаргумент, відхилення відбувається в 6 разів швидше! Я йду з цим. Дякую за вирішення!
ландо

Гм ... мабуть, R CMD checkвиявляє .Internal()виклик у функціях, вироблених (1). Досить легко обійтися, мені просто потрібно make_deparse()(expr, control = 64, backtick = TRUE). Дуже реконструювати департер щоразу, коли я його використовую, але це все ще набагато швидше, ніж наївний, яким deparse()я користувався раніше.
ландо

Не для мене. Я спробував створити пакет із лише функціями make_deparseта Runфункціями в (1), пробіг R CMD buildі R CMD check --as-cranпід, "R version 3.6.1 Patched (2019-11-18 r77437)"і це не скаржилось, і мені не потрібно було ніяких обхідних шляхів. Ви впевнені, що не робите чогось іншого або крім того, що це спричиняє?
Г. Гротендієк

1
Це було викликано вашим кодом , що визначає його на рівні верхньої частини: direct_deparse <- make_direct_deparse(). Код, показаний у відповіді, обережно не робив цього і визначав його лише у межах функції, тобто в межах Run.
Г. Гротендієк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.