1

Hi fellow Shiny users,

I would like my users to be able to add new variables into a master data frame. The users will type the definition using textInput. Then we will add it to the data frame using server.R. Here's my code. I am not able to make it work. Any help will be greatly appreciated. Thank you!

Rawdata:

colA <- c('1','2','3','3','2')
colB <- c('1','1','3','3','2')
colC <- c('14','12','33','33','26')
colD <- c('Value','Mainstream','Value','Premium','Premium')
colE <- c(1,2,3,4,5)
rawdata <- as.data.frame(cbind(colA,colB, colC, colD, colE))
View(rawdata)

ui.R:

fluidPage(
            sidebarLayout(
                sidebarPanel(
                    textInput("addVar", "New attribute definition"),
                    helpText("Note: Type the attribute definition using R code."),
                    helpText("For example:"), 
                    helpText("data$Value <- ifelse (data$price_tiers_new == 'Value', 1, 0)"),

                    br(),
                    actionButton("addButton", strong("Add!")),
                    width = 3
                ),

                mainPanel(
                    verticalLayout(
                        br()
                        #Will display histogram of the newly added variables       
                    )
                )
           )
)

server.R:

function(input, output, session) {

    curr <- reactiveValues()
    curr$df <- rawdata

    observeEvent(input$addButton, {

        eval(parse(text=input$filter))

    })
}

For example, here are two new variable definitions to try. If we add the first definition, rawdata will have one additional column (Value). If we add the second definition, rawdata will have two additional columns (Value and Premium).

curr$df$Value <- ifelse(curr$df$colD == 'Value', 1, 0)
curr$df$Premium <- ifelse(curr$df$colD == 'Premium', 1, 0)
Ketty
  • 811
  • 10
  • 21

2 Answers2

1

Using eval(parse(text=input$addVar)) should work.

You could also add a default text for the textInput() to make the (unconventional but interesting) use of textInput() more clear.

textInput("addVar", "New attribute definition", 
          "curr$df$Value <- ifelse(curr$df$colD == 'Value', 1, 0)")

The complete app (including a textOutput to check the result) would read:

colA <- c('1','2','3','3','2')
colB <- c('1','1','3','3','2')
colC <- c('14','12','33','33','26')
colD <- c('Value','Mainstream','Value','Premium','Premium')
colE <- c(1,2,3,4,5)
rawdata <- as.data.frame(cbind(colA, colB, colC, colD, colE))

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      textInput("addVar", "New attribute definition", "curr$df$Value <- ifelse(curr$df$colD == 'Value', 1, 0)"),
      helpText("Note: Type the attribute definition using R code."),
      helpText("For example:"), 
      helpText("data$Value <- ifelse (data$price_tiers_new == 'Value', 1, 0)"),
      br(),
      actionButton("addButton", strong("Add!")),
      width = 3
    ),

    mainPanel(
      verticalLayout(
        br(),
        verbatimTextOutput("txt")
        #Will display histogram of the newly added variables       
      )
    )
  )
)

server <- function(input, output, session) {
  output$txt <- renderPrint(curr$df)
  curr <- reactiveValues()
  curr$df <- rawdata

  observeEvent(input$addButton, {
    eval(parse(text=input$addVar))
  })
}

shinyApp(ui, server)
Tonio Liebrand
  • 17,189
  • 4
  • 39
  • 59
  • Isn't `eval(parse(text=))` risky with it's scope being global? A mischief or mistake could, potentially, affect all the files in the current folder. For example, you could add the text `mt3 <<- mtcars` in the text box and you'll have mt3 waiting for you after you've quit the program. If it can create, it can delete or alter. Please see the "risks of `eval(parse())`" discussion on SO/SE – skoh Sep 06 '17 at 20:15
  • sry, i cant follow, which files do you mean? Also i would recommend using the `<<-` within shiny. But if you have a better solution you should add it! – Tonio Liebrand Sep 06 '17 at 20:30
  • 1. ('which files') the files that are available at the command line where the shiny app is executed; example (thanks @flodel) if a user put in `rm(list=ls())` in the textInput for new variable creation, it would remove all files. 2. I did not dissuade the use of `<<-` in shiny. 3. Having a better* solution is no prerequisite for pointing established risks of a solution (Stack Exchange Guidelines). *Better – skoh Sep 08 '17 at 14:51
  • (cont'd) ...is subjective. I assume you mean a solution without the stated risks; see alternate answer below. – skoh Sep 08 '17 at 15:19
1

Though there is an accepted answer to this question, it's worth noting that eval(parse()) has significant risks if not used carefully (for example: evaluating the incoming text as is. See SO discussion on specific dangers of eval(parse())).

A way around those risks is to mutate (pun unintended) the incoming text and eval(parse()) the mutated text. This way you get the intended results or an error, but almost never a disaster like running long codes with, say, T <- FALSE (thanks @flodel) or some other planted bug.

For example: input_vector and name_vector are the lists of new variable definition conditions and new variable names respectively.

input_vector <- list()
input_vector[[1]] <- "ifelse(am == 1, 'wohoo', 'io')"
input_vector[[2]] <- "vs == 1"
input_vector[[3]] <- "cyl >= 6" 
input_vector[[4]] <- "0"    
name_vector <- list()
name_vector[[1]] <- "automatic_"
name_vector[[2]] <- "VS_Equals_1_"
name_vector[[3]] <- "HighCylinder_"
name_vector[[4]] <- "Blank_"    
new_var_count <- 1:4

mutated_data <- reactive({
  mt %>% 
  {eval(parse(text=
  paste0("mutate_(.,", 
      paste0(name_vector, as.character(new_var_count),"= input_vector[[", 
      as.character(new_var_count), "]]", collapse= " , "),
      ")"
  )
))}
})
skoh
  • 576
  • 6
  • 9