R Блестящий: переключение между панелями вкладок приводит к ошибкам

#r #shiny #tabpanel

#r #блестящий #панель вкладок

Вопрос:

Я создал приложение, которое будет использовать модель случайного леса для прогнозирования типа видов в наборе данных Iris. Идея состоит в том, что пользователь может выбрать значение для других input widgets переменных, которые затем использует модель для прогнозирования. Все это работает нормально.

Недавно я решил внедрить журнал, содержащий различные входные данные, временную метку и оценку. Я поместил этот журнал в другой tabPanel , чтобы дать лучший обзор. Все работает нормально, когда я нажимаю кнопку сохранения, входные данные, временная метка и оценка сохраняются в журнале, однако, когда я возвращаюсь к оригиналу tabPanel («Калькулятор»), появляются ошибки, говорящие о том, что количество столбцов не совпадает (или что-то в этом роде, я перевел это с датского).

Кто-нибудь знает, почему возникает эта проблема и как ее исправить?

У меня также возникли проблемы с запуском приложения с помощью кнопки «Запустить приложение» в R. Он отлично работает, когда я выбираю все с помощью ctrl A и нажимаю ctrl enter для запуска кода.

Вот мой код:

 require(shiny)
require(tidyverse)
require(shinythemes)
require(data.table)
require(RCurl)
require(randomForest)
require(mlbench)
require(janitor)
require(caret)
require(recipes)
require(rsconnect)


# Read data
DATA <- datasets::iris

# Rearrange data so the response variable is located in column 1
DATA <- DATA[,c(names(DATA)[5],names(DATA)[-5])]

# Creating a model
model <- randomForest(DATA$Species ~ ., data = DATA, ntree = 500, mtry = 3, importance = TRUE)


.# UI -------------------------------------------------------------------------
ui <- fluidPage(
  navbarPage(title = "Dynamic Calculator",
               
    tabPanel("Calculator", 
  
            sidebarPanel(
              
              h3("Values Selected"),
              br(),
              tableOutput('show_inputs'),
              hr(),
              actionButton("submitbutton", label = "calculate", class = "btn btn-primary", icon("calculator")),
              actionButton("savebutton", label = "Save", icon("save")),
              hr(),
              tableOutput("tabledata")
              
            ), # End sidebarPanel
            
            mainPanel(
              
              h3("Variables"),
              uiOutput("select")
            ) # End mainPanel
              ), # End tabPanel Calculator

  tabPanel("Log",
           br(),
           DT::dataTableOutput("datatable15", width = 300), 
           ) # End tabPanel "Log"
  ) # End tabsetPanel
) # End UI bracket


# Server -------------------------------------------------------------------------
server <- function(input, output, session) {
  
  # Create input widgets from dataset  
  output$select <- renderUI({
    df <- req(DATA)
    tagList(map(
      names(df[-1]),
      ~ ifelse(is.numeric(df[[.]]),
               yes = tagList(sliderInput(
                 inputId = paste0(.),
                 label = .,
                 value = mean(df[[.]], na.rm = TRUE),
                 min = round(min(df[[.]], na.rm = TRUE),2),
                 max = round(max(df[[.]], na.rm = TRUE),2)
               )),
               no = tagList(selectInput(
                 inputId = paste0(.),
                 label = .,
                 choices = sort(unique(df[[.]])),
                 selected = sort(unique(df[[.]]))[1],
               ))
      ) # End ifelse
    )) # End tagList
  })
  
  
  # creating dataframe of selected values to be displayed
  AllInputs <- reactive({
    id_exclude <- c("savebutton","submitbutton")
    id_include <- setdiff(names(input), id_exclude)
    
    if (length(id_include) > 0) {
      myvalues <- NULL
      for(i in id_include) {
        myvalues <- as.data.frame(rbind(myvalues, cbind(i, input[[i]])))
        
      }
      names(myvalues) <- c("Variable", "Selected Value")
      myvalues %>% 
        slice(match(names(DATA[,-1]), Variable))
    }
  })
  
  
  # render table of selected values to be displayed
  output$show_inputs <- renderTable({
    AllInputs()
  })
  
  
  # Creating a dataframe for calculating a prediction
  datasetInput <- reactive({  
    
    df1 <- data.frame(AllInputs(), stringsAsFactors = FALSE)
    input <- transpose(rbind(df1, names(DATA[1])))
    
    write.table(input,"input.csv", sep=",", quote = FALSE, row.names = FALSE, col.names = FALSE)
    test <- read.csv(paste("input.csv", sep=""), header = TRUE)
    
    
  # Defining factor levels for factor variables
    cnames <- colnames(DATA[sapply(DATA,class)=="factor"])
    if (length(cnames)>0){
      lapply(cnames, function(par) {
        test[par] <<- factor(test[par], levels = unique(DATA[,par]))
      })
    }
    
  # Making the actual prediction and store it in a data.frame     
    Prediction <- predict(model,test)
    Output <- data.frame("Prediction"=Prediction)
    print(format(Output, nsmall=2, big.mark=","))
    
    
    
  })
  
  # display the prediction when the submit button is pressed
  output$tabledata <- renderTable({
    if (input$submitbutton>0) { 
      isolate(datasetInput()) 
    } 
  })

# -------------------------------------------------------------------------
  
  # Create the Log 
  saveData <- function(data) {
    data <- as.data.frame(t(data))
    if (exists("datatable15")) {
      datatable15 <<- rbind(datatable15, data)
    } else {
      datatable15 <<- data
    }
  }
  
  loadData <- function() {
    if (exists("datatable15")) {
      datatable15
    }
  }
  
  # Whenever a field is filled, aggregate all form data
  formData <- reactive({
    fields <- c(colnames(DATA[,-1]), "Timestamp", "Prediction")
    data <- sapply(fields, function(x) input[[x]])
    data$Timestamp <- as.character(Sys.time())
    data$Prediction <- as.character(datasetInput())
    data
  })
  
  # When the Submit button is clicked, save the form data
  observeEvent(input$savebutton, {
    saveData(formData())
  })
  
  # Show the previous responses
  # (update with current response when Submit is clicked)
  output$datatable15 <- DT::renderDataTable({
    input$savebutton
    loadData()
  })
  
} # End server bracket

# ShinyApp -------------------------------------------------------------------------
shinyApp(ui, server)

 

Комментарии:

1. Я не вижу, где определен объект fields (строка 163)

2. Вы правы, мне каким-то образом удалось удалить эту строку кода, но каким-то образом приложение все равно смогло запуститься. Я отредактировал приведенный выше код, чтобы теперь включить объект fields.

Ответ №1:

При создании вашего реактивного AllInputs , вы делаете цикл на id_include. Проблема в том, что все input[[i]] они не имеют длины 1: они могут быть NULL или иметь длину более одного. Вы не можете использовать cbind для двух переменных разной длины, что приводит к ошибке.

Поэтому я добавил условие перед вычислением myvalues, и все работает нормально :

   # creating dataframe of selected values to be displayed
  AllInputs <- reactive({
    id_exclude <- c("savebutton","submitbutton")
    id_include <- setdiff(names(input), id_exclude)
    if (length(id_include) > 0) {
      myvalues <- NULL
      for(i in id_include) {
        if(!is.null(input[[i]]) amp; length(input[[i]] == 1)){
          myvalues <- as.data.frame(rbind(myvalues, cbind(i, input[[i]])))
        }
      }
      names(myvalues) <- c("Variable", "Selected Value")
      myvalues %>% 
        slice(match(names(DATA[,-1]), Variable))
    }
  })
 

Кстати, поскольку циклы не являются хорошей практикой в R, вы можете захотеть взглянуть на apply функции семейства.

Комментарии:

1. Большое спасибо! Я слышал, как несколько человек говорили, что for loops это не очень хорошая практика в R, но я так и не получил ответа на вопрос, почему это так. Это потому, что они не учитывают определенные условия? как тот, который вы указали в приведенном выше коде? Я все еще новичок в R и Shiny, поэтому я все еще пытаюсь расширить свои знания об основах.

2. Есть много статей о том, почему циклы for не подходят для R, например privefl.github.io/blog/why-loops-are-slow-in-r