1
library(tidyverse)
df <- data.frame(x = c(1, 2, 3, 4), 
                 y1 = c(1, 3, 2, 4), 
                 y2 = c(3000, 2000, 4000, 1000))
label_1 = c("1", "2", "3") %>% format(width = 5, justify = "right")
label_2 = c("1000", "2000", "3000") %>% format(width = 5, justify = "right")
p1 <- df %>% ggplot(aes(x = x, y = y1))+
  geom_line()+
  scale_y_continuous(breaks = c(1, 2, 3), 
                     labels = label_1)
p2 <- df %>% ggplot(aes(x = x, y = y2))+
  geom_line()+
  scale_y_continuous(breaks = c(1000, 2000, 3000), 
                     labels = label_2)
p1
p2

enter image description here enter image description here

In the above code, I try to make p1 and p2 to have the same length of y-axis label, by format(width = 5). In the actual graph, labels in p1 are still shorter than those in p2.

By trial and error, I get the right length when setting width =8.

label_3 = c("1", "2", "3") %>% format(width = 8, justify = "right")
p1 <- df %>% ggplot(aes(x = x, y = y1))+
  geom_line()+
  scale_y_continuous(breaks = c(1, 2, 3), 
                     labels = label_3)
p1

enter image description here

Could someone please explain this or guide me to the relevant previous posts?

Zhiqiang Wang
  • 6,206
  • 2
  • 13
  • 27
  • 1
    Relevant post? https://stackoverflow.com/questions/13294952/left-align-two-graph-edges-ggplot – zx8754 Feb 17 '22 at 08:30

1 Answers1

1

The regular space does not have the same width as a digits (in non-monospaced fonts). Unicode has a 'figure space' that has 'tabular width', i.e. the same width as digits. If you make a helper function that replaces spaces with the tabular widths (\u2007), both plots should have the same size for their y-axis. The function:

pad_numbers <- function(x, width, justify = "right") {
  x <- format(x, width = width, justify = justify)
  gsub(" ", "\u2007", x)
}

In action:

library(tidyverse)
df <- data.frame(x = c(1, 2, 3, 4), 
                 y1 = c(1, 3, 2, 4), 
                 y2 = c(3000, 2000, 4000, 1000))

label_1 = c("1", "2", "3") %>% pad_numbers(width = 5, justify = "right")
label_2 = c("1000", "2000", "3000") %>% pad_numbers(width = 5, justify = "right")
p1 <- df %>% ggplot(aes(x = x, y = y1))+
  geom_line()+
  scale_y_continuous(breaks = c(1, 2, 3), 
                     labels = label_1)
p2 <- df %>% ggplot(aes(x = x, y = y2))+
  geom_line()+
  scale_y_continuous(breaks = c(1000, 2000, 3000), 
                     labels = label_2)

p1

p2

Created on 2022-02-17 by the reprex package (v2.0.1)

Lastly, if you want the y-axis to have the same width for plot composition purposes, I recommend the {ragg} package, which does a good job at aligning the panels between two plots. This wouldn't require the helper function we wrote at the beginning.

teunbrand
  • 33,645
  • 4
  • 37
  • 63
  • Brilliant! It works perfectly for my purpose. I use it to generate many individual images for a video. Life is easier with smart people around. Thank you. – Zhiqiang Wang Feb 17 '22 at 08:07
  • 1
    Great, glad it helped. If you're using {gganimate} for animating plots, you typically wouldn't have to worry about the dimensions changing over the course of time. – teunbrand Feb 17 '22 at 09:47
  • I use ggplot to generate individual images, and then Python `moviepy` to produce final videos. – Zhiqiang Wang Feb 17 '22 at 10:05