在分配reactiveValues()时创建了空数据帧

时间:2015-09-18 09:51:22

标签: r shiny reactive-programming

可再现的例子:

server.R

library(shiny)

shinyServer( function(input, output, session) {

    myDf <- reactiveValues(myData = NULL)

    problematicDf <- reactiveValues(test = NULL)

    observeEvent(input$myButton, {
            myDf$myData <- df
    })


    observe({
            output$myTestUi <- renderUI({
                    selectInput(inputId = 'mySelection', 
                                label = 'Selection', 
                                choices = levels(myDf$myData$z),
                                multiple = T,
                                selected = c(levels(myDf$myData$z)[1])
                    )
            })
    })


    observe({
            problematicDf$test <- subset(myDf$myData, ((myDf$myData$z %in% input$mySelection)))
    })


    observe({
            str(problematicDf$test)
    })

    observe({
            as.matrix(x = problematicDf$test)
    })
})

ui.R

library(shiny)

shinyUI( bootstrapPage( 

    h3("Push the button"),
    actionButton(inputId = "myButton",
                 label = "clickMe"),

    h4("Split Merkmal"),
    uiOutput("myTestUi")

))

global.R

df <- data.frame(x = 1:10, y = 10:1, z = letters[1:10])
df$z <- as.factor(df$z)

这给了我:

NULL
[1] "NULL"
NULL
Warning: Unhandled error in observer: 'data' must be of a vector type, was 'NULL'
observe({
    as.matrix(x = problematicDf$test)
})

仅查看

的输出
observe({
  str(problematicDf$test)
  print(class(problematicDf$test))
  print(problematicDf$test$z)
})

点击action Button后,在没有as.matrix的情况下,我得到:

NULL
[1] "NULL"
NULL
'data.frame':   0 obs. of  3 variables:
 $ x: int 
 $ y: int 
 $ z: Factor w/ 10 levels "a","b","c","d",..: 
[1] "data.frame"
factor(0)
Levels: a b c d e f g h i j
'data.frame':   1 obs. of  3 variables:
 $ x: int 1
 $ y: int 10
 $ z: Factor w/ 10 levels "a","b","c","d",..: 1
[1] "data.frame"
[1] a
Levels: a b c d e f g h i j

这是有问题的。正如您所看到的,它首先创建了一个df,它是一个空的,占位符为class = NULL。然后,它填补了这一点。但是,等待创建reactive functions的其他problematicDf$test似乎很快就会创建 ,因为空df已创建class = NULL })。他们之后不再更新。它们仅在进行其他选择时更新。 这导致(在我的情况下)程序崩溃,因为我需要继续使用如此创建的data.frame进行工作和子集等。

如何处理?! 我可以添加if else并检查class = NULL。但在我看来,这是一种不雅的方式。

1 个答案:

答案 0 :(得分:1)

您是否有理由将myDf初始化为NULL?如果没有,您可以轻松解决在初始化时分配df的问题。如果这样做,您的代码也不需要按钮。

您不应在renderUI内声明observe。每当您碰到所选项目时,observe都会被激活,它会将您的selectInput设置为初始状态(仅选择项目'a')。

修改1

#server.R

shinyServer( function(input, output, session) {
   # initialize myDf$myData with df and problematicDf as NULL
   myDf <- reactiveValues(myData = df)
   problematicDf <- reactiveValues(test = NULL)

   # you don't have to observe here. once the selectInput 
   # has been created, you can (un)select how many choices you want
   output$myTestUi <- renderUI({
       selectInput(inputId = 'myTestUi', 
                   label = 'Selection', 
                   choices = levels(myDf$myData$z),
                   multiple = T,
                   # this line determines that ONLY the first item on 
                   # c(levels(myDf$myData$z)[1] is selected on the selectInput initialization!
                   selected = c(levels(myDf$myData$z)[1])
       )
   })

   observe({
       problematicDf$test <- subset(myDf$myData, 
                             (myDf$myData$z %in% input$myTestUi))
   })

   observeEvent(input$myButton, {
       # the code in here will only run when the button is pushed!
       print("you should only put things here that you want to be flushed on the button being clicked!")
   })

})

修改2

所以请将myDf$myData初始化为NULL并在observeEvent内分配df。

renderUI将以selectInput开头,没有任何选择,但只要按下该按钮,就会为其分配选项。

您提到了一些reactive函数,这些函数在数据仍为NULL时崩溃。有很多方法可以解决这个问题。一种方法是使用你提到的if(is.null(myDf$myData)) return(NULL)。可能不是最优雅的方式,但通常工作得很好。其他方式可能包括isolateobserveEvent(默认情况下它不会对NULL输入做出反应)。这取决于您的应用程序的整体工作方式。

但似乎你还没有完全理解反应性和其他闪亮元素是如何起作用的。尝试阅读rstudio文章,特别是“反应式编程”部分(http://shiny.rstudio.com/articles/)。