Long facet_wrap labels in ggplotly / plotly overlap facet's strip.background
Asked Answered
N

2

9

I've got a plot like the one below, where I need to display a plot title and some long facet labels. In ggplot2, it looks just fine.

Reprex:

library(ggplot2)
library(stringr)
library(plotly)

iris$Species2 <- paste(iris$Species, "... some text to make the label really long and hard to put on a facet label")
iris$Species2 <- str_wrap(iris$Species2, 20)

g <- ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
  geom_point() +
  labs(title = "This title isn't helping anyone") + 
  facet_wrap(~Species2)

g

enter image description here

However, converting to a dynamic plot is not working as expected... the facet labels get cut off and run into the title:

gp <- ggplotly(g)
gp

enter image description here

There's a previous SO question about this, but it looks like the OP didn't try the answer - no one caught that the suggested answer doesn't work as expected.

I'm no stranger to plotly having strange behaviour when facets are involved - see conversation here on github, but I don't know plotly well enough to modify an object to force it to have a longer strip.background.

Hoping someone can help me modify the object gp for a solution.

Norma answered 9/4, 2020 at 13:58 Comment(0)
D
3
gp <- ggplotly(g)
# move facet labels down
gp[["x"]][["layout"]][["annotations"]][[3]][["y"]] <- 0.85 
gp[["x"]][["layout"]][["annotations"]][[4]][["y"]] <- 0.85
gp[["x"]][["layout"]][["annotations"]][[5]][["y"]] <- 0.85

# extend y axis to make room to move facet box down
gp[["x"]][["layout"]][["yaxis"]][["range"]] <- c(1.88,5.5) 
# extend facet boxes down
gp[["x"]][["layout"]][["shapes"]][[2]][["y0"]] <- - 100 
gp[["x"]][["layout"]][["shapes"]][[4]][["y0"]] <- - 100 
gp[["x"]][["layout"]][["shapes"]][[6]][["y0"]] <- - 100

gp
Deplete answered 9/4, 2020 at 14:56 Comment(5)
Thanks e.matt but that only changes the font size, not the size of the strip background. In my "real" graph, the text is already as small as it can be... also I could just modify the text size in the ggplot object before converting to plotly.Norma
I have updated, which should move facet boxes down to cover the whole facet label. Is this more what you were after?Deplete
Thanks! Yes, it helps - the text is still outside the boxes at the aspect ratio I am currently wanting to show my graphs, but I think I could tweak your numbers to make it work. I'll try that.Norma
Just a note of warning - at my "squished" aspect ratio (a short, rather than tall, plot), some of the data are covered by the new strip backgrounds.Norma
If some data are still covered increase the ylim 5.5 eg 7 to a bigger value and move facet boxes up i.e less negative eg -80Deplete
T
3

Based on e.matt answer, I wrote a function which simplifies the process:

Original

facet_strip_bigger <- function(gp){

  # n_facets should be the number of facets x2
  n_facets <- c(1:length(gp[["x"]][["layout"]][["shapes"]]))
  
  for(i in n_facets){
    if(n_facets[i] %% 2 == 0){
      gp[["x"]][["layout"]][["shapes"]][[i]][["y0"]] <- + 80 # increase as needed
      gp[["x"]][["layout"]][["shapes"]][[i]][["y1"]] <- 0
    }
  }
  
  return(gp)
}

So in this particular case:

iris$Species2 <- paste(iris$Species, "... some text to make the label really long and 
    hard to put on a facet label")
iris$Species2 <- str_wrap(iris$Species2, 20)

g <- ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
  geom_point() +
  labs(title = "This title isn't helping anyone") + 
  theme(axis.title.y = element_blank(),
        axis.title.x = element_blank())+
  facet_wrap(~Species2) 

g %>% 
  ggplotly() %>% 
  layout(title = list(y = 0.96,
                      yanchor = "top",
                      yef = "container"),
         margin = list(t = 110),
         yaxis = list(title = list(text = "Sepal width",
                                   standoff = 10L)),
         xaxis = list(title = list(text = "Sepal length"))
         ) %>%
  facet_strip_bigger()

enter image description here

Edit

I improved the function so that size is an argument, so no need to edit the function every time the size needs to be changed.

facet_strip_bigger <- function(gp, size){
  if(missing(gp)){
    print("this function needs a facet_wrap ggplotly object")
  }
  if(missing(size)){
    print("this function needs 'size' argument to be specified as integer. 80 will be introduced as default")
    size <- 80
  }
  
  n_facets <- c(1:length(gp[["x"]][["layout"]][["shapes"]]))
  
  for(i in n_facets){
    if(n_facets[i] %% 2 == 0){
      gp[["x"]][["layout"]][["shapes"]][[i]][["y0"]] <- + as.numeric(size)
      gp[["x"]][["layout"]][["shapes"]][[i]][["y1"]] <- 0
    }
  }
  
  return(gp)
}
Tallbot answered 24/3, 2021 at 14:14 Comment(1)
I wonder if you could get the size needed from the ggplot object instead of having to pass it to the function?Norma

© 2022 - 2025 — McMap. All rights reserved.