Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

r

ggplot2

ggiraph

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
like image 610
Sidders Avatar asked Jan 21 '26 20:01

Sidders


1 Answers

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")
        )
      })
    }
    ) +
like image 199
Sidders Avatar answered Jan 23 '26 11:01

Sidders



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!