rggplot2ggiraph

ggiraph, R: How to link legend and plot with same data_id attribute?


When I hover my cursor across a chart, I want the respective legend label and fill to also be highlighted, and vice versa. To do this, they need the same data_id - but I am struggling to get this right. How do I get this to work within my code?

If I add data_id=groupID to scale_fill_manual_interactive() to make the legend interactive, I get the following error:

Error in scale_interactive(scale_fill_manual, ...) : 
  object 'groupID' not found

data_id = function(breaks) { as.character(breaks) } works but it doesn't link the legend and plot. But I can't find an explanation for why that should work but 'data_id=groupID' doesn't, so solving this alone has been impossible.

Here is the code (EDIT I've managed to get the custom labels to display correctly and have updated the code)

library(ggplot2)
library(ggiraph)
library(ggrepel)
library(scales)

Area <- c("location1", "location2", "location3", "location4")
very_good <-  c(14, 7, 17, 16)
good <-  c(33, 31, 35, 31)
quite_bad <-  c(33, 36, 30, 1)
very_bad <-  c(17, 2, 14, 10)

#Custom labels for the legend
Labels <- c("Very good", "Good", "Quite bad", "Very bad, wont return")

df1 <- data.frame(
  Area, 
  very_good, 
  good, 
  quite_bad,
  very_bad
)

df1_subset <- df1 %>%
  mutate_at(vars(2:5), funs(./100)) %>% 
  pivot_longer(
    cols = c(2:5),
    names_to = "Question", values_to = "Result"
  )

df1_subset <- transform(
  df1_subset,groupID=as.numeric(forcats::fct_inorder(Question))
)

set.seed(1)

stacked_chart <- ggplot(
  data = df1_subset,
  aes(
    x = Result,
    y = Area,
    group = Question,
    fill = Question,
    data_id = groupID
  )
) +
  geom_col_interactive(
    position = position_fill(reverse = TRUE)
  ) +
  geom_text_repel_interactive(
    aes(
      color = ifelse(Result > 0.06,  "#FFFFFF", "transparent"),
      label = percent(Result)
    ),
    fontface = "bold",
    position = position_fill(
      reverse = TRUE
    ),
    box.padding = 0.05,
    segment.color = "transparent",
    size = 5,
    direction = "x",
    hjust = 1.5
  ) +
  scale_y_discrete(
    limits = rev(Area)
  ) +
  scale_x_continuous(
    labels = scales::percent,
    expand = c(0, 0),
    limits = c(0, 1)
  ) +
  scale_color_identity() +
  scale_fill_manual_interactive(
data_id = lapply(Labels, function(breaks) {
  as.character(breaks)
}),
labels = function(breaks) {
  lapply(Labels, function(breaks) {
    label_interactive(
      breaks,
      data_id = as.character(breaks)
    )
  })
},
    values = c(
      "#000000",
      "#333333",
      "#666666",
      "#999999"
    )
  ) +
  theme_minimal() +
  theme(
    legend.position = "top",
    legend.justification = "left",
    legend.title = element_blank()
  )

stacked_chart_ggiraph <- girafe(
  ggobj = stacked_chart, width_svg = 9, height_svg = 6,
  options = list(
    opts_sizing(rescale = TRUE),
    opts_toolbar(saveaspng = FALSE),
    opts_hover_inv(css = girafe_css(
      css = "opacity:0.3;"
    )),
    opts_hover(css = girafe_css(
      css = "cursor:pointer;fill:red;",
      text = "cursor:pointer;fill:#222222;"
    )),
    opts_hover_key(css = girafe_css(
      css = "cursor:pointer;fill:red;"
    ))
  )
)

stacked_chart_ggiraph

Solution

  • I did end up finding a solution for this, which was a miracle in itself as ggiraph's documentation is still VERY incomplete. But it's been a few weeks so please comment if I've missed anything.

    So ggiraph allows for custom functionality with the extra_interactive_params argument briefly mentioned in this document.

    It took a lot of trial and error, but to get it to work in my case, it needed to be initialised in the initial geom_*_interactive function before it would work with the scale function, where my legend is created. I also had to use backticks `` to force R to accept custom attributes that contained special characters, like hyphens -

    Here are the relevant bits of code:

      geom_col_interactive(
        aes(
          data_id = groupID,
          `data-id` = groupID
        ),
        extra_interactive_params = "data-id",
        position = position_fill(reverse = TRUE)
      ) +
    

    ...

      scale_fill_manual_interactive(
        extra_interactive_params = "data-id",
        `data-id` = seq_along(Legend_colours),
        values = Legend_colours,
        labels = function(breaks) {
          lapply(seq_along(Labels), function(breaks) {
            label_interactive(
              Labels[breaks],
              `data-id` = breaks,
              extra_interactive_params = c("data-id")
            )
          })
        }
        ) +