1

I am building a Shiny app that would take in numeric data (in a "handsontable" format) and make a boxplot out of the entered data. I would like to have an option to change column names (as "textInput") of the resulting "handsontable" and pass those names to the plot's "x" axis labels. While the code below works fine for changing names in the table I could not find a solution to pass those updated column names ("colHeaders") to the plot. I would very much appreciate a piece of advice. Many thanks! edit: the app also calculates column means and saves PNG but that is unlikely to be related to my problem

library(shiny)
library(rhandsontable)
library(dplyr)

#UI
ui <- shinyUI(fluidPage(
  titlePanel("Boxplot builder"),
   textOutput('result'),
     sidebarLayout(
        sidebarPanel(
            wellPanel(
               plotOutput("plot")),

            wellPanel(
               h3("Build BoxPlot"), 
                   actionButton("build", "Build")),   

            wellPanel(
               h3("Export Plot"),
                   downloadButton('ExportPlot', 'Export as png')), 
                         ),

            mainPanel(textInput("colnames", "Enter Column Names (separated by comma)", 
                  value="", placeholder = "e.g. Var1, Var2, etc"),
                  h5(tags$b("Enter data")),
                         rHandsontableOutput('table'))
                                  )))

rowNames <- c(sprintf("[%d]",seq(1:20)), "Means")
 defaultDF <- data.frame(
 row.names = rowNames,
 A = rep(NA_integer_, nrow=21),
 B = rep(NA_integer_, 21),
 C = rep(NA_integer_, 21),
  stringsAsFactors = FALSE)

#Server
server <- function(input, output, session)
 ({
  values <- reactiveValues(data = defaultDF) 

 observe({
  req(input$table)
  DF <- hot_to_r(input$table)
  DF[setdiff(rowNames, "Means"),]
  DF["Means",] <- colMeans(DF[setdiff(rowNames, "Means"),], na.rm = TRUE)
  values$data <- DF
})

output$table <- renderRHandsontable({
  req(values$data)
  rhandsontable(values$data, rowHeaderWidth = 100, 
  colHeaders=str_trim(unlist(strsplit(input$colnames,","))),) %>%
    hot_row(nrow(values$data), readOnly = TRUE)
}) 

observeEvent(input$build, {      
  output$plot <- renderPlot({
            boxplot(values$data)
          })
})    

 #Export PNG
  output$ExportPlot = downloadHandler(
  filename = 'BoxPlot.png',
  content = function(file) {        
      png(file) 
    boxplot(values$data) 
    dev.off()  
          })                                          
      })

shinyApp(ui = ui, server = server)
  • Hi, Welcome to SO! It would be best if you included a [minimal dataset](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) that people could use to think about your code. – xilliam Jun 13 '20 at 14:50
  • Hi, xilliam! Many thanks! The code just generates a table with 20 rows of NaNs and column names: A, B, C. The last row is for means. The table is then manually populated – Barnacle Goose Jun 13 '20 at 19:28

1 Answers1

0

You can add names to your boxplot call:

boxplot(values$data, names = str_trim(unlist(strsplit(isolate(input$colnames),","))))

and provide the column names from input$colnames (after splitting by comma).

You might add isolate if you don't want the plot updated when names are changed unless the build button is pressed.

Also, you can create a separate reactive expression that returns the column names, such as:

my_col_names <- reactive({
  str_trim(unlist(strsplit(isolate(input$colnames),",")))
})

and then call my_col_names() instead from plot, table, and wherever you need the column names so you don't need to repeat this code. Note that you may not want isolate if this expression should be evaluated when the column names change.

Ben
  • 28,684
  • 5
  • 23
  • 45
  • I also found that adding a second observer with an expression suggested by Ben `colnames(DF) = str_trim(unlist(strsplit(isolate(input$colnames),","))) ` preceded by `req(input$colnames)` does the job at the level of the table but the number of column names is now required to match exactly the number of columns in a pre-built table. Otherwise, I get an error "Error in if: missing value where TRUE/FALSE needed", which disappears when I finished typing the last of column names (3rd in the example above). Ben's solution works best at the level of the plot, which is good enough for me – Barnacle Goose Jun 18 '20 at 08:58