1

I'm trying to render two pie charts from one ggplot pie code by setting up a function to receive a different set of parameter values each time the plot function is called. This saves me having to write two sets of virtually identical code for each Pie.

The three parameters being passed are factor, title and scale. Pie1 has factor=age_group, title="Age_Group Segmentation" and scale=c("#ffd700", "#bcbcbc", "#ffa500", "#254290", "#f0e68c", "#808000"). Pie2 has factor=Outcome, title="Outcome Segmentation" and scale=c(#ffd700", "#bcbcbc", "#ffa500", "#254290")

I know in principle that the way to do this is:

plot_func <- function(factor, title, scale) {ggplot(dfnew, aes("", share, fill = factor)) + geom_bar( +
labs(x = NULL, y = NULL, fill = NULL,title = title) + scale_fill_manual(values = scale)))}

then call this plot_function in renderplot: plot_func(group, title, scale) with the parameter values.

The problem is I don't know the required syntax especially since a reactive data object is being passed into ggplot as well, data_mod(). I haven't found anything in stackoverflow to mimic what I'm trying to do. The full code including a sample dataframe is included.

library(shiny)
library(ggplot2)
library(dplyr)

# use the below if you want to increase the file size being inputed to 9MB
# options(shiny.maxRequestSize = 9.1024^2)

ui <- shinyUI(navbarPage(
  "Example",
  tabPanel("Data",
           sidebarLayout(
             sidebarPanel("Nothing here at the moment"),
             mainPanel(
               "Select Dashboard Panel for results.Click on
    Select/All to make the plots render"
             )
           )),
  tabPanel("Dashboard",
           sidebarLayout(
             sidebarPanel(
               checkboxInput('all', 'Select All/None', value = TRUE),
               uiOutput("year_month"),
               tags$head(
                 tags$style(
                   "#year_month{color:red; font-
   size:12px; font-style:italic;
          overflow-y:scroll; max-height: 100px; background:
    ghostwhite;}"
                 )
               )
             ),
             mainPanel(uiOutput("tb"))
           ))
))

complaint_id <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
age_group <- c(
  "09 Months",
  "03 Months",
  "06 Months",
  "Over A Year",
  "12 Months",
  "01 Months",
  "09 Months",
  "03 Months",
  "06 Months",
  "Over A Year",
  "12 Months",
  "01 Months"
)
closed_date_ym <- c(
  "2019-09",
  "2019-09",
  "2019-09",
  "2019-09",
  "2019-09",
  "2019-09",
  "2019-08",
  "2019-08",
  "2019-08",
  "2018-08",
  "2019-08",
  "2019-08"
)
officer <- c("A", "B", "C", "D", "A", "B", "C", "D", "A", "B", "C",
             "D")
Outcome <- c(
  "Excellent",
  "Good",
  "OK",
  "Poor",
  "Excellent",
  "Good",
  "OK",
  "Poor",
  "Excellent",
  "Good",
  "OK",
  "Poor"
)
sample_data <- data.frame(complaint_id, age_group, closed_date_ym,
                          officer, Outcome)

server <- shinyServer(function(session, input, output) {
  # Make it reactive
  data <- reactive({
    sample_data
  })

  # Have to modify the reactive data object to add a column of 1s(Ones) inorder
  # that the Pie chart %s are calculated correctly within the segments. We apply
  # this modification to a new reactive object, data_mod()
  data_mod <- reactive({
    req(data())
    df <- data() %>% select(complaint_id, age_group, closed_date_ym,
                        officer, Outcome)

    df$Ones <- rep(1, nrow(data()))
    df
  })


  # creates a selectInput widget with unique YYYY-MM variables ordered from most
  # recent to oldest time period

  output$year_month <- renderUI({
    req(data_mod())
    data_ordered <- order(data_mod()$closed_date_ym, decreasing = T)
    data_ordered <- data_mod()[data_ordered,]
    checkboxGroupInput("variable_month",
                       "Select Month",
                       choices = unique(data_ordered$closed_date_ym))

  })

  observe({
    req(data_mod())
    data_ordered <- order(data_mod()$closed_date_ym, decreasing = T)
    data_ordered <- data_mod()[data_ordered,]
    updateCheckboxGroupInput(
      session,
      "variable_month",
      choices = unique(data_ordered$closed_date_ym),
      selected = if (input$all)
        unique(data_ordered$closed_date_ym)
    )

  })

  # This subsets the dataset based on what "variable month" above is selected
  # and renders it into a Table
  output$table <- renderTable({
    req(data_mod())
    dftable <- data_mod()
    df_subset <- dftable[, 1:5][dftable$closed_date_ym %in%
                                  input$variable_month, ]
  },
  options = list(scrollX = TRUE))

  # This takes the modified reactive data object data_mod(), assigns it to a
  # dataframe df. The dataset in df is subsetted based on the selected variable
  # month above and assigned into a new data frame, dfnew. The Pie chart is
  # built on the variables within dfnew.
  plot_func <- function(factor, title, scale) {
    group_by(factor) %>%
      summarize(volume = sum(Ones)) %>%
      mutate(share = volume / sum(volume) * 100.0) %>%
      arrange(desc(volume))
    ggplot(dfnew, aes("", share, fill = factor)) +
      geom_bar(
        width = 1,
        size = 1,
        color = "white",
        stat = "identity"
      ) +
      coord_polar("y") +
      geom_text(aes(label = paste0(round(share, digits = 2), "%")),
                position = position_stack(vjust = 0.5)) +
      labs(
        x = NULL,
        y = NULL,
        fill = NULL,
        title = title
      ) +
      guides(fill = guide_legend(reverse = TRUE)) +
      scale_fill_manual(values = scale) +
      theme_classic() +
      theme(
        axis.line = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        plot.title = element_text(hjust = 0.5, color = "#666666")
      )

  }

  output$plot1 <- renderPlot({
    req(data_mod(), input$variable_month)
    df <- data_mod()
    dfnew <- df[, 1:6][df$closed_date_ym %in% input$variable_month, ] %>%
      plot_func(
        factor = age_group,
        title = "Age Group Segmentation",
        scale = c(
          "#ffd700",
          "#bcbcbc",
          "#ffa500",
          "#254290",
          "#f0e68c",
          "#808000"
        )
      )
  })
  output$plot2 <- renderPlot({
    req(data_mod(), input$variable_month)
    df <- data_mod()
    dfnew <- df[, 1:6][df$closed_date_ym %in% input$variable_month, ] %>%
      plot_func(
        factor = Outcome,
        title = "Outcome Segmentation",
        scale =
          c("#ffd700", "#bcbcbc", "#ffa500", "#254290")
      )
  })

  # the following renderUI is used to dynamically gnerate the tabsets when the file is loaded
  output$tb <- renderUI({
    req(data())
    tabsetPanel(tabPanel("Plot",
                         plotOutput("plot1"), plotOutput("plot2")),
                tabPanel("Data", tableOutput("table")))

  })
})

shinyApp(ui = ui, server = server)

The expected results are to have two pie charts being rendered but I get the error message

Warning: Error in plot_func: unused argument (.)

Can someone help me out with this? Much appreciated.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
JohnA
  • 15
  • 4
  • Welcome to stackoverflow! Please provide us with a [reproducible example](https://stackoverflow.com/a/48343110/9841389). – ismirsehregal Sep 17 '19 at 08:32
  • Thankyou. Yes I'm new to stack overflow. My full code ui and server uploads a dataset from a directory so wont be possible unless I spply the csv dataset as well. Is it possible to attach my csv dataset and hence use the full working code? – JohnA Sep 17 '19 at 08:40
  • It is sufficient to supply us with some dummy data (create a `data.frame`) or use `dput()` to paste the data here. Furthermore, please add the libraries used and the app structure (server, ui). – ismirsehregal Sep 17 '19 at 08:42
  • I will supply the full code – JohnA Sep 17 '19 at 08:44
  • Hi, thankyou for your helpful suggestion as to how I should submit my question. I have now provided a full code, ui, server + library + a sample data.frame. I Look forward to hearing back. Regards – JohnA Sep 17 '19 at 22:23

1 Answers1

0

Please check the following:

library(shiny)
library(ggplot2)
library(dplyr)


# Global ------------------------------------------------------------------
# use the below if you want to increase the file size being inputed to 9MB
# options(shiny.maxRequestSize = 9.1024^2)

sample_data <- data.frame(
    complaint_id = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
    age_group = c(
      "09 Months",
      "03 Months",
      "06 Months",
      "Over A Year",
      "12 Months",
      "01 Months",
      "09 Months",
      "03 Months",
      "06 Months",
      "Over A Year",
      "12 Months",
      "01 Months"
    ),
    closed_date_ym = c(
      "2019-09",
      "2019-09",
      "2019-09",
      "2019-09",
      "2019-09",
      "2019-09",
      "2019-08",
      "2019-08",
      "2019-08",
      "2018-08",
      "2019-08",
      "2019-08"
    ),
    officer = c("A", "B", "C", "D", "A", "B", "C", "D", "A", "B", "C", "D"),
    Outcome = c(
      "Excellent",
      "Good",
      "OK",
      "Poor",
      "Excellent",
      "Good",
      "OK",
      "Poor",
      "Excellent",
      "Good",
      "OK",
      "Poor"
    )
  )


# UI ----------------------------------------------------------------------
ui <- shinyUI(navbarPage(
  "Example",
  tabPanel("Data",
           sidebarLayout(
             sidebarPanel("Nothing here at the moment"),
             mainPanel(
               "Select Dashboard Panel for results.Click on
    Select/All to make the plots render"
             )
           )),
  tabPanel("Dashboard",
           sidebarLayout(
             sidebarPanel(
               checkboxInput('all', 'Select All/None', value = TRUE),
               uiOutput("year_month"),
               tags$head(
                 tags$style(
                   "#year_month{color:red; font-
   size:12px; font-style:italic;
          overflow-y:scroll; max-height: 100px; background:
    ghostwhite;}"
                 )
               )
             ),
             mainPanel(uiOutput("tb"))
           ))
))



# Server ------------------------------------------------------------------
server <- shinyServer(function(session, input, output) {
  # Make it reactive
  data <- reactive({
    sample_data
  })

  # Have to modify the reactive data object to add a column of 1s(Ones) inorder
  # that the Pie chart %s are calculated correctly within the segments. We apply
  # this modification to a new reactive object, data_mod()
  data_mod <- reactive({
    req(data())
    data_mod <-
      data() %>% select(complaint_id, age_group, closed_date_ym, officer, Outcome)
    data_mod$Ones <- rep(1, nrow(data()))
    data_mod
  })


  # creates a selectInput widget with unique YYYY-MM variables ordered from most
  # recent to oldest time period

  output$year_month <- renderUI({
    req(data_mod())
    data_ordered <-
      order(data_mod()$closed_date_ym, decreasing = TRUE)
    data_ordered <- data_mod()[data_ordered,]
    checkboxGroupInput("variable_month",
                       "Select Month",
                       choices = unique(data_ordered$closed_date_ym))

  })

  observe({
    req(data_mod())
    data_ordered <-
      order(data_mod()$closed_date_ym, decreasing = TRUE)
    data_ordered <- data_mod()[data_ordered,]
    updateCheckboxGroupInput(
      session,
      "variable_month",
      choices = unique(data_ordered$closed_date_ym),
      selected = if (input$all)
        unique(data_ordered$closed_date_ym)
    )

  })

  # This subsets the dataset based on what "variable month" above is selected
  # and renders it into a Table
  output$table <- renderTable({
    req(data_mod())
    dftable <- data_mod()
    df_subset <- dftable[, 1:5][dftable$closed_date_ym %in%
                                  input$variable_month, ]
  },
  options = list(scrollX = TRUE))

  # This takes the modified reactive data object data_mod(), assigns it to a
  # dataframe df. The dataset in df is subsetted based on the selected variable
  # month above and assigned into a new data frame, DF. The Pie chart is
  # built on the variables within DF.
  plot_func <- function(DF, grp_vars, title, scale) {
    group_by(DF, DF[[grp_vars]]) %>%
      summarize(volume = sum(Ones)) %>%
      mutate(share = volume / sum(volume) * 100.0) %>%
      arrange(desc(volume)) %>%
      ggplot(aes("", share, fill = unique(DF[[grp_vars]]))) +
      geom_bar(
        width = 1,
        size = 1,
        color = "white",
        stat = "identity"
      ) +
      coord_polar("y") +
      geom_text(aes(label = paste0(round(share, digits = 2), "%")),
                position = position_stack(vjust = 0.5)) +
      labs(
        x = NULL,
        y = NULL,
        fill = NULL,
        title = title
      ) +
      guides(fill = guide_legend(reverse = TRUE)) +
      scale_fill_manual(values = scale) +
      theme_classic() +
      theme(
        axis.line = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        plot.title = element_text(hjust = 0.5, color = "#666666")
      )
  }

  output$plot1 <- renderPlot({
    req(data_mod(), input$variable_month)
    plot_func(
      DF = data_mod()[, 1:6][data_mod()$closed_date_ym %in% input$variable_month, ],
      grp_vars = "age_group",
      title = "Age Group Segmentation",
      scale = c(
        "#ffd700",
        "#bcbcbc",
        "#ffa500",
        "#254290",
        "#f0e68c",
        "#808000"
      )
    )
  })
  output$plot2 <- renderPlot({
    req(data_mod(), input$variable_month)
    plot_func(
      DF = data_mod()[, 1:6][data_mod()$closed_date_ym %in% input$variable_month, ],
      grp_vars = "Outcome",
      title = "Outcome Segmentation",
      scale = c("#ffd700", "#bcbcbc", "#ffa500", "#254290")
    )
  })

  # the following renderUI is used to dynamically gnerate the tabsets when the file is loaded
  output$tb <- renderUI({
    req(data())
    tabsetPanel(tabPanel("Plot",
                         plotOutput("plot1"), plotOutput("plot2")),
                tabPanel("Data", tableOutput("table")))

  })
})



# Compile shiny app -------------------------------------------------------
shinyApp(ui = ui, server = server)

The main issue was to correctly pass the grp_vars (renamed as factor is a function in R) to your plot_func. This issue was dplyr not shiny related.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • Thank you so much for giving me that help. I have applied that to my original app which retrieves a csv file from a folder now. It works! The only thing it's not doing is matching the segment colour with the correct legend colour. The segment %'s are correct but the colour matching against the legend isn't. For example, segment A is coloured blue but A in the legend has a different colour. Is there something which can be added in ggplot to link the segment colours with the legend colours? Look forward to your suggestion. Many thanks for your help. – JohnA Sep 19 '19 at 11:48
  • It seems to be ok for the example data given here and it's hard to say whats going wrong with your actual data without a reproducible example. I think you should try to provide example data which shows the same behaviour - maybe in another question because this one was about passing the parameters in the shiny context. It seems your additional question is more `ggplot2` related. – ismirsehregal Sep 19 '19 at 13:01