1

I'm trying to use click events using the plotly_click option in RShiny. What I want to do is: On clicking the plot, the dataset corresponding to the click event is displayed. So when I click on 'Office Supplies' in categories on the plot, dataset corresponding to category column='Office Supplies' is displayed. Similarly, when I drill down to sub category level and click on any of sub category in the plot, dataset corresponding to the sub category is displayed. But what I am not able to achieve is: that when I click on 'Back' action button, I see an empty data table and not data table corresponding to key 'Office Supplies' i.e. On clicking the back button, I see an empty table which is what I don't want. How should I do this?. Any help would be appreciated. Below is my code:

library(shiny)
library(plotly)
library(dplyr)
library(readr)

sales <- read_csv("https://plotly-r.com/data-raw/sales.csv")
categories <- unique(sales$category)
sub_categories <- unique(sales$sub_category)
ids <- unique(sales$id)

ui <- fluidPage(
  uiOutput("history"),
  plotlyOutput("bars", height = 200),
  plotlyOutput("lines", height = 300),
  uiOutput('back'),
  uiOutput("back1"),
  dataTableOutput("click1")
)

server <- function(input, output, session) {
  # These reactive values keep track of the drilldown state
  # (NULL means inactive)
  drills <- reactiveValues(category = NULL,
                           sub_category = NULL,
                           id = NULL)
  # filter the data based on active drill-downs
  # also create a column, value, which keeps track of which
  # variable we're interested in
  sales_data <- reactive({
    if (!length(drills$category)) {
      return(mutate(sales, value = category))
    }
    sales <- filter(sales, category %in% drills$category)
    if (!length(drills$sub_category)) {
      return(mutate(sales, value = sub_category))
    }
    sales <- filter(sales, sub_category %in% drills$sub_category)
    mutate(sales, value = id)
  })

  # bar chart of sales by 'current level of category'
  output$bars <- renderPlotly({
    a<- sales
    render_value(a)
    d <- count(sales_data(), value, wt = sales)

    p <- plot_ly(d,
                 x = ~ value,
                 y = ~ n,
                 source = "bars",key=~value) %>%
      layout(yaxis = list(title = "Total Sales"),
             xaxis = list(title = ""))

    if (!length(drills$sub_category)) {
      add_bars(p, color = ~ value,key=~value)
    } else if (!length(drills$id)) {
      add_bars(p,key=~value) %>%
        layout(hovermode = "x",
               xaxis = list(showticklabels = FALSE))
    } else {
      # add a visual cue of which ID is selected
      add_bars(p,key=~value) %>%
        filter(value %in% drills$id) %>%
        add_bars(color = I("black")) %>%
        layout(
          hovermode = "x",
          xaxis = list(showticklabels = FALSE),
          showlegend = FALSE,
          barmode = "overlay"
        )
    }
  })


  # control the state of the drilldown by clicking the bar graph
  observeEvent(event_data("plotly_click", source = "bars"), {
    x <- event_data("plotly_click", source = "bars")$x
    if (!length(x))
      return()

    if (!length(drills$category)) {
      drills$category <- x
    } else if (!length(drills$sub_category)) {
      drills$sub_category <- x
    } else {
      drills$id <- x
    }
  })

  output$back <- renderUI({
    if (!is.null(drills$category) && is.null(drills$sub_category)) {
      actionButton("clear", "Back", icon("chevron-left"))
    }
  })

  output$back1 <- renderUI({
    if (!is.null(drills$sub_category)) {
      actionButton("clear1", "Back", icon("chevron-left"))
    }
  })

  observeEvent(input$clear,
               drills$category <- NULL)
  observeEvent(input$clear1,
               drills$sub_category <- NULL)

  render_value=function(df_1){
    output$click1<- DT::renderDataTable({
      s <- event_data("plotly_click",source="bars")
      if (is.null(s)){
        return(NULL)
      }
      else if(!is.null(drills$category) && is.null(drills$sub_category)){
        ad<- df_1[df_1$category %in% s$key,]
        return(DT::datatable(ad))
      }
      else if(!is.null(drills$sub_category)){
        print(s$key)
        ad<- df_1[df_1$sub_category %in% s$key,]
        return(DT::datatable(ad))
      }
    })
  }

}

shinyApp(ui, server)
Jan
  • 4,974
  • 3
  • 26
  • 43
  • I had a similar solution on [this answer](https://stackoverflow.com/a/66980091/10992158) or you might want to look into “brushing” in ggplot2 – Daniel_j_iii Apr 25 '21 at 20:42

1 Answers1

1

As you did not provide sample data, I used gapminder data to test. When you click on 'back' button for sub_category, it is not recognizing the click event on the plot. Alternately, you can just output sales_data() as shown below.

library(shiny)
library(plotly)
library(dplyr)
library(readr)
library(gapminder)

#sales <- read_csv("https://plotly-r.com/data-raw/sales.csv")

sales <- gapminder
sales$category <- sales$continent
sales$sub_category <- sales$country
sales$id <- sales$year
sales$n <- sales$lifeExp
sales$sales <- sales$gdpPercap

categories <- unique(sales$category)
sub_categories <- unique(sales$sub_category)
ids <- unique(sales$id)

ui <- fluidPage(
  
  # uiOutput("history"),
  plotlyOutput("bars", height = 200),
  # plotlyOutput("lines", height = 300),
  uiOutput('back'),
  uiOutput("back1"),
  DTOutput("t1")       ## working
  ,DTOutput("click1")  ## not working
)

server <- function(input, output, session) {
  # These reactive values keep track of the drilldown state
  # (NULL means inactive)
  drills <- reactiveValues(category = NULL,
                           sub_category = NULL,
                           id = NULL)
  # filter the data based on active drill-downs
  # also create a column, value, which keeps track of which
  # variable we're interested in
  sales_data <- reactive({
    if (!length(drills$category)) {
      return(mutate(sales, value = category))
    }
    sales <- filter(sales, category %in% drills$category)
    if (!length(drills$sub_category)) {
      return(mutate(sales, value = sub_category))
    }
    sales <- filter(sales, sub_category %in% drills$sub_category)
    mutate(sales, value = id)
  })
  
  output$t1 <- renderDT({
    if (is.null(drills$category) & is.null(drills$sub_category) ) return(NULL)  ## comment out this line if you want all data to be displayed initially
    sales_data()
  })
  
  # bar chart of sales by 'current level of category'
  output$bars <- renderPlotly({
    a<- sales
    render_value(a)
    d <- count(sales_data(), value, wt = sales)

    p <- plot_ly(d,
                 x = ~ value,
                 y = ~ n,
                 source = "bars",key=~value) %>%
      layout(yaxis = list(title = "Total Sales"),
             xaxis = list(title = ""))

    if (!length(drills$sub_category)) {
      add_bars(p, color = ~ value,key=~value)
    } else if (!length(drills$id)) {
      add_bars(p,key=~value) %>%
        layout(hovermode = "x",
               xaxis = list(showticklabels = FALSE))
    } else {
      # add a visual cue of which ID is selected
      add_bars(p,key=~value) %>%
        filter(value %in% drills$id) %>%
        add_bars(color = I("black")) %>%
        layout(
          hovermode = "x",
          xaxis = list(showticklabels = FALSE),
          showlegend = FALSE,
          barmode = "overlay"
        )
    }
  })


  # control the state of the drilldown by clicking the bar graph
  observeEvent(event_data("plotly_click", source = "bars"), {
    x <- event_data("plotly_click", source = "bars")$x
    if (!length(x))
      return()

    if (!length(drills$category)) {
      drills$category <- x
    } else if (!length(drills$sub_category)) {
      drills$sub_category <- x
    }else {
      drills$id <- x
    }
    
  })

  output$back <- renderUI({
    if (!is.null(drills$category) && is.null(drills$sub_category)) {
      actionButton("clear", "Back", icon("chevron-left"))
    }
  })

  output$back1 <- renderUI({
    if (!is.null(drills$sub_category)) {
      actionButton("clear1", "Back", icon("chevron-left"))
    }
  })

  observeEvent(input$clear,
               {drills$category <- NULL})
  observeEvent(input$clear1, {
               drills$sub_category <- NULL})

  render_value=function(df_1){
    output$click1<- DT::renderDataTable({
      s <- event_data("plotly_click",source="bars")
      if (is.null(s)){
        return(NULL)
      }else if((!is.null(drills$category) && is.null(drills$sub_category))){
        print(s$key)
        ad<- df_1[df_1$category %in% s$key,]
        return(DT::datatable(ad))
      }else if(!is.null(drills$sub_category)){
        #print(s$key)
        ad<- df_1[df_1$sub_category %in% s$key,]
        return(DT::datatable(ad))
      }
    })
  }
  
}

shinyApp(ui, server)
YBS
  • 19,324
  • 2
  • 9
  • 27