Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 759
Nova Avatar asked Apr 09 '20 13:04

Nova


2 Answers

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
like image 192
e.matt Avatar answered Nov 07 '22 01:11

e.matt


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)
}
like image 23
Sergi Ballestar Avatar answered Nov 07 '22 02:11

Sergi Ballestar