tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.45k stars 2.02k forks source link

When setting "scales" in facet_grid, the horizontal justification of the axis labels shifts among the panels #5820

Closed ujtwr closed 1 week ago

ujtwr commented 5 months ago

Case1

Panel "F1" has 3 values and Panel "F2" has other 3 values. We use facet_grid without "scale" option, Axis Y of both panels has same 6 values. So, if we set "hjust = 0" in "element_text" on "axis.text.y", Text of Y axis are aligned left.

tibble(
  F = c(rep("F1", 3), rep("F2", 3)),
  Y = c("AAAAAAAAAAAAAAAAAAA", "BBBBB", "CCCCCCCCC", "DDDDD", "EEEEEEEE", "FFF"),
  Cnt = c(10, 20, 30, 50, 40 ,60)
) %>% 
  ggplot(mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F)
  ) +
  theme(
    axis.text.y = element_text(hjust = 0)
  )

Rplot

Case2

Next, we add an option "scales = free_y" on facet_grid function, panel F1 and F2 has other values that have different width. So, the alignment position of the text varies depending on the panel.

tibble(
  F = c(rep("F1", 3), rep("F2", 3)),
  Y = c("AAAAAAAAAAAAAAAAAAA", "BBBBB", "CCCCCCCCC", "DDDDD", "EEEEEEEE", "FFF"),
  Cnt = c(10, 20, 30, 50, 40 ,60)
) %>% 
  ggplot(mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F),
    scales = "free_y"
  ) +
  theme(
    axis.text.y = element_text(hjust = 0)
  )

Rplot02

teunbrand commented 5 months ago

Hi there, thanks for the report! To be clear, both axes have left-aligned text, it is just that there is additional space on the bottom axes due to misalignment with the top axis.

The issue occurs because the two guides are drawn independently of oneanother, unaware of dimensions of other axes in the same direction. Having dabbled a bit in the guide code, I can say with some confidence that it'll not be straightforward to line up the labels.

ujtwr commented 5 months ago

For example, when moving the label of a panel to the left side, I would like the Y-axis text to align along the panel.

tibble(
  F = c(rep("F1", 3), rep("F2", 3)),
  Y = c("AAAAAAAAAAAAAAAAAAA", "BBBBB", "CCCCCCCCC", "DDDDD", "EEEEEEEE", "FFF"),
  Cnt = c(10, 20, 30, 50, 40 ,60)
) %>% 
  ggplot(mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F),
    scales = "free_y",
    switch = "y"
  ) +
  theme(
    axis.text.y = element_text(hjust = 0),
    strip.text.y.left = element_text(angle = 0),
    strip.placement = "outside"
  )

image

teunbrand commented 5 months ago

Thanks, the intent is clear 👍 It is implementation-wise that this could be tricky.

smouksassi commented 5 months ago

in your case patchwork can help you doing what you want

library(patchwork)
library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

dataggplot <- tibble(
  F = c(rep("F1", 3), rep("F2", 3)),
  Y = c("AAAAAAAAAAAAAAAAAAA", "BBBBB", "CCCCCCCCC", "DDDDD", "EEEEEEEE", "FFF"),
  Cnt = c(10, 20, 30, 50, 40 ,60)
) 
a1 <-   ggplot(dataggplot %>% 
           filter(F=="F1"),
         mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F),
    scales = "free_y",
    switch = "y"
  ) +
  theme(
    axis.text.y = element_text(hjust = 0),
    strip.text.y.left = element_text(angle = 0),
    strip.placement = "outside"
  )+
  coord_cartesian(xlim=c(NA,60))+
  theme(axis.title.x = element_blank(),axis.ticks.x.bottom = element_blank(),
        axis.text.x.bottom = element_blank())
a2 <- ggplot(dataggplot %>% 
         filter(F=="F2"),
       mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F),
    scales = "free_y",
    switch = "y"
  ) +
  theme(
    axis.text.y = element_text(hjust = 0),
    strip.text.y.left = element_text(angle = 0),
    strip.placement = "outside"
  )+
  coord_cartesian(xlim=c(NA,60))

a1/a2 +
  plot_layout(axis_titles = "collect_y")

Created on 2024-04-04 with reprex v2.1.0

teunbrand commented 5 months ago

@smouksassi I think the intent is to change the 'whitespace' area for the lower series of axis labels from left to right w.r.t the labels. That'd left-align all the y-axis labels. Like the following picture that I have expertly edited using MS paint:

@ujtwr please correct me if I'm misinterpreting the request

smouksassi commented 5 months ago

a quick fix would be to manually pad with spaces until you have a simiar strwidth this is not a solution but prevent you form needing to manually edit your plot

library(stringr)
library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

dataggplot <- tibble(
  F = c(rep("F1", 3), rep("F2", 3)),
  Y = c("AAAAAAAAAAAAAAAAAAA", "BBBBB", "CCCCCCCCC", "DDDDD", "EEEEEEEE",
        "FFF                                       "),
  Cnt = c(10, 20, 30, 50, 40 ,60)
) %>% 
  mutate(Ypad= stringr::str_pad(Y, width = 20, side = "right",pad=" ",use_width=FALSE))

strwidth(dataggplot$Y)
#> Error in strwidth(dataggplot$Y): plot.new has not been called yet

ggplot(dataggplot,
               mapping = aes(y = Y, x = Cnt)) +
  geom_col() +
  facet_grid(
    rows = vars(F),
    scales = "free_y",
    switch = "y"
  ) +
  theme(text = element_text(size=16),
    axis.text.y = element_text(hjust = 0),
    strip.text.y.left = element_text(angle = 0),
    strip.placement = "outside"
  )

Created on 2024-04-04 with reprex v2.1.0

ujtwr commented 5 months ago

@smouksassi Thank you for the suggestion. I have already tried it, but I'm a Japanese user, so sometimes there can be misalignment when padding with white spaces.