Search code examples
rshinyrchartsggvis

Create dynamic ggvis chart from uploaded file in shiny


I am trying to use Shiny and ggvis to:

1) upload a data set

2) have the user select 2 columns (x, y)

3) return a ggvis plot displaying (x, y) from the uploaded data set

I've tried editing the examples from the Shiny Interactivity page as well as the movie explorer example. However, no chart is displayed.

I think my issue is around uploading the data set, but I don't know where to begin... Any suggestions?

Note - I've also tried this using rCharts, but I run into similar problems where no chart is displayed.

server.R

library(shiny)
library(dplyr)
library(ggvis)

shinyServer(function(input, output, session) {

fileHeaderNames <- reactive({

  infile <- input$datfile

  if(is.null(infile))
    return(NULL)

  d <- read.csv(infile$datapath, header = T)
  return(names(d))

})

# dynamic variable names
observe({

  updateSelectInput(session, 'x', choices = fileHeaderNames())
  updateSelectInput(session, 'y', choices = fileHeaderNames())

}) # end observe

  # uploading data set
  theData <- reactive({ 

    validate(
       need(input$datfile != "", "Please upload a file")
    )

    infile <- input$datfile
    dat <- read.csv(infile$datapath, 
                    header = T,
                    stringsAsFactors = F)

    if(is.null(infile)) return(NULL)

    data.frame(x = dat[, input$x],
               y = dat[, input$y])

    })

  # A simple visualisation. In shiny apps, need to register observers
  # and tell shiny where to put the controls
  theData %>%
    ggvis(~x, ~y) %>%
    layer_points() %>%
    bind_shiny("plot", "plot_ui")

})

ui.R

library(ggvis)
library(shiny)
shinyUI(pageWithSidebar(
  div(),
  sidebarPanel(
    fileInput('datfile', ''),
    selectInput('x', 'x:' ,'x'),
    selectInput('y', 'y:', 'y'),
    uiOutput("plot_ui")
  ),
  mainPanel(
    ggvisOutput("plot")
  )
))

Solution

  • Here is an attempt, I added a couple of reactive blocks to get the names that should be added on the plot axis.

    A trick you can use is to create a filtered dataframe that has two columns x and y and that changes when the user changes the values in the selectInput. You can then tell ggvis to plot x and y from that filtered dataframe and the plot will be interactive.

    library(shiny)
    library(dplyr)
    library(ggvis)
    
    shinyServer(function(input, output, session) {
      #load the data when the user inputs a file
      theData <- reactive({
        infile <- input$datfile        
        if(is.null(infile))
          return(NULL)        
        d <- read.csv(infile$datapath, header = T)
        d        
      })
    
    
    
      # dynamic variable names
      observe({
        data<-theData()
        updateSelectInput(session, 'x', choices = names(data))
        updateSelectInput(session, 'y', choices = names(data))
    
      }) # end observe
    
      #gets the y variable name, will be used to change the plot legends
      yVarName<-reactive({
        input$y
      })
    
     #gets the x variable name, will be used to change the plot legends
      xVarName<-reactive({
        input$x
      })
    
      #make the filteredData frame
    
      filteredData<-reactive({
        data<-isolate(theData())
        #if there is no input, make a dummy dataframe
        if(input$x=="x" && input$y=="y"){
          if(is.null(data)){
            data<-data.frame(x=0,y=0)
          }
        }else{
          data<-data[,c(input$x,input$y)]
          names(data)<-c("x","y")
        }
        data
      })
    
      #plot the ggvis plot in a reactive block so that it changes with filteredData
      vis<-reactive({
        plotData<-filteredData()
        plotData %>%
        ggvis(~x, ~y) %>%
        layer_points() %>%
        add_axis("y", title = yVarName()) %>%
        add_axis("x", title = xVarName()) %>%
        add_tooltip(function(df) format(sqrt(df$x),digits=2))
      })
        vis%>%bind_shiny("plot", "plot_ui")
    
    })
    

    EDIT: added tooltips.