Bar switching positions in interactive geom_bar

Pb: when I click on the geom_bar bar, the bars switch positions even though I properly set the levels in the aes call.
Please try below the simplest example I could come up with.
All it does is add alpha to the bars below the clicked one.
Problem: click bars and see them switching position.

The alpha is added with the 'type' variable that is updated in dat() on click event.
If I deactivate the aes call in geom_bar the problem doesn't occur anymore. Nor does it happen if I place the alpha in the main aes() rather than geom_bar's one.

The reactiveVal dat()'s type is unchanged, so even though the bars switch position, for the click logic they do not (you can test this by clicking on the same spot twice: on the first bar will switch position, not in the second).

library(shiny); library(tidyverse)
ui <- function() {
      plotOutput(outputId = "bar",click = "click")
}

server <- function(input, output, session) {
      
      dat <- reactiveVal(
            tibble(value = 1:4,
                   name = c("a", "b", "a", "b"),
                   type = c("small", "small", "big", "big"),
                   cut_off = TRUE )
      )
      
      last_click <- reactiveVal(NULL)
      
      observeEvent(input$click, {
            if (!is.null(input$click)) last_click(input$click)
      })
      
      clicked_sample <- eventReactive(last_click(), {
            if (is.null(last_click())) return(NULL)
            
            click_x <- last_click()$x
            splits <- seq(1/4, 1 - 1/4, 1/2)
            
            sample_lvls <- dat()$name %>%
                  as_factor() %>% 
                  levels()
            
            clicked_sample_name <- sample_lvls[round(click_x)]
      
            types <- dat()$type %>% unique() %>% sort()
            
            x <- click_x - round(click_x) + 1/2
            
            clicked_type <- types[which.min(abs(splits - x))]
            
            dat() %>%
                  filter(type == clicked_type & name == clicked_sample_name)
            
      }, ignoreNULL = FALSE)
      
      observeEvent(clicked_sample(), {
            dat(
                  dat() %>%
                        mutate(cut_off = if_else(
                              value >= clicked_sample()$value,
                              TRUE,
                              FALSE,
                              missing = FALSE)
                        )
            )
      })
      
      output$bar <- renderPlot({
            g <- ggplot(dat()) +
                  aes(x = name, y = value,
                         fill = factor(type,
                                           levels = as.character(type) %>% unique() %>% sort())
                   ) +
                  geom_bar(
                        aes(alpha = cut_off %>% factor(levels = c(FALSE, TRUE))),
                        position = "dodge",
                        stat = "identity"
                  ) +
                  scale_alpha_discrete(guide = "none", drop = FALSE)
            
            if (!is.null(clicked_sample()$value)) {
                  g + geom_hline(yintercept = clicked_sample()$value)
            } else {
                  g
            }
      })
}

shinyApp(ui, server)

And if you want some stackoverflow points...https://stackoverflow.com/questions/50570471/bar-switching-positions-in-interactive-geom-bar

1 Like

Someone posted on SO with an alternative to placing the alpha in the main aes: putting all the aes parameters inside the the geom_bar. Which I think is the right solution as it would leave you free to do as you wanted for the other geoms.