【问题标题】:Function operator check if the first function argument is empty df函数运算符检查第一个函数参数是否为空 df
【发布时间】:2021-02-25 07:52:44
【问题描述】:

我有一个函数运算符来处理绘图错误:

library(ggplot2)
library(dplyr)

handle_plot_error <- function(f) {
    tryCatch(
        f,
        error = function(e) plot_error(e)
    )
}

plot_error <- function(error) {
    ggplot(
        tibble(text = str_wrap(error, 80)),
        aes(x = 1, y = 1, label = text)
    ) +
        geom_text(color = "red") +
        theme_void()
}

我还有一个创建情节方面的功能:

plot_facet <- function(df) { 
    df %>% 
        ggplot(aes(cyl, mpg)) + 
        geom_line() + 
        facet_grid(~am) 
} 

plot_facet 可能会收到一个空数据框作为输入:

handle_plot_error(plot_facet(tibble()))

我的函数无法处理此错误,因此我想改进 handle_plot_error 以包含以下内容:

if(nrow(df) == 0) { 
    stop("No data available")
}

然后将此错误消息传递给plot_error 函数。 我知道我可以在 plot_facet 中包含 stop 案例,但我更喜欢它在 handle_plot_error 中,因为我将它用于很多绘图功能。

也许这会有所帮助:https://adv-r.hadley.nz/function-operators.html

【问题讨论】:

  • 不确定我是否理解您的问题。 handle_plot_error 用作函数运算符,用于绘制函数并捕获可能的绘图错误并将它们显示在绘图上(我在 Shiny 上使用它)。
  • 我突然想到handle_plot_error 函数的主要目的是在闪亮的应用程序中显示有意义的错误消息。否则,您可能不需要在 ggplot2 图中打印错误。如果您的用例是一个闪亮的应用程序,那么您最好使用validate(need())。特别是如果您的 data.frame 是用户输入,您可以轻松检查 nrow(df)== 0 是否会在您的图中显示错误(并且所有下游反应都将停止)。

标签: r error-handling try-catch


【解决方案1】:

您的代码没有捕捉到错误,因为它仅在为打印而构建绘图时发生。如果您不想显式调用print,如handle_plot_error(print(plot_facet(tibble()))),则需要在tryCatch 中构建绘图。

handle_plot_error <- function(f) {
  tryCatch({
    ggplot_build(f)
    f
    },
    error = function(e) plot_error(e)
  )
}

这对于大而复杂的地块来说效率有点低。

编辑:

您可以轻松访问和测试 ggplot 对象的数据。

handle_plot_error <- function(f) {
  tryCatch({
    stopifnot("Data.frame is empty!" = nrow(f$data) > 0)
    ggplot_build(f)
    f
    },
    error = function(e) plot_error(e)
  )
}

【讨论】:

  • 谢谢!这可行,但我想知道是否可以检查 (if(nrow(df)) == 0) 函数 f 的第一个参数,该参数传递给 handle_plot_error,以便打印更直观的错误消息?
【解决方案2】:

您可以定义一个检查nrow(df) == 0 函数,然后将其包含在tryCatch 中。

您原来的handle_plot_error 函数还不是函数运算符,因为函数运算符将函数作为输入并返回函数作为输出。这就是为什么我更改了handle_plot_error,将function(…) {} 包裹在tryCatch 周围,使其成为类似purrr::possibly 的函数运算符。然后,您可以包装您想要处理的函数,然后调用 handle_plot_error(plot_facet)(mtcars) 中的参数,或者定义函数的已处理版本 try_plot_facet &lt;- handle_plot_error(plot_facet),然后可以以常规方式调用它 try_plot_facet(mtcars)

library(ggplot2)
library(dplyr)

check_df <- function(df) {
  if(nrow(df) == 0) { 
    rlang::abort("No data available") # `abort` has nicer error messages
  }
}

handle_plot_error <- function(f) {
  function(...){ 
  tryCatch({
    check_df(...)
    ggplot_build(f(...))
  },
  error = function(e) plot_error(e)
  )
  }
}

plot_error <- function(error) {
  plot <- ggplot(
    tibble(text = stringr::str_wrap(error, 80)),
    aes(x = 1, y = 1, label = text)
  ) +
    geom_text(color = "red") +
    theme_void()
  
  list(plot = plot)
}

plot_facet <- function(df) {
  df %>% 
    ggplot(aes(cyl, mpg)) + 
    geom_line() + 
    facet_grid(~am) 
} 

handle_plot_error(plot_facet)(tibble())
handle_plot_error(plot_facet)(mtcars)

如果plot_facet 中有多个参数,我们可以在错误处理函数中明确命名它们:

handle_plot_error <- function(f) {
  function(df, ...){ 
    tryCatch({
      check_df(df)
      ggplot_build(f(df, ...))
    },
    error = function(e) plot_error(e)
    )
  }
}

plot_facet <- function(df, var) {
  df %>% 
    ggplot(aes({{var}}, mpg)) + 
    geom_line() + 
    facet_grid(~am) 
} 

handle_plot_error(plot_facet)(tibble())
handle_plot_error(plot_facet)(mtcars, cyl)

【讨论】:

  • 我猜你在plot_facet 中不需要check_df() 那么?它适用于plot_facet 的多个参数吗?
  • @mihagazvoda:是的 check_df()plot_facet 不需要。我只是忘了删除它,现在更正了答案。
  • 抱歉,我注意到我的第二个问题有错字。 :D 如果plot_facet 有多个参数怎么办?
  • @mihagazvoda:这取决于参数的类型,是字符串还是裸变量名?也许你可以发布一个例子。我在这里的回答更多是为了指出“Advanced R”中定义的真实函数运算符的样子。正如 Roland 的回答所建议的那样,不使用函数运算符可能会更容易。
  • @mihagazvoda:查看我的更新答案,其中包含两个参数。
【解决方案3】:

正如@Roland 所说,错误发生在构建时。他的解决方案可能是最好的,因为它会捕获除零行之外的其他类型的错误。但是,如果您真的想为它们提供特殊的错误消息,您可以查看绘图表达式的结果并查看包含的 data 组件是否有零行。例如,

handle_plot_error <- function(f) {
  tryCatch({
    if (!inherits(f, "ggplot"))
       stop("You need to construct a plot.",
            call. = FALSE)
    if (nrow(f$data) == 0)
       stop("Dataset must have at least one row",
            call. = FALSE)
    f
    },
    error = function(e) plot_error(e)
  )
}

您可以通过在检查后调用ggplot_build 来实现这两种方法。

【讨论】:

    猜你喜欢
    • 2013-09-12
    • 1970-01-01
    • 2011-10-24
    • 2012-02-28
    • 2015-07-22
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多