захватывать предупреждения с помощью capture.output

У меня проблемы с использованием capture.output(), и я не могу понять, почему, так как по большей части это просто оболочка для sink().

Рассмотрим этот тривиальный пример с использованием sink():

foo = function() warning("foo")

f = file()
sink(f, type = "message")
foo()
readLines(f)
## [1] "Warning message:" "In foo() : foo"  
close(f)

Это работает, как и ожидалось. однако capture.output() нет:

f = file()
capture.output(foo(), file = f, type = "message")
## Warning message:
## In foo() : foo
readLines(f)
## character(0)
close(f)

capture.output() работает для сообщений:

bar = function() message("bar")
f = file()
capture.output(bar(), file = f, type = "message")
readLines(f)
## [1] "bar"
close(f)

Но согласно документации должны быть захвачены как сообщения, так и предупреждения:

Сообщения, отправленные на stderr() (включая сообщения от message, warning и stop), перехватываются type = "message".

Что я здесь упускаю?


person mikeck    schedule 25.04.2020    source источник
comment
См. r.789695.n4.nabble.com / и r.789695. n4.nabble.com/. Проблема в том, что предупреждения не выводятся до тех пор, пока не вернется функция верхнего уровня. С sink() это не проблема, потому что после этого вы вызываете foo() самостоятельно. Но с capture.output() функция foo() вызывается внутри оболочки, поэтому предупреждения не печатаются до тех пор, пока capture.output() не вернется, и в этот момент перенаправление будет закрыто.   -  person MrFlick    schedule 25.04.2020
comment
Спасибо @MrFlick! Это объясняет проблему, а также указывает на возможное решение: использование аргумента immediate = TRUE для немедленной печати предупреждения, а не ожидания возврата функции верхнего уровня.   -  person mikeck    schedule 25.04.2020


Ответы (1)


Комментарий @MrFlick указывает на потенциальное решение, если у вас есть контроль над аргументами, которые передаются warning(). Если вы используете аргумент immediate. = TRUE, то capture.output() может получить предупреждающее сообщение.

baz = function() warning("baz", immediate. = TRUE)
res = capture.output(baz(), type = "message")
print(res)
## [1] "Warning in baz() : baz"

ИЗМЕНИТЬ

В качестве альтернативы, @user2554330 указывает, что вы можете использовать options(warn = 1) для немедленной глобальной печати предупреждений.

oldopt = getOption("warn")
options(warn = 1)
res = capture.output(foo(), type = "message")
print(res)
## [1] "Warning in foo() : foo"
options(warn = oldopt)

ИЗМЕНИТЬ 2

Для полноты картины я думаю, что полезно указать на этот альтернативный подход с использованием withCallingHandlers, который не требует никаких изменений в параметрах и может быть более чистым решением в зависимости от приложения. Рассмотрим этот пример вложенных предупреждений:

foo = function() {
  warning("foo")
  bar()
}
bar = function() { 
  warning("bar")
  baz()
}
baz = function() {
  warning("baz")
  TRUE
}
# create a container to hold warning messages
logs = vector("character")
# function to capture warning messages
log_fun = function(w) logs <<- append(logs, w$message)
# function call with message capturing
withCallingHandlers(foo(), warning = log_fun)
## [1] TRUE
## Warning messages:
## 1: In foo() : foo
## 2: In bar() : bar
## 3: In baz() : baz
print(logs)
## [1] "foo" "bar" "baz"

Обратите внимание, что withCallingHandlers позволяет указать различное поведение для разных условий сигнала, например. warnings и messages можно хранить в отдельных переменных.

person mikeck    schedule 25.04.2020
comment
Вы можете установить options(warn = 1) для немедленной печати предупреждений. - person user2554330; 25.04.2020