1

When using plotly R package to create a sunburst pie chart in Shiny app, the user can click on the plot to zoom in/out dynamically. We would want to be able to download some data for current selected/centered piece.

However we cannot find this information from all the possible eventdata options. There are hover events but that's not enough, as user can click a piece then mouse move around to hover on other pieces without clicking it.

There is no clicking event with zoom in/out. And there is no relayout event. I think there must be some js event fired with zoom in/out, but that is not captured with existing eventdata function.

Update: It seemed there is selectedPath property for js chart, however I don't know how to access this data in Shiny.

Update2: Thanks for the answer which solved the problem. Also it turned out to be a missing feature in plotly R package, and it has been addd in most recent commit.

dracodoc
  • 2,603
  • 1
  • 23
  • 33

1 Answers1

8

Currently plotly_click only provides data for the root and the leafs of a sunburst plot. However you can pass the plotly_hover event_data to a reactiveVal once the plot was clicked.

As you haven't provided any example please see the following - using onclick() from library(shinyjs) for the workaround.

library(plotly)
library(shinyjs)

DF <- structure(list(labels = c("total", "A", "C", "B", "F", "E", "F", 
                                "E", "D", "E", "D", "D", "F", "G", "H", "H", "G", "H", "G", "H", 
                                "H", "G", "I", "G", "I", "H", "G", "I", "I", "G", "G", "I", "H", 
                                "H", "I", "I", "I", "H", "I", "G"),
                     values = c(100L, 36L, 29L,
                                35L, 12L, 14L, 10L, 14L, 8L, 18L, 5L, 10L, 9L, 6L, 5L, 4L, 3L,
                                7L, 4L, 2L, 6L, 3L, 8L, 3L, 2L, 4L, 4L, 4L, 4L, 4L, 3L, 2L, 5L, 
                                5L, 2L, 2L, 1L, 1L, 1L, 5L),
                     parents = c(NA, "total", "total", "total", "total - A", "total - C", "total - C", "total - A",
                                 "total - B", "total - B", "total - C", "total - A", "total - B",
                                 "total - A - F", "total - C - E", "total - C - F", "total - A - E",
                                 "total - A - E", "total - B - D", "total - B - D", "total - B - E",
                                 "total - C - D", "total - B - E", "total - A - D", "total - B - D",
                                 "total - B - F", "total - C - F", "total - A - E", "total - C - E",
                                 "total - B - E", "total - B - F", "total - A - D", "total - A - D",
                                 "total - A - F", "total - B - F", "total - C - F", "total - C - D",
                                 "total - C - D", "total - A - F", "total - C - E"),
                     ids = c(
                       "total", "total - A", "total - C", "total - B", "total - A - F", "total - C - E", 
                       "total - C - F", "total - A - E", "total - B - D", "total - B - E", "total - C - D",
                       "total - A - D", "total - B - F", "total - A - F - G", "total - C - E - H",
                       "total - C - F - H", "total - A - E - G", "total - A - E - H", "total - B - D - G",
                       "total - B - D - H", "total - B - E - H", "total - C - D - G", "total - B - E - I",
                       "total - A - D - G", "total - B - D - I", "total - B - F - H", "total - C - F - G",
                       "total - A - E - I", "total - C - E - I", "total - B - E - G", "total - B - F - G",
                       "total - A - D - I", "total - A - D - H", "total - A - F - H", "total - B - F - I",
                       "total - C - F - I", "total - C - D - I", "total - C - D - H", "total - A - F - I",
                       "total - C - E - G"
                     )), row.names = c(NA,-40L), class = "data.frame")

ui <- fluidPage(
  useShinyjs(),
  plotlyOutput("sunburst"),
  htmlOutput("hoverDataOut"),
  htmlOutput("clickDataOut")
)

server <- function(input, output, session) {
  output$sunburst <- renderPlotly({
    plot_ly(data = DF, source = "sunSource", customdata = ~ids, ids = ~ids, labels= ~labels, parents = ~parents, values= ~values, type='sunburst', branchvalues = 'total')
  })

  hoverData <- reactive({
    currentEventData <- unlist(event_data(event = "plotly_hover", source = "sunSource", priority = "event"))
  })

  clickData <- reactiveVal()

  observe({
    clickData(unlist(event_data(event = "plotly_click", source = "sunSource", priority = "event")))
  })

  onclick(id = "sunburst", expr = {clickData(hoverData())})

  output$hoverDataOut <- renderText({
    paste("Hover data:", paste(names(hoverData()), unlist(hoverData()), sep = ": ", collapse = " | "))
  })

  output$clickDataOut <- renderText({
    paste("Click data:", paste(names(clickData()), unlist(clickData()), sep = ": ", collapse = " | "))
  })

}

shinyApp(ui, server)

Result


I've created a GitHub issue to get some more information about this behaviour.

You might also be interested in this post.


Update: After the latest commit in reaction to this plotly_sunburstclick can be used as follows:

# install latest r-plotly dev version:
# devtools::install_github("ropensci/plotly")

library(plotly)
library(shinyjs)

DF <- structure(list(labels = c("total", "A", "C", "B", "F", "E", "F", 
                                "E", "D", "E", "D", "D", "F", "G", "H", "H", "G", "H", "G", "H", 
                                "H", "G", "I", "G", "I", "H", "G", "I", "I", "G", "G", "I", "H", 
                                "H", "I", "I", "I", "H", "I", "G"),
                     values = c(100L, 36L, 29L,
                                35L, 12L, 14L, 10L, 14L, 8L, 18L, 5L, 10L, 9L, 6L, 5L, 4L, 3L,
                                7L, 4L, 2L, 6L, 3L, 8L, 3L, 2L, 4L, 4L, 4L, 4L, 4L, 3L, 2L, 5L, 
                                5L, 2L, 2L, 1L, 1L, 1L, 5L),
                     parents = c(NA, "total", "total", "total", "total - A", "total - C", "total - C", "total - A",
                                 "total - B", "total - B", "total - C", "total - A", "total - B",
                                 "total - A - F", "total - C - E", "total - C - F", "total - A - E",
                                 "total - A - E", "total - B - D", "total - B - D", "total - B - E",
                                 "total - C - D", "total - B - E", "total - A - D", "total - B - D",
                                 "total - B - F", "total - C - F", "total - A - E", "total - C - E",
                                 "total - B - E", "total - B - F", "total - A - D", "total - A - D",
                                 "total - A - F", "total - B - F", "total - C - F", "total - C - D",
                                 "total - C - D", "total - A - F", "total - C - E"),
                     ids = c(
                       "total", "total - A", "total - C", "total - B", "total - A - F", "total - C - E", 
                       "total - C - F", "total - A - E", "total - B - D", "total - B - E", "total - C - D",
                       "total - A - D", "total - B - F", "total - A - F - G", "total - C - E - H",
                       "total - C - F - H", "total - A - E - G", "total - A - E - H", "total - B - D - G",
                       "total - B - D - H", "total - B - E - H", "total - C - D - G", "total - B - E - I",
                       "total - A - D - G", "total - B - D - I", "total - B - F - H", "total - C - F - G",
                       "total - A - E - I", "total - C - E - I", "total - B - E - G", "total - B - F - G",
                       "total - A - D - I", "total - A - D - H", "total - A - F - H", "total - B - F - I",
                       "total - C - F - I", "total - C - D - I", "total - C - D - H", "total - A - F - I",
                       "total - C - E - G"
                     )), row.names = c(NA,-40L), class = "data.frame")

ui <- fluidPage(
  useShinyjs(),
  plotlyOutput("sunburst"),
  htmlOutput("hoverDataOut"),
  htmlOutput("clickDataOut")
)

server <- function(input, output, session) {
  output$sunburst <- renderPlotly({
    plot_ly(data = DF, source = "sunSource", customdata = ~ids, ids = ~ids, labels= ~labels, parents = ~parents, values= ~values, type='sunburst', branchvalues = 'total')
  })

  hoverData <- reactive({
    currentEventData <- unlist(event_data(event = "plotly_hover", source = "sunSource", priority = "event"))
  })

  clickData <- reactive({
    currentEventData <- unlist(event_data(event = "plotly_sunburstclick", source = "sunSource", priority = "event"))
  })

  output$hoverDataOut <- renderText({
    paste("Hover data:", paste(names(hoverData()), unlist(hoverData()), sep = ": ", collapse = " | "))
  })

  output$clickDataOut <- renderText({
    paste("Click data:", paste(names(clickData()), unlist(clickData()), sep = ": ", collapse = " | "))
  })

}

shinyApp(ui, server)
ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • 1
    This is a great answer using existing information to solve the problem. I though about that the hover information when clicked is enough, but didn't come up this simple and clear solution. I still need to check if the clicked piece is the last level of leaf/root as that will cause zoom change but this is enough. Thanks! – dracodoc Nov 06 '19 at 13:38
  • I cannot edit the type above. If the clicked leaf is the last level, clicking it will not zoom the chart so not the event we want. – dracodoc Nov 06 '19 at 13:44
  • By the way, @ismirsehregal , for my purpose maybe comment out this line will be better: `observe({ clickData(unlist(event_data(event = "plotly_click"`. When clicked on last level leaf, the plotly click event fired and was put into clickData. However this kind of click doesn't zoom and is not what we wanted. With this commented out, the click data only update when non-last level leaf was clicked, thus always track the center of zoomed chart, which is exactly what we want. – dracodoc Nov 06 '19 at 13:59
  • Okay, the desired behaviour wasn't entirely clear to me - glad you've found a way. – ismirsehregal Nov 06 '19 at 14:02
  • Actually for our purpose we also need to track the history of clickdata. As when you clicked the center piece the plot will zoom out, and what we want is always the center piece after zoom in/out. So we need to compare last click data to know if it's a zoom in or out. – dracodoc Nov 06 '19 at 14:05
  • You might want to check [sund2b](https://timelyportfolio.github.io/sunburstR/articles/sunburst-2-0-0.html#d2b-sunburst) from library([sunburstR](https://github.com/timelyportfolio/sunburstR)), which also zooms on clicking the leafs. – ismirsehregal Nov 06 '19 at 14:11
  • Yes we looked at sunburstR. There are some differences on how they implement the chart, mainly that sund2b always keep the root and all the parents node in center, we prefer the plotly way for our purpose. – dracodoc Nov 07 '19 at 00:09
  • Thanks for the issue created, turned out this is a missing feature in plotly R package and it has been fixed in most recent commit. – dracodoc Nov 07 '19 at 00:10
  • It's wonderfull! could you see https://stackoverflow.com/questions/69317405/pie-chart-for-multi-columns-in-r-using-shiny – Masoud Sep 24 '21 at 15:15