Проверьте, рисует ли функция что-то

Мы работаем с function, который может нарисовать сюжет или нет.
Я ищу решение, чтобы проверить, есть ли у функции побочный эффект рисования. Я надеюсь, что есть какое-то dev.* решение, чтобы проверить это.
inherits можно использовать только для решений, которые возвращают повторно используемые объекты, такие как ggplot2. С другой стороны, boxplot возвращает list и plot класс NULL.
Я ожидаю точно проверить dev.
Предоставляется обширный список различной графики и неграфики.

input_plots <- list(
  function() print(ggplot2::qplot(1)),
  function() lattice::densityplot(1),
  function() grid::grid.draw(ggplotify::as.grob(lattice::densityplot(1))),
  function() plot(1),
  function() boxplot(2),
  function() hist(1)
)

input_noplots <- list(
  function() list(),
  function() NULL,
  function() 2,
  function() NA
)

# We are working with a function which could draw a plot or not
all(vapply(input_plots, is.function, FUN.VALUE = logical(1)))
#> [1] TRUE
all(vapply(input_noplots, is.function, FUN.VALUE = logical(1)))
#> [1] TRUE

# all input_plots should be TRUE for is_draw
# all input_noplots should be FALSE for is_draw
is_draw <- function(fun){
  # inherits works only for functions returning proper instances
  # you can call a function fun()
  ...
  # return logical if the fun draw a plot
}

# all(vapply(input_plots, is_draw, FUN.VALUE = logical(1)))
# TRUE
# all(vapply(input_noplots, Negate(is_draw), FUN.VALUE = logical(1)))
# TRUE

Created on 2022-11-29 with reprex v2.0.2

ПРОВЕРИТЬ РЕШЕНИЕ:

# all input_plots should be TRUE for is_draw
# all input_noplots should be FALSE for is_draw

# this function will clear your device
is_draw <- function(f) {
  try(dev.off(), silent = TRUE)
  # graphics.off() # close any current graphics devices
  cdev <- dev.cur()
  f()
  if (cdev != dev.cur()) {
    on.exit(dev.off())
    return(TRUE)
  }
  return(FALSE)
}

all(vapply(input_plots, is_draw, FUN.VALUE = logical(1)))
#> Warning: `qplot()` was deprecated in ggplot2 3.4.0.
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> [1] TRUE
# TRUE
all(vapply(input_noplots, Negate(is_draw), FUN.VALUE = logical(1)))
#> [1] TRUE
# TRUE

plot(1)
all(vapply(input_plots, is_draw, FUN.VALUE = logical(1)))
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> [1] TRUE
# TRUE
all(vapply(input_noplots, Negate(is_draw), FUN.VALUE = logical(1)))
#> [1] TRUE
# TRUE

Created on 2022-11-29 with reprex v2.0.2

На самом деле нет способа надежно сделать это, не вызывая функцию или что-то в этом роде. Я имею в виду, что даже hist() можно назвать с помощью hist(plot=TRUE) или hist(plot=FALSE). А функции ggplot на самом деле ничего не рисуют. Это метод print() для объекта ggplot, который взаимодействует с графическим устройством. Так что ваш первый input_plots действительно должен быть ЛОЖНЫМ. Вы согласны с запуском функции для наблюдения за возможными побочными эффектами? Или вы пытаетесь это выяснить, не запуская функцию?

MrFlick 29.11.2022 15:39

Мы можем вызывать каждую функцию внутри. Да, с ggplot мы должны его распечатать, я обновил вопрос. Я также добавил grid::grid.draw в grob.

polkas 29.11.2022 15:42

Важным замечанием является то, что это НЕ будет работать для блестящих reactive для базовых графиков (не подлежащих повторному использованию) и их второго и следующего вызовов. Только первый реактивный вызов базовых графиков будет отображать что-либо.

polkas 29.11.2022 16:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
73
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Пока у вас в настоящее время нет открытого графического устройства, один из способов — проверить, изменилось ли текущее устройство.

is_draw <- function(f) {
  graphics.off() # close any current graphics devices
  cdev <- dev.cur()
  f()
  if (cdev != dev.cur()) {
    on.exit(dev.off())
    return(TRUE)
  }
  return(FALSE)
}

Обратите внимание, что это зависит от того, сможете ли вы оценить функцию. Это возвращает FALSE для вашего примера lattice, потому что, как и ggplot, эти графики отображаются только при вызове метода print().

Этот метод также закрывает графическое устройство, поэтому сюжет теряется, но вам нужно закрыть его, чтобы увидеть, откроется ли новое.

Загвоздка в том, что этот метод работает только до тех пор, пока у вас не открыто графическое устройство в начале. В противном случае функция построения графика не создаст новое графическое устройство, а заменит последнее.

MrFlick 29.11.2022 16:13

Я сделал небольшое обновление этой функции try(dev.off(), silent = TRUE) в начале. Пожалуйста, подумайте о том, чтобы добавить его и сюда. Большое спасибо.

polkas 29.11.2022 16:26

@polkas Я изменил его на graphics.off(), который закроет все устройства, если открыто более одного.

MrFlick 29.11.2022 16:35

Еще одно спасибо за ваше время и отличное решение.

polkas 29.11.2022 16:41

Одна вещь, на которую следует обратить внимание: любые вызовы par() откроют устройство, даже если функция в конечном итоге ничего не отрисует.

Deepayan Sarkar 06.12.2022 12:03

Кажется, это работает на Rgui в Windows. Вы можете проверить, работает ли он в вашей среде. Запустите все графические устройства с помощью dev.off, а затем после запуска кода проверьте длину dev.list() .

for(d in dev.list()) dev.off()

x <- 3 # does not plot
length(dev.list())
## [1] 0

plot(0)  # plots
length(dev.list())
## [1] 1

Спасибо за ваше время, но, на мой взгляд, второе решение кажется более подходящим.

polkas 29.11.2022 16:32

Альтернативный подход с использованием recordPlot():

is_draw <- function(f) { 
    plot(rnorm(10))
    pre <- recordPlot()
    f() 
    post <- recordPlot()
    !identical(pre, post) 
}

Спасибо, интересное решение. Я не думаю, что вы намеренно оставляете это plot(rnorm(10)) или это альтернатива dev.off. Кажется, что функция работает так же без этого вызова сюжета. К сожалению, решение не работает для lattice::densityplot(1).

polkas 06.12.2022 13:32

Это нужно для того, чтобы убедиться, что мы начинаем со случайного состояния. В противном случае последовательный вызов одного и того же f() должен давать TRUE только для первого вызова и FALSE для остальных (на практике это происходит не всегда, но я не проверял, почему). Кроме того, без какого-либо предыдущего графика первый вызов recordPlot() может привести к ошибке.

Deepayan Sarkar 07.12.2022 08:13

И он не должен работать с lattice::densityplot(1), или ggplot2::qplot(1) и т. д., потому что эти вызовы ничего не строят. Они будут отображаться только при печати, поэтому вам нужно print(lattice::densityplot(1)) и т. д. Я думаю, что другие указывали на это ранее. То же самое с hist(plot=TRUE) и hist(plot=FALSE).

Deepayan Sarkar 07.12.2022 08:16

Другие вопросы по теме