【问题标题】:Add new columns to a DataFrame on shiny apps在闪亮的应用程序上向 DataFrame 添加新列
【发布时间】:2019-01-02 21:47:33
【问题描述】:

我正在使用闪亮的应用程序在 R 上开发一个简单的 ML 模型,应用程序的结构将是:

1) 从本地文件加载数据 2)用加载的数据训练模型 3) 绘制结果

我的问题处于第 2 阶段,我可以使用以下代码绘制输入数据:

  output$plot1 <- renderPlot({
    ggplot(mydata(), aes(x=LotArea, y=SalePrice)) + geom_point()
  })

但由于预测值不在原始DF中,我需要先添加它们。

我使用的代码是:

  obsB <- reactive({


    set.seed(0) 
    xgb_model = train(
      mydata()["LotArea"], as.vector(t(mydata()["SalePrice"])),  
      trControl = xgb_trcontrol,
      tuneGrid = xgbGrid,
      method = "xgbTree"
    )

    predicted = predict(xgb_model, mydata()["LotArea"])
    mydata()["predicted"] = predicted


  })

这是我得到的错误:

Warning: Error in FUN: object 'predicted' not found

当我将“LotArea”更改为“predicted”时会发生这种情况

  output$plot1 <- renderPlot({
    ggplot(mydata(), aes(x=predicted, y=SalePrice)) + geom_point()
  })

这是我拥有的完整代码:

library(shiny)
library(readxl)
library(tidyverse)
library(xgboost)
library(caret)
library(iml)


#### UI


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fileInput("file1", "Choose CSV File",
                accept = c(
                  "text/csv",
                  "text/comma-separated-values,text/plain",
                  ".csv")
      ),
      tags$hr(),
      checkboxInput("header", "Header", TRUE)
    ),
    mainPanel(
      #tableOutput("contents"),
      plotOutput("plot1", click = "plot_brush")
    )
  )
)

server <- function(input, output) {
  mydata <- reactive({
    req(input$file1, input$header, file.exists(input$file1$datapath))
    read.csv(input$file1$datapath, header = input$header)
  })


  output$contents <- renderTable({
    req(mydata())
    #mydata()
  })


  ### test
  xgb_trcontrol = trainControl(
    method = "cv",
    number = 5,  
    allowParallel = TRUE,
    verboseIter = FALSE,
    returnData = FALSE
  )


  #I am specifing the same parameters with the same values as I did for Python above. The hyperparameters to optimize are found in the website.
  xgbGrid <- expand.grid(nrounds = c(10,14),  # this is n_estimators in the python code above
                         max_depth = c(10, 15, 20, 25),
                         colsample_bytree = seq(0.5, 0.9, length.out = 5),
                         ## The values below are default values in the sklearn-api. 
                         eta = 0.1,
                         gamma=0,
                         min_child_weight = 1,
                         subsample = 1
  )




  obsB <- reactive({



    set.seed(0) 
    xgb_model = train(
      mydata()["LotArea"], as.vector(t(mydata()["SalePrice"])),  
      trControl = xgb_trcontrol,
      tuneGrid = xgbGrid,
      method = "xgbTree"
    )

    predicted = predict(xgb_model, mydata()["LotArea"])
    mydata()["predicted"] = predicted


  })

  output$plot1 <- renderPlot({
    ggplot(mydata(), aes(x=predicted, y=SalePrice)) + geom_point()
  })




  }

shinyApp(ui, server)

编辑:

我变了:

mydata()["predicted"] = predicted

为:

data =  mydata()
data["predicted"] = predicted

但知道我得到了一个不同的错误:

Warning: Error in : You're passing a function as global data.
Have you misspelled the `data` argument in `ggplot()

编辑 2:这是我正在使用的数据示例:

https://drive.google.com/file/d/1R8GA0fW0pOgG8Cpykc8mAThvKOCRCVl0/view?usp=sharing

【问题讨论】:

  • 这个predict(xgb_model, mydata()["LotArea"]) 是否返回正确的值?
  • @Parfait 实际上,我不确定。如果我使用反应函数,我将无法打印结果,如果我使用观察函数,那么我会在该行收到此错误:“警告:= 中的错误:赋值的无效(NULL)左侧”。
  • 尝试在闪亮之外进行测试。
  • 我认为问题在于 mydata() 不是数据框它是反应式表达,我认为你不能这样做。您可以在函数内部执行 data
  • mydata()["predicted"] 导致错误,因为在 mydata() 中没有名为 predicted 的列,并且您无法像在任何普通数据框中那样动态创建新列。因此,您可以将predicated 分配给现有列或尝试此answer

标签: r shiny


【解决方案1】:

您无法使用该语法更新反应式值。

你的问题是:

  • 如果您使用 value = reactive({...}) 创建响应式值,则无法在该代码块之外更改其值
  • 如果您希望能够在代码中的多个位置更改反应元素的值,您需要使用reactiveValreactiveValues 函数来创建变量。以这种方式创建的变量可以使用variableName(newValue) 语法进行更改。

例如

# we are somewhere inside the server code
mydata = reactiveVal()

observe({
    req(input$file1, input$header, file.exists(input$file1$datapath))
    data = read.csv(input$file1$datapath, header = input$header)
    mydata(data)
})

obsB <- reactive({


    set.seed(0) 
    xgb_model = train(
      mydata()["LotArea"], as.vector(t(mydata()["SalePrice"])),  
      trControl = xgb_trcontrol,
      tuneGrid = xgbGrid,
      method = "xgbTree"
    )

    predicted = predict(xgb_model, mydata()["LotArea"])
    newData = mydata()
    newData['predicted'] = predicted
    mydata(newData)
  })
  • 否则,您需要将更改 mydata 的所有内容合并到该单个代码块中。

请注意,我怀疑在上面的代码中可能存在一个循环,因为您更新了依赖于mydata 的代码块中的 mydata。由于我没有样本数据,因此无法对其进行测试,但您可能必须使用isolate 或使用另一个不是mydata 的触发器才能使其工作(例如mydata 的触发器)

附带说明,如果您的应用需要数据才能运行,那么您最好提供示例数据。我无法对此进行测试,因为我无法轻易猜出输入应该是什么样子。此外,最好使用专门为隔离问题的问题编写的新代码,而不是在此处粘贴您的实际项目,因为您可以摆脱数据和包依赖关系,并且不会出现与问题无关的干扰

由于您的代码与问题无关的其他问题,这里有一个带注释和固定的版本

library(shiny)
library(readxl)
library(tidyverse)
library(xgboost)
library(caret)
library(iml)


#### UI


ui <- fluidPage(
    sidebarLayout(
        sidebarPanel(
            fileInput("file1", "Choose CSV File",
                      accept = c(
                          "text/csv",
                          "text/comma-separated-values,text/plain",
                          ".csv")
            ),
            tags$hr(),
            checkboxInput("header", "Header", TRUE)
        ),
        mainPanel(
            #tableOutput("contents"),
            plotOutput("plot1", click = "plot_brush")
        )
    )
)

server <- function(input, output) {
    # create mydata as a reactiveVal so that it can be edited everywhere
    mydata = reactiveVal()

    # reactive block is changed with an observe that allows mydata to be updated
    # on change of data
    observe({
        req(input$file1, input$header, file.exists(input$file1$datapath))
        data = read.csv(input$file1$datapath, header = input$header)
        mydata(data)
    })


    output$contents <- renderTable({
        req(mydata())
        #mydata()
    })


    ### test
    xgb_trcontrol = trainControl(
        method = "cv",
        number = 5,
        allowParallel = TRUE,
        verboseIter = FALSE,
        returnData = FALSE
    )


    #I am specifing the same parameters with the same values as I did for Python above. The hyperparameters to optimize are found in the website.
    xgbGrid <- expand.grid(nrounds = c(10,14),  # this is n_estimators in the python code above
                           max_depth = c(10, 15, 20, 25),
                           colsample_bytree = seq(0.5, 0.9, length.out = 5),
                           ## The values below are default values in the sklearn-api.
                           eta = 0.1,
                           gamma=0,
                           min_child_weight = 1,
                           subsample = 1
    )



    # note that obsB reactive variable is gone. if you don't use a 
    # reactive variable, the code block will not be executed.
    # unlike observe blocks, reactive blocks are lazy and should
    # not be relied on for their side effects
    observe({
        # this if ensures you don't run this block before mydata isn't a data frame
        # also prevents it running after it updates mydata. otherwise this will
        # be executed twice. its an invisible problem that'll make it run half
        # as fast unless you debug.
        if ('data.frame' %in% class(mydata()) & !'predicted' %in% names(mydata())){
            set.seed(0)
            xgb_model = train(
                mydata()["LotArea"], as.vector(t(mydata()["SalePrice"])),
                trControl = xgb_trcontrol,
                tuneGrid = xgbGrid,
                method = "xgbTree"
            )

            predicted = predict(xgb_model, mydata()["LotArea"])
            data = mydata()
            data["predicted"] = predicted
            mydata(data)
        }





    })

    output$plot1 <- renderPlot({
        data = mydata()
        # this is here to prevent premature triggering of this ggplot.
        # otherwise you'll get the "object not found" error
        if('predicted' %in% names(data)){
            ggplot(mydata(), aes(x=predicted, y=SalePrice)) + geom_point()
        }
    })




}

shinyApp(ui, server)

【讨论】:

  • 我仍然遇到错误,我已经用数据样本更新了问题。
  • 这就是为什么我也更喜欢专注于这个问题的代码。您遇到的特定错误是因为您没有进行上述所有建议的更改。您需要通过mydata = reactiveVal() 创建mydata,将创建的原始块更改为observe 块。但是,如果您解决此问题,您会注意到它仍然无法正常工作,因为 1)绘制 ggplot 的代码直接依赖于 mydata,它会在您添加预测列之前尝试读取它,以及 2)因为 obsBreactive 代码块开始时从未执行过的任何地方都不会使用它。这是一个流量控制问题。
  • 将粘贴整个应用程序的工作版本。但它会超出主题
猜你喜欢
  • 2020-06-16
  • 2017-07-27
  • 2018-06-11
  • 1970-01-01
  • 2021-11-19
  • 2015-06-15
  • 2018-06-13
  • 2019-06-10
  • 2017-01-15
相关资源
最近更新 更多