Search code examples
rshinyshinydashboardshiny-server

Rewrite variable in reactive output


I have a problem with rewrite variable in reactive output. In my code df1() is uploaded main dataset. In modified_df1 i changed data type of variable based on user select. My problem is, in final dataset modified_df1 a I get only new column var (because I use cbind(df1(), var)). Is there some way, how I can rewrite previous column, no add new with cbind()? I pase server code here, thanks.

  df1 <- reactive({
      req(input$file1)
      df <- read.csv(input$file1$datapath,
                     header = input$header,
                    sep = input$sep,
                     quote = input$quote)
     df
     
   })
   
   
   colnames <- reactive({ names(df1()) })
   
   observeEvent(df1(), {
     
     updateCheckboxGroupInput(session, "class_var",
                              label = "Select Columns",
                              choices = colnames(),
                              selected = "") 
     
   })
   
   ## update when selection changes
   
   # storing the selected variable from the variables list table 
   table.sel <- reactive({
     df1()[,which(colnames(df1()) == col.name()[input$class_var,1])]
   })
   

   
  modified_df1 = eventReactive(input$chg_class,{
    if( input$choose_class == "Numeric"){
       var <- as.numeric(df1()[, input$class_var])
     } else if(input$choose_class == "Factor"){ message("get new vars");
       var <- as.factor(df1()[, input$class_var])
     } else if( input$choose_class == "Character"){
       var <- as.character(df1()[, input$class_var])
     } else if( input$choose_class == "Date"){
       var <- as.Date(df1()[, input$class_var])
     } 
    
    df2 = cbind(df1(), var)
    
   })

Solution

  • Since we are selecting multiple columns, we can use dplyr::across to loop through them. For example if you want to change the columns selected to a character:

    var <- df1() %>% mutate(across(all_of(input$class_var), ~as.character(.)))
    

    I made an app to ilustrate better what's happening.

    library(shiny)
    library(tidyverse)
    
    write_csv(iris, file = "iris.csv")
    
    # ui ----------------------------------------------------------------------
    
    
    ui <- fluidPage(
      fluidRow(
        column(
          width = 6,
          fileInput(
            "file1",
            "Upload File",
            multiple = FALSE,
            accept = NULL,
            width = NULL,
            buttonLabel = "Browse...",
            placeholder = "No file selected"
          ),
          checkboxGroupInput("class_var", "Select Columns", choices = "", selected = ""),
          radioButtons("choose_class", "Select Columns", choices = c("Numeric", "Factor", "Character", "Date")),
          actionButton("chg_class", "Change Class")
        ),
        column(
          width = 6,
          tableOutput("table_df")
        )
      )
    )
    
    # server ------------------------------------------------------------------
    
    
    server <- function(input, output, session) {
      df1 <- reactive({
        req(input$file1)
        read.csv(input$file1$datapath)
      })
    
    
    
      colnames <- reactive({
        names(df1())
      })
    
      observeEvent(df1(), {
        updateCheckboxGroupInput(session,
          "class_var",
          label = "Select Columns",
          choices = colnames(),
          selected = ""
        )
      })
    
    
      ## update when selection changes
    
      # storing the selected variable from the variables list table
      table.sel <- reactive({
        df1()[, which(colnames(df1()) == col.name()[input$class_var, 1])]
      })
    
    
    
      modified_df1 <- eventReactive(input$chg_class, {
        if (input$choose_class == "Numeric") {
          var <- df1() %>% mutate(across(all_of(input$class_var), ~ as.numeric(.)))
        } else if (input$choose_class == "Factor") {
          message("get new vars")
          var <- df1() %>% mutate(across(all_of(input$class_var), ~ as.factor(.)))
        } else if (input$choose_class == "Character") {
          var <- df1() %>% mutate(across(all_of(input$class_var), ~ as.character(.)))
        } else if (input$choose_class == "Date") {
          var <- df1() %>% mutate(across(all_of(input$class_var), ~ as.Date(.)))
        }
    
        var
      })
    
    
    
      output$table_df <- renderTable({
        modified_df1()
      })
    }
    
    shinyApp(ui, server)
    

    Note that if you try to convert to Date a character vector an error will occur.

    enter image description here