1

I am trying to duplicate/mirror y-axis tick-marks of different sizes for the secondary y-axis using ggplot2 and gtable libraries.

I have successfully shortened minor tick-marks for the primary(left) y-axis by referring to this answer. Now, I am trying to duplicate or mirror them on secondary(right) y-axis. I have tried two approaches but neither has produced the desired result.

In the first approach I apply the same method I used for the left axis:

library("ggplot2")
require("scales")
library("ggthemes")
library("grid")
library("gtable")
labs = seq(0,100,10)
labs[!!((seq_along(labs)-1)%%5)] = ''
g <- ggplot(data.frame(x = 1:10, y = (1:10)^2), aes(x,y)) +
  geom_point() +
  scale_y_continuous(breaks = seq(0,100,10), labels = labs, sec.axis = dup_axis(name = NULL, labels = NULL)) +
  theme(axis.ticks.length=unit(10, "pt"), plot.margin = margin(10, 16, 0, 12),panel.grid = element_blank())


gg <- ggplotGrob(g)
yaxisR <- gg$grobs[[which(gg$layout$name == "axis-r")]]
yaxisL <- gg$grobs[[which(gg$layout$name == "axis-l")]] 
ticksR <- yaxisR$children[[2]]
ticksL <- yaxisL$children[[2]]
marksR <- ticksR$grobs[[1]]
marksL <- ticksL$grobs[[2]]
marksR$x[c(2:5,7:10)*2-1] <- unit(1, "npc") - unit(5, "pt")
marksL$x[c(2:5,7:10)*2-1] <- unit(1, "npc") - unit(5, "pt")

ticksR$grobs[[1]] <- marksR
ticksL$grobs[[2]] <- marksL
yaxisR$children[[2]] <- ticksR
yaxisL$children[[2]] <- ticksL
gg$grobs[[which(gg$layout$name == "axis-r")]] <- yaxisR
gg$grobs[[which(gg$layout$name == "axis-l")]] <- yaxisL
grid.newpage()
grid.draw(gg)

But the right axis tick-marks get shortened from the wrong side: first result

In the second approach I try to mirror the primary y-axis tick-marks by using this answer :

library("ggplot2")
require("scales")
library("ggthemes")
library("grid")
library("gtable")
labs = seq(0,100,10)
labs[!!((seq_along(labs)-1)%%5)] = ''
g <- ggplot(data.frame(x = 1:10, y = (1:10)^2), aes(x,y)) +
  geom_point() +
  scale_y_continuous(breaks = seq(0,100,10), labels = labs) +
  theme(axis.ticks.length=unit(10, "pt"), plot.margin = margin(10, 16, 0, 12), panel.grid = element_blank())


gg <- ggplotGrob(g)
panel <-c(subset(gg$layout, name=="panel", se=t:r))
yaxisL <- gg$grobs[[which(gg$layout$name == "axis-l")]] 

ticksL <- yaxisL$children[[2]]
marksL <- ticksL$grobs[[2]]
marksL$x[c(2:5,7:10)*2-1] <- unit(1, "npc") - unit(5, "pt")

ticksL$grobs[[2]] <- marksL
yaxisL$children[[2]] <- ticksL
gg$grobs[[which(gg$layout$name == "axis-l")]] <- yaxisL

gg <- gtable_add_cols(gg, unit(0, "lines"), panel$r)
gg <- gtable_add_grob(gg, marksL, t = panel$t, l = panel$r+1, name = "ticks")

gg$layout[gg$layout$name == "ticks", ]$clip = "off"

grid.newpage()
grid.draw(gg)

This way results in the right axis tick marks pointing in the wrong direction: second result

Is there anything I can do to either code to have both y-axises identical and mirroring each other? Thank you!

1 Answers1

1

I tried a few things, and I'm not sure I understand it fully, but here is a solution:

The object marksR$x consist of 2 elements per tick line. I think these are the left and right ends of the line. Which means, you only have to add:

marksR$x[c(2:5,7:10)*2] <- unit(0, "npc")

While also keeping

marksR$x[c(2:5,7:10)*2-1] <- unit(1, "npc") - unit(5, "pt")

Which works for me

P1storius
  • 917
  • 5
  • 12
  • Thank you @MKBakker! Your solution works for me, and I am sure other people will find very useful. Grid graphical objects (grobs) confuse me quite a bit, and I wish there was an easier way to accomplish this task in ggplot2. By 2 elements per tick line did you mean these? `head(marksR$x) [1] 0npc 10pt 0npc 10pt 0npc 10pt` – Gennadii Prykhodko Aug 06 '19 at 14:47
  • 1
    Correct, the length of `marksR$x` is 2x the number of tick marks. That is why the indexes `c(2:5,7:10)*2-1` are used in the original method. It selects the first of each pair of tick mark x-values. And leave the other one unchanged. With my suggestion, you (also) change the second value of each pair of tick mark x-values. – P1storius Aug 06 '19 at 15:01