0

Question

Despite both sharing the same reactive data frame, why does the bar plot not match the data shown in the table in my app?

Overview

I'm currently arranging the iris data frame in df - a reactive expression that uses the input of a radio button and arranges the data based on base::switch(). Afterwards, df is displayed in two forms: a bar plot and a table.

The trouble is that the bar plot doesn't respond to the radio button, while the table does. My desired output would be for the bar plot to be arranged in ascending/descending order - based on the radio button - in the same way the table is arranged.

Any clarification on why this is happening and how I can resolve this would be much appreciated!

Screenshot of App

Code

# load necessary package
library( shiny )
library( tidyverse )

# create UI
ui <- fluidPage(
  title = "Reactive Programming"
  , plotOutput( outputId = "my.barplot" )
  , radioButtons( inputId = "direction"
                  , label = "Arrange the data:"
                  , choices = c("Ascending", "Descending")
                  , selected = "Descending"
                  , inline = TRUE )
  , tableOutput( outputId = "my.table" )
)

# create server
server <- function( input, output, session){

  # arrange iris in ascending/descending order based on the "Sepal.Length" column
  df <- reactive({
    switch( EXPR = input$direction
            , "Ascending" = iris %>% arrange( Sepal.Length ) 
            , "Descending" = iris %>% arrange( desc( Sepal.Length ) ) )
  })

  # plot df()
  output$my.barplot <- renderPlot({
    ggplot( data = df() ) +
      geom_bar( aes( x = Species
                     , y = Sepal.Length )
                , stat = "identity" )
  })

  # show df()
  output$my.table <- renderTable({
    df()
  })
}

# run App
shinyApp( ui = ui, server = server )

# end of script #

Session Info

R version 3.4.4 (2018-03-15)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.2

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] forcats_0.3.0   stringr_1.3.0   dplyr_0.7.4     purrr_0.2.4     readr_1.1.1    
 [6] tidyr_0.8.0     tibble_1.4.2    ggplot2_2.2.1   tidyverse_1.2.1 shiny_1.0.5    

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.16     cellranger_1.1.0 pillar_1.2.1     compiler_3.4.4  
 [5] plyr_1.8.4       bindr_0.1.1      tools_3.4.4      digest_0.6.15   
 [9] lubridate_1.7.3  jsonlite_1.5     nlme_3.1-131.1   gtable_0.2.0    
[13] lattice_0.20-35  pkgconfig_2.0.1  rlang_0.2.0      psych_1.7.8     
[17] cli_1.0.0        rstudioapi_0.7   yaml_2.1.18      parallel_3.4.4  
[21] haven_1.1.1      bindrcpp_0.2     xml2_1.2.0       httr_1.3.1      
[25] hms_0.4.2        grid_3.4.4       glue_1.2.0       R6_2.2.2        
[29] readxl_1.0.0     foreign_0.8-69   modelr_0.1.1     reshape2_1.4.3  
[33] magrittr_1.5     scales_0.5.0     htmltools_0.3.6  rvest_0.3.2     
[37] assertthat_0.2.0 mnormt_1.5-5     colorspace_1.3-2 mime_0.5        
[41] xtable_1.8-2     httpuv_1.3.6.2   stringi_1.1.7    lazyeval_0.2.1  
[45] munsell_0.4.3    broom_0.4.3      crayon_1.3.4 
Cristian E. Nuno
  • 2,822
  • 2
  • 19
  • 33
  • Essentially a duplicate of [How to order bars in ggplot2 bar graph](https://stackoverflow.com/q/5208679/903061). As the answer shows, the bar order is not determined by the row order, it is determined by the order of the factor levels. – Gregor Thomas May 18 '18 at 06:01
  • @Gregor, thank you! This helped me understand where I was going wrong. I've updated the construction of `df` to include a mutated version of `Species` that reorders this variable by `Sepal.Length`. I appreciate your help! – Cristian E. Nuno May 18 '18 at 13:57

2 Answers2

1

You don't have to change the order of the data in ggplot to change the order in the plot. You have to specify it inside the plot functions:

I've created an if-else condition to create different plots when Ascending or Descending is clicked. Have a look at the code. I've just added a reorder criteria in the geom_bar() function

Code:

library( shiny )
library( tidyverse )

# create UI
ui <- fluidPage(
  title = "Reactive Programming"
  , plotOutput( outputId = "my.barplot" )
  , radioButtons( inputId = "direction"
                  , label = "Arrange the data:"
                  , choices = c("Ascending", "Descending")
                  , selected = "Descending"
                  , inline = TRUE )
  , tableOutput( outputId = "my.table" )
)

# create server
server <- function( input, output, session){

  # arrange iris in ascending/descending order based on the "Sepal.Length" column
  df <- reactive({
    switch( EXPR = input$direction
            , "Ascending" = iris %>% arrange( Sepal.Length ) 
            , "Descending" = iris %>% arrange( desc( Sepal.Length ) ) )
  })

  # plot df()
  output$my.barplot <- renderPlot({
    if(input$direction=="Ascending")
      ggplot( data = df() ) +
      geom_bar( aes( x = Species
                     , y = Sepal.Length )
                , stat = "identity" )
    else if(input$direction=="Descending")
      ggplot( data = df() ) +
      geom_bar( aes( x = reorder(Species,-Sepal.Length)
                     , y = Sepal.Length )
                , stat = "identity" )
  })

  # show df()
  output$my.table <- renderTable({
    df()
  })
}

# run App
shinyApp( ui = ui, server = server )

You can use labs() to rename the axis if you want

The Output shiny app is here Shiny Output

Vedha Viyash
  • 708
  • 1
  • 8
  • 19
0

Overview

Thanks to @Gregor, I realized my mistake was not understanding that bar plot order is determined by factor level.

While @Vedha Viyash's answer does work, I combined @Gregor's advice to use dplyr::mutate() to alter df()$Species by using stats::reorder() to set the levels in df()$Species to be arranged by df()$Sepal.Length.

This honors the D.R.Y principal by not repeating the construction of the plot object inside of output$my.barplot.

Screenshot of shiny app

# load necessary package
library( shiny )
library( tidyverse )

# create UI
ui <- fluidPage(
  title = "Reactive Programming"
  , plotOutput( outputId = "my.barplot" )
  , radioButtons( inputId = "direction"
                  , label = "Arrange the data:"
                  , choices = c("Ascending", "Descending")
                  , selected = "Descending"
                  , inline = TRUE )
  , tableOutput( outputId = "my.table" )
)

# create server
server <- function( input, output, session){

  # arrange iris in ascending/descending order based on the "Sepal.Length" column
  df <- reactive({
    switch( EXPR = input$direction
            , "Ascending" = iris %>% arrange( Sepal.Length ) %>% mutate( Species = reorder( x = Species, X = Sepal.Length ) )
            , "Descending" = iris %>% arrange( desc( Sepal.Length ) ) %>% mutate( Species = reorder( x = Species, X = desc( Sepal.Length ) ) ) )
  })

  # plot df()
  output$my.barplot <- renderPlot({
    ggplot( data = df() ) +
      geom_bar( aes( x = Species
                     , y = Sepal.Length )
                , stat = "identity" )
  })

  # show df()
  output$my.table <- renderTable({
    df()
  })
}

# run App
shinyApp( ui = ui, server = server )

# end of script #
Cristian E. Nuno
  • 2,822
  • 2
  • 19
  • 33
  • Yeah, that sure will work, but if you use 'if-else' condition you can also modify other aspects of the graph when either of the ActionButton is selected. I'm saying that the App will be more flexible that way. – Vedha Viyash May 18 '18 at 13:59
  • 1
    @VedhaViyash I only want `df` to be flexible, not the construction of `output$my.barplot`. However, I would not have even heard of the `stats::reorder()` function without your help so thank you! – Cristian E. Nuno May 18 '18 at 14:56
  • 1
    @VedhaViyash DRY is good here. If you want to modify aspects of the graph, you want to make the change in one place. If another option is added to, say, change the theme, you want to make the change in one place. Not repeating the plot code makes the code much easier to maintain and debug. – Gregor Thomas May 18 '18 at 16:15