16

I'm having problems stopping the y-axis text from overlapping with the ticks using ggplotly around ggplot. How can I fix this? I've tried the following code:

enter image description here

set.seed(395)
df1<- data.frame(CO2= c(cumsum(rnorm(1*36)), cumsum(rnorm(1*36))),
                  Group= rep(c("A","B"), each=36),
                  Segment=rep(seq(1,12),each=36))

plot<-ggplot(df1, aes(CO2, fill = Group)) +
           geom_density(alpha = 0.8)+
           facet_wrap(~ Segment)+
           theme_bw()+
           labs(x="CO2", y="density")
#Shouldn't the following work?
    pb <- plotly_build(plot)
    pb$layout$margin$l <- 200
    pb$layout$margin$b <- 100
    pb
HCAI
  • 2,213
  • 8
  • 33
  • 65
  • Have you tried this: http://stackoverflow.com/questions/14487188/increase-distance-between-text-and-title-on-the-y-axis ? – ottlngr Mar 13 '17 at 12:10
  • It doesn't seem to make any difference. I should clarify I'm using ggplotly – HCAI Mar 13 '17 at 12:18
  • I am getting `Error in eval(expr, envir, enclos) : object 'y' not found` for the `ggplot` call. – Maximilian Peters Mar 13 '17 at 18:18
  • 1
    you example does not work; I have you tried something like pb %>% layout(margin = list(t=150, l = 150, r =150, b=150)) – MLavoie Mar 13 '17 at 21:42

3 Answers3

20

Let's use a simple reproducible example from here.

library(gapminder)
library(plotly)
p <- ggplot(gapminder, aes(x=gdpPercap, y=lifeExp)) + geom_point() + scale_x_log10()
p <- p + aes(color=continent) + facet_wrap(~year)
gp <- ggplotly(p)

enter image description here

We can move the adjust the margins as suggested by MLavoie but then our axis legend moves as well.

gp %>% layout(margin = list(l = 75))

enter image description here

The axis label is actually not a label but an annotation, so let's move it first. You can query the structure of the annotations in the graph gp:

# find the annotation you want to move
str(gp[['x']][['layout']][['annotations']]) 

List of 15
 $ :List of 13
  ..$ text          : chr "gdpPercap"
  ..$ x             : num 0.5
  ..$ y             : num -0.0294
  ..$ showarrow     : logi FALSE
  ..$ ax            : num 0
  ..$ ay            : num 0
  ..$ font          :List of 3
  .. ..$ color : chr "rgba(0,0,0,1)"
  .. ..$ family: chr ""
  .. ..$ size  : num 14.6
  ..$ xref          : chr "paper"
  ..$ yref          : chr "paper"
  ..$ textangle     : num 0
  ..$ xanchor       : chr "center"
  ..$ yanchor       : chr "top"
  ..$ annotationType: chr "axis"
 $ :List of 13
  ..$ text          : chr "lifeExp"
  ..$ x             : num -0.0346
  ..$ y             : num 0.5
.... <truncated>

Ok, so annotations are stored in a list of 15; "lifeExp" is the second([[2]]) element of this list. The "x" ([['x']]) and "y" values control the movement left and right/up and down in this case, respectively.

# Check current x-location of x-axis label
gp[['x']][['layout']][['annotations']][[2]][['x']]
[1] -0.03459532

# Move the label further to the left
gp[['x']][['layout']][['annotations']][[2]][['x']] <- -0.1
gp %>% layout(margin = list(l = 75))

enter image description here

Nova
  • 5,423
  • 2
  • 42
  • 62
Maximilian Peters
  • 30,348
  • 12
  • 86
  • 99
  • Thank you! This works a treat! Do you have any references for this code or documentation? Is it a plotly thing or a ggplot feature you are tweaking? – HCAI Mar 14 '17 at 09:51
  • @HCAI: glad it worked! margins is a plotly layout attribute (I added a link to the original answer), the 2nd part is a tweak/hack of the ggplot to plotly conversion result. The conversion usually works pretty nice but sometimes it needs a bit help. – Maximilian Peters Mar 15 '17 at 02:21
  • 1
    When I try this I get: Error in `*tmp*`[[2]] : subscript out of bounds – Michael Szczepaniak Nov 27 '17 at 17:54
  • @MichaelSzczepaniak: I just tried it again and the code still works for me. Can you post your code and setup as a separate question? – Maximilian Peters Nov 27 '17 at 18:30
  • @Max: Thx for the reply. I posted another question with code here: https://stackoverflow.com/questions/47523919/converting-ggplot-object-to-plotly-object-creates-axis-title-that-overlaps-tick – Michael Szczepaniak Nov 28 '17 at 04:17
  • @Nova: Thanks for the suggested edit. In cases like this, i.e. substantial edits, feel free to add another answer. – Maximilian Peters Apr 12 '19 at 21:42
  • Cool, thanks. I liked your answer - thanks for taking the time. – Nova Apr 15 '19 at 12:37
5

Find an answer from Github that solved this problem elegantly.

layout_ggplotly <- function(gg, x = -0.02, y = -0.08){
  # The 1 and 2 goes into the list that contains the options for the x and y axis labels respectively
  gg[['x']][['layout']][['annotations']][[1]][['y']] <- x
  gg[['x']][['layout']][['annotations']][[2]][['x']] <- y
  gg
}
gp %>% layout_ggplotly
C.Wang
  • 186
  • 2
  • 3
2

I found both answers above quite useful. However, I noticed that the layout_ggplotly() function given above works correctly only when x and y axis titles exist. If either of those is missing -- for example, as a result of theme(axis.title.x = element_blank()) -- then positional reference using ...[[1]]... and ...[[2]]... refers to wrong titles / annotations.

I wrote a function that I used in my project to address such limitation, I believe this builds up on the previous answers.

annotatify <- function(gg, x.y = -0.05, y.x = -0.05) {
  wip <- gg[['x']][['layout']][['annotations']] %>% 
    enframe() %>%
    mutate(value = map(value, as_tibble)) %>% 
    unnest(cols = c(value)) %>% 
    filter(!is.na(annotationType)) %>% 
    mutate(
      x = case_when(x == 0.5 ~ x, TRUE ~ x.y),
      y = case_when(y == 0.5 ~ y, TRUE ~ y.x)
    ) %>% 
    select(name, text, x, y) %>% 
    unique()

  if (nrow(wip) == 2) {
    for (i in 1:nrow(wip)) {
      if (wip$x[i] == 0.50) {
        gg[['x']][['layout']][['annotations']][[1]][['y']] <- wip$y[i]
      } else {
        gg[['x']][['layout']][['annotations']][[2]][['x']] <- wip$x[i]
      }
    }
  } else if (wip$y == 0.5) {
    gg[['x']][['layout']][['annotations']][[1]][['x']] <- wip$x
  } else {
    gg[['x']][['layout']][['annotations']][[1]][['y']] <- wip$y
  }

  gg
}
Dmitry Ishutin
  • 396
  • 2
  • 13