在闪亮的应用程序中使用 ggvis layer_histogram 会为空 data.frame 生成错误

Using ggvis layer_histogram in shiny app generates error for empty data.frame

我想在闪亮的应用程序中使用 ggvis 从一组可过滤的数据中绘制堆叠直方图。

当过滤器 return 为空 data.frame 时,我希望显示一个空图。

以下使用 "non-stacked" 直方图按预期工作:

server <- function(input, output, session) {

  library(shiny)
  library(ggvis)
  library(dplyr)

  data(diamonds, package = "ggplot2")

    diamonds_sub <- reactive({
      d <- diamonds
      if (input$CLARITY != "All") {
        d <- d %>% filter(clarity == input$CLARITY)
      }
      d <- as.data.frame(d)
      d
    })

    hist_standard <- reactive({
      diamonds_sub %>%
        filter(cut == "Ideal") %>%
        ggvis(x=~price) %>%
        layer_histograms()
    })

    hist_standard %>% bind_shiny("hist_standard")

}

ui <- shinyUI(
  fluidPage(
    titlePanel("Histogram test")
    ,sidebarLayout(
      sidebarPanel(
        selectInput("CLARITY", "Clarity"
                    ,c("All", "I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"
                       ,"Non-Existent Clarity")
        )
      )
      ,mainPanel(ggvisOutput("hist_standard"))
    )
  )
)

shinyApp(ui = ui, server = server) 

当我在应用程序中 select "Non-Existent Clarity" 时,我得到以下结果:

我的目标是使用以下代码在堆叠直方图中获得此行为:

server <- function(input, output, session) {

  library(shiny)
  library(ggvis)
  library(dplyr)

  data(diamonds, package = "ggplot2")

    diamonds_sub <- reactive({
      d <- diamonds
      if (input$CLARITY != "All") {
        d <- d %>% filter(clarity == input$CLARITY)
      }
      d <- as.data.frame(d)
      d
    })

    hist_stacked <- reactive({
      diamonds_sub %>%
        filter(cut == "Ideal") %>%
        ggvis(x=~price, prop("fill", ~color)) %>%
        group_by(color) %>%
        layer_histograms()
    })

    hist_stacked %>% bind_shiny("hist_stacked")
}

ui <- shinyUI(
  fluidPage(
    titlePanel("Histogram test")
    ,sidebarLayout(
      sidebarPanel(
        selectInput("CLARITY", "Clarity"
                    ,c("All", "I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"
                       ,"Non-Existent Clarity")
        )
      )
      ,mainPanel(ggvisOutput("hist_stacked"))
    )
  )
)

shinyApp(ui = ui, server = server)

虽然该应用程序将 运行 写入,当我尝试 select "Non-Existent Clarity" "stacked" 版本时,我的应用程序崩溃并显示以下错误和警告消息:

Listening on http://127.0.0.1:3062
Guessing width = 500 # range / 38
Error: Length of logical index vector must be 1 or 10, got: 0
Error: no applicable method for 'compute_stack' applied to an object of class "function"
Warning: Error in eval: invalid 'envir' argument of type 'closure'
Stack trace (innermost first):
    124: eval
    123: prop_value.prop_variable
    122: prop_value
    121: data_range
    120: <reactive>
    109: x
    108: value.reactive
    107: FUN
    106: lapply
    105: values
    104: drop_nulls
    103: concat
    102: data_range
    101: <reactive>
     90: old_domain
     89: expand_range
     88: <reactive>
     77: x
     76: value.reactive
     75: value
     74: data.frame
     73: <reactive>
     62: data_reactive
     61: as.vega
     60: session$sendCustomMessage
     59: observerFunc
      4: <Anonymous>
      3: do.call
      2: print.shiny.appobj
      1: <Promise>
Warning: Error in eval: invalid 'envir' argument of type 'closure'
Stack trace (innermost first):
    124: eval
    123: prop_value.prop_variable
    122: prop_value
    121: data_range
    120: <reactive>
    109: x
    108: value.reactive
    107: FUN
    106: lapply
    105: values
    104: drop_nulls
    103: concat
    102: data_range
    101: <reactive>
     90: old_domain
     89: expand_range
     88: <reactive>
     77: x
     76: value.reactive
     75: value
     74: data.frame
     73: <reactive>
     62: data_reactive
     61: as.vega
     60: session$sendCustomMessage
     59: observerFunc
      4: <Anonymous>
      3: do.call
      2: print.shiny.appobj
      1: <Promise>
Warning: Error in UseMethod: no applicable method for 'apply_props' applied to an object of class "function"
Stack trace (innermost first):
    74: apply_props
    73: <reactive>
    62: data_reactive
    61: as.vega
    60: session$sendCustomMessage
    59: observerFunc
     4: <Anonymous>
     3: do.call
     2: print.shiny.appobj
     1: <Promise>
Warning: Error in eval: invalid 'envir' argument of type 'closure'
Stack trace (innermost first):
    111: eval
    110: prop_value.prop_variable
    109: prop_value
    108: data_range
    107: <reactive>
     96: x
     95: value.reactive
     94: FUN
     93: lapply
     92: values
     91: drop_nulls
     90: concat
     89: data_range
     88: <reactive>
     77: x
     76: value.reactive
     75: value
     74: data.frame
     73: <reactive>
     62: data_reactive
     61: as.vega
     60: session$sendCustomMessage
     59: observerFunc
      4: <Anonymous>
      3: do.call
      2: print.shiny.appobj
      1: <Promise>
Warning: Error in UseMethod: no applicable method for 'apply_props' applied to an object of class "function"
Stack trace (innermost first):
    62: <Anonymous>
    61: stop
    60: data_table[[name]]
    59: observerFunc
     4: <Anonymous>
     3: do.call
     2: print.shiny.appobj
     1: <Promise>
ERROR: [on_request_read] connection reset by peer

问题:如何从堆叠直方图中获得与非堆叠直方图相同的 "blank plot" 行为?

这确实不是 hist_stacked 中(我认为)不良行为的解决方案,但它确实以一种骇人听闻的方式解决了我的问题...

从上面的 error/warning 输出中可以看出(特别是 Error: no applicable method for 'compute_stack' applied to an object of class "function"),似乎 hist_stacked 在被要求 "compute stacks" 为空 data.frame。由于 ggviz 本身会 error-out(即在评估达到 group_by 之前),我需要确定在我之前是否已过滤到空 data.frame永远开始进入 ggviz

我通过添加一个额外的反应函数 (diamonds_sub_dim) 来计算 data.frame

的维度来实现这一点
    diamonds_sub_dim <- reactive({
      d <- diamonds
      if (input$CLARITY != "All") {
        d <- d %>% filter(clarity == input$CLARITY)
      }
      d <- as.data.frame(d)
      dim(d)
    })

然后我在 hist_stacked 函数内的 if-else 语句中使用这个函数,如下所示。如果 diamonds_sub_dim()[1]==0,那么我绘制原始的未堆叠直方图。 data.frame 为空的事实将使我得到一个空的情节。否则,我会正常计算堆叠直方图。

server <- function(input, output, session) {

  library(shiny)
  library(ggvis)
  library(dplyr)

  data(diamonds, package = "ggplot2")

    diamonds_sub <- reactive({
      d <- diamonds
      if (input$CLARITY != "All") {
        d <- d %>% filter(clarity == input$CLARITY)
      }
      d <- as.data.frame(d)
      d
    })

    diamonds_sub_dim <- reactive({
      d <- diamonds
      if (input$CLARITY != "All") {
        d <- d %>% filter(clarity == input$CLARITY)
      }
      d <- as.data.frame(d)
      dim(d)
    })

    hist_stacked <- reactive({

      if (diamonds_sub_dim()[1]==0) {
        diamonds_sub() %>%
          filter(cut == "Ideal") %>%
          ggvis(x=~price) %>%
          layer_histograms()
      } else {
        diamonds_sub() %>%
          filter(cut == "Ideal") %>%
          ggvis(x=~price, prop("fill", ~color)) %>%
          group_by(color) %>%
          layer_histograms()
      }
    })
    hist_stacked %>% bind_shiny("hist_stacked")
}

ui <- shinyUI(
  fluidPage(
    titlePanel("Histogram test")
    ,sidebarLayout(
      sidebarPanel(
        selectInput("CLARITY", "Clarity"
                    ,c("All", "I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"
                       ,"Non-Existent Clarity")
        )
      )
      ,mainPanel(ggvisOutput("hist_stacked")
                 )
    )
  )
)

shinyApp(ui = ui, server = server)

如果有人有建议,我会很乐意接受更优雅的答案。