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.

Nova
  • 5,423
  • 2
  • 42
  • 62

2 Answers2

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
e.matt
  • 836
  • 1
  • 5
  • 12
  • 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. – Nova Apr 09 '20 at 15:49
  • 1
    I have updated, which should move facet boxes down to cover the whole facet label. Is this more what you were after? – e.matt Apr 09 '20 at 17:34
  • 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. – Nova Apr 09 '20 at 17:48
  • 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. – Nova Apr 09 '20 at 17:50
  • 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 -80 – e.matt Apr 09 '20 at 17:54
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)
}