0
votes

I need some help creating dynamic plots that go inside dynamic tabs. Here's the situation ... I have a data file that contains digital marketing data. The file has the following pieces of information: Campaign Name, Channel Name, Page Views, and Visits. Campaign Names are unique and roll up to one of four different marketing channels. This will change in the future depending on the data I feed it (for example, I may have six marketing channels in the next file I use), but this will do for troubleshooting purposes. I want to dynamically create tabs and output for each marketing channel in the file. I have been able to figure out how to create the tabs, but I'm having a hard time figuring out how to create other types of output (like a plot) to go with each tab.

The tabs were created using renderUI in the following code in the server file:

 output$mytabs = renderUI({
   if(is.null(rawData())){return ()}
   channels = unique(rawData()$Channel)
   myTabs = lapply(channels, tabPanel)
   do.call(tabsetPanel, myTabs)
 })

  output$scatterPlot <- renderUI({
    if(is.null(rawData())){return()}
    createPlots()
    myData = rawData()
    channels = unique(myData$Channel)  
    plot_output_list <- lapply(seq_along(channels), function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname)
    })

    do.call(tagList, plot_output_list)
  })

  createPlots <- reactive ({ 
    myData = rawData()
    channels = unique(myData$Channel) 
    for (i in seq_along(channels)) {
      local({
        my_i <- i
        plotname <- paste("plot", my_i, sep="")
        tempRows = which(myData$Channel==channels[i]) 
        output[[plotname]] <- renderPlot({
          plot(x = myData$Spend[tempRows], y = myData$Return[tempRows])
        })
      })
    }
  })

These were then referenced in the ui file as follows:

  mainPanel(
    tabsetPanel(
      tabPanel("Data Summary", uiOutput("dataSummary")),
      tabPanel("Parameters & Model Fit", 
               uiOutput('mytabs'),
               uiOutput('scatterPlot')),
      tabPanel("Budget & Spend Summary"),
      tabPanel("Testing", plotOutput('plot5'))
    )
  )

Ultimately, I'd like to plot Page Views vs Visits for each Marketing Channel on the corresponding marketing channel tab. At the moment, all four plots are showing up in each of marketing channel tab. In my global file, I've created two functions -- one function plots one channel at a time and another function that plots all the channels and saves each one as separate elements within a list. I'm not sure which one of these will end up being useful, if either.

I'm certain that I'm not understanding how to set 'myTabs' or I'm referencing it incorrectly from the ui. Even with this snippet of the code, someone here might be able to quickly spot the error and recommend a fix, but I am happy to provide the rest of my code if that would be helpful.

Thanks! Jess

EDIT: For Reference, here is all of my code. Just changed the dir object to be the directory you want to work in.

library(shiny)
dir = ""
setwd(dir)

#######################
###  Generate Data  ###
#######################

channels = c("Affiliate","Email","Media","SEO")
nObs = c(round(runif(1,100,200)))
pageViews = runif(nObs*length(channels),50,500)
visits = runif(nObs*length(channels),10,100)

campaignNames = unlist(lapply(channels, FUN = function(x) paste(x,seq(from=1,to=nObs,by=1),sep="")))
channelNames = rep(channels,nObs)

myData = data.frame(Campaign = campaignNames, Channel = channelNames, Return = pageViews, Spend = visits)
write.table(myData,file="myTestData.csv",sep=",",col.names=TRUE,row.names=FALSE)


########################
###  Global Functions  #
########################

summarizeData = function(myDat){
  summaryData = summarize(group_by(myDat,Channel), 'Campaign Count' = length(Campaign), Return = sum(Return), Spend = sum(Spend))
  return(summaryData)
}


###  PLOT DATA AND MODEL FIT  ###
plotSingle = function(myData, channelName){
  p1 <- ggplot(myData[which(myData$Channel==channelName),], aes(x = Spend, y = Return)) +
    geom_point(color="black") +
    theme(panel.background = element_rect(fill = 'grey85'),
          panel.grid.major = element_line(colour = "white"))
  return(p1)
}

plotAll = function(myData){
  channels = unique(myData$Channel)
  plots <- list()  # new empty list
  for (i in 1:length(channels)) {
    channelName = channels[i]
    p1 = plotSingle(myData = myData, channelName = channelName)
    plots[[i]] = p1 
  }
  return(plots)
}


############
###  UI  ###
############

ui <- fluidPage(
  headerPanel('Plot Testing'),
  sidebarPanel(
    h3(helpText("Data Input")),
    fileInput(inputId = "rawDataInput", label = "Upload Data"),
    h5(helpText("Select the file parameters below")),
    checkboxInput(inputId = 'header', label = 'Header', value = TRUE),
    checkboxInput(inputId = "stringAsFactors", "stringAsFactors", FALSE),
    br(),
    radioButtons(inputId = 'sep', label = 'Separator', choices = c(Comma=',',Semicolon=';',Tab='\t', Space=''), selected = ',')

  ),
  mainPanel(
    tabsetPanel(
      tabPanel("Data Summary", uiOutput("dataSummary")),
      tabPanel("Parameters & Model Fit", 
               uiOutput('mytabs'),
               uiOutput('scatterPlot')),
      tabPanel("Budget & Spend Summary"),
      tabPanel("Testing", plotOutput('plot5'))
    )
  )
)

################
###  Server  ###
################

server = function(input, output) {

  rawData <- reactive({
    file1 <- input$rawDataInput
    if(is.null(file1)){return()} 
    read.table(file=file1$datapath, sep=input$sep, header = input$header, stringsAsFactors = input$stringAsFactors)

  })


  # this reactive output contains the summary of the dataset and display the summary in table format
  output$filedf <- renderTable({
    if(is.null(rawData())){return ()}
    input$rawDataInput
  })

  # this reactive output contains the summary of the dataset and display the summary in table format
  output$sum <- renderTable({
    if(is.null(rawData())){return ()}
    summarizeData(rawData())
  })


  # This reactive output contains the dataset and display the dataset in table format
  output$table <- renderTable({
    if(is.null(rawData())){return ()}
    rawData()
  })

  dataPlots = reactive({
    channels = unique(rawData()$Channel)
    plots = plotAll(rawData())
  })

  output$mytabs = renderUI({
    if(is.null(rawData())){return ()}
    channels = unique(rawData()$Channel)
    createPlots()
    plot_output_list <- lapply(seq_along(channels), function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname)
    })
    myTabs = lapply(channels, tabPanel)
    do.call(tabsetPanel, myTabs)
  })

  createPlots <- reactive ({ 
    myData = rawData()
    channels = unique(myData$Channel) 
    for (i in seq_along(channels)) {
      local({
        my_i <- i
        plotname <- paste("plot", my_i, sep="")
        tempRows = which(myData$Channel==channels[i]) 
        output[[plotname]] <- renderPlot({
          plot(x = myData$Spend[tempRows], y = myData$Return[tempRows])
        })
      })
    }
  })

  output$scatterPlot <- renderUI({
    if(is.null(rawData())){return()}
    createPlots()
    myData = rawData()
    channels = unique(myData$Channel)  
    plot_output_list <- lapply(seq_along(channels), function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname)
    })

    do.call(tagList, plot_output_list)
  })

  output$dataSummary <- renderUI({
    if(is.null(rawData())){return()}
    else
      tabsetPanel(tabPanel("About file", tableOutput("filedf")),tabPanel("Data", tableOutput("table")),tabPanel("Summary", tableOutput("sum")))
  })

  output$plot5 = renderPlot({
    if(is.null(rawData())){return ()}
    myData = rawData()
    channelName = "Affiliate"
    p1 <- ggplot(myData[which(myData$Channel==channelName),], aes(x = Spend, y = Return)) +
      geom_point(color="black") +
      theme(panel.background = element_rect(fill = 'grey85'),
            panel.grid.major = element_line(colour = "white"))
    return(p1)
  })

}


###  Run App ###
shinyApp(ui = ui, server = server)
1
Its easier to help you when you provide a minimal reproducible example that we can copy/paste into R to run. That should include sample input data. That way possible solutions can be easily tested and verified.MrFlick
Thanks, @MrFlick. I was worried that might be the case. I've added all the code to the original post.Jessica Langford

1 Answers

0
votes

Your example isn't exactly minimal so i did some stripping away. First the data and helper functions

library(shiny)
library(ggplot2)

channels = c("Affiliate","Email","Media","SEO")
nObs = c(round(runif(1,100,200)))

myData = data.frame(
    Campaign = unlist(lapply(channels, FUN = function(x) paste(x,seq(from=1,to=nObs,by=1),sep=""))), 
    Channel = rep(channels,nObs), 
    Return = runif(nObs*length(channels),50,500), 
    Spend = runif(nObs*length(channels),10,100)
)

plotSingle = function(myData, channelName){
  ggplot(myData[which(myData$Channel==channelName),], aes(x = Spend, y = Return)) +
    geom_point(color="black") +
    theme(panel.background = element_rect(fill = 'grey85'),
          panel.grid.major = element_line(colour = "white"))
}

Now the UI

ui <- fluidPage(
  headerPanel('Plot Testing'),
  mainPanel(    
    uiOutput('mytabs'),
    plotOutput('scatterPlot')
  )
)

Note that we only use one plotOutput here. What we will do is just change the plot it's showing based on the currently selected tab. Here's the server code

server = function(input, output) {

  rawData <- reactive({
    myData
  })

  output$mytabs = renderUI({
    if(is.null(rawData())){return ()}
    channels = unique(rawData()$Channel)
    myTabs = unname(Map(tabPanel, channels))
    do.call(tabsetPanel, c(myTabs, id="channeltab"))
  })

  output$scatterPlot <- renderPlot({
    if(is.null(rawData()) | is.null(input$channeltab)){return ()}
    plotSingle(rawData(), input$channeltab)
  })

}

You see we set an id on the tabsetPanel we create. We can then use that as input to determine which panel is selected and show the correct plot. All run with

shinyApp(ui = ui, server = server)