ggplot2
ggplot2 copied to clipboard
support sec.axis for scale_discrete
scale_y_discrete() and scale_x_discrete() currently appear not to support the sec.axis argument of their scale_continuous() brethren.
sec.axis would be nice for discrete scales as well, such as when you have various ways to refer to factor levels (short and long) and the like.
I second this request, and for the same reasons as @maxheld83 mentioned. This would be a really nice feature, which in the current ggplot2 version cannot be achieved even with hacks.
Why was this closed? There still seems to be only this workaround available: https://stackoverflow.com/a/45362497/1842673 Could this be implemented?
The issue is still open, as far as I can tell. If somebody would like to contribute a PR, we'd certainly review it.
It didn't make sense to tackle this feature before @paleolimbot had completed his rewrite of position scales, but since that has happened and is merged I'd assume implementing discrete secondary axes shouldn't be too hard.
Famous last words!
I think a guide extension is the way to go about this (actually, I think regular second axes should use this as well). It's not future proof since we're about to redo guides (post patch release), but you could hack this with the current system:
library(ggplot2)
guide_axis_label_trans <- function(label_trans = identity, ...) {
axis_guide <- guide_axis(...)
axis_guide$label_trans <- rlang::as_function(label_trans)
class(axis_guide) <- c("guide_axis_trans", class(axis_guide))
axis_guide
}
guide_train.guide_axis_trans <- function(x, ...) {
trained <- NextMethod()
trained$key$.label <- x$label_trans(trained$key$.label)
trained
}
ggplot(mpg, aes(class, hwy)) +
geom_boxplot() +
guides(x.sec = guide_axis_label_trans(~paste("Ha!", .x)))

Created on 2020-04-15 by the reprex package (v0.3.0)
was searching for something similar and found the SO thread linking to here. +1 this useful feature request. Thanks @paleolimbot for the hack
I also came here from SO looking for a way to duplicate the discrete y scale. +1 this would be a nice feature to see in future versions. Thanks!
I think it would be nice to have this feature.
I would like to use sec.axis for scale_y_discrete for plotting a second geom: geom_density_ridges. The y-axis for the geom_histogram plot is continuous, while the desired y-axis for the geom_density_ridges plot is discrete.
Example:
p1 <- ggplot()
# scale for geom_histogram
p1 <- p1 + geom_histogram(data=df, aes(x=xVar, y=..density.., fill=..x.., color=..x..), alpha=0.4, bins=25, position = "identity")
p1 <- p1 + geom_rug(data=df, aes(x=xVar, color=..x..), alpha = 0.15, position = "identity")
p1 <- p1 + scale_fill_distiller(palette="YlGnBu", direction=1)
p1 <- p1 + scale_color_distiller(palette="YlGnBu", direction=1)
# new scale for geom_density_ridges
p1 <- p1 + new_scale_color()
p1 <- p1 + geom_density_ridges(data=df, aes(x=xVar, y=..density.., group=factorVar), stat = "identity")
p1 <- p1 + scale_color_brewer(palette="YlGnBu")
# what should I do below?
p1 <- p1 + scale_y_discrete(sec.axis = sec_axis())
p1 %>% print()
Take care and many thanks
I have just arrived here from SO. Please, it would be great to have this option.
I would love this!
Yes, also a great idea from my perspective!
I also support this feature request ... even if the xaxis label could just be duplicated without any transformation ... this would be very useful for large / long plots to have the same label on the top and bottom (or left and right, respectively).
I found this package: {ggh4x}, library(ggh4x)
here is the resource
library(ggh4x)
#> Loading required package: ggplot2
df <- data.frame(x = seq(-3, 3, length.out = 6), y = LETTERS[1:6])
g <- ggplot(df, aes(x, y)) +
geom_col() +
scale_x_continuous(breaks = -3:0, guide = "axis_truncated",
sec.axis = dup_axis(breaks = 0:3, guide = "axis_truncated")) +
theme(axis.line.x = element_line())
g + guides(y.sec = guide_axis_manual(
breaks = unit(c(1, 3), "cm"),
labels = expression("treshold"^2, "annotation"[3]),
label_colour = c("red", "blue"), label_size = c(8, 12)
))
Created on 2021-10-25 by the reprex package [(v2.0.1)
If you run this code you'll see that you can change/customize your secondary axis even with discrete values. Hope it helps.
Joining the others to say this would be nice to have. When you have bipolar questions on surveys, it's nice to put labels on the left and right, just as it appears in the survey itself.
Agreed! Just here to leave my temporary solutions for others:
# load packages
library(dplyr)
library(ggplot2)
# create some data
categories <- c("a", "b")
other_labels <- c("apples", "bananas")
dat <- data.frame(numeric_var = 1:10, categorical_var = rep(categories, 5))
# change variable type
dat_mutated <- dat %>%
mutate(categorical_var = as.numeric(factor(categorical_var, levels = categories, ordered = TRUE)))
# create plot
dat_mutated %>%
ggplot(aes(x = categorical_var, y = numeric_var)) +
geom_tile() +
ggplot2::scale_x_continuous(
breaks = unique(dat_mutated$categorical_var),
labels = categories,
sec.axis = ggplot2::dup_axis(
labels = other_labels,
name = "catecorical_var with different labels"
)
)
Agreed! This would be very useful for omics expression heatmaps, where you might want to refer to the accession number as well as the full title of a protein on your plot! +1
Seconded.
Would also like to be able to duplicate categorical axes
I support this feature request. Thanks!
Commenting to keep this in favor.
Famous last words!
I think a guide extension is the way to go about this (actually, I think regular second axes should use this as well). It's not future proof since we're about to redo guides (post patch release), but you could hack this with the current system:
library(ggplot2) guide_axis_label_trans <- function(label_trans = identity, ...) { axis_guide <- guide_axis(...) axis_guide$label_trans <- rlang::as_function(label_trans) class(axis_guide) <- c("guide_axis_trans", class(axis_guide)) axis_guide } guide_train.guide_axis_trans <- function(x, ...) { trained <- NextMethod() trained$key$.label <- x$label_trans(trained$key$.label) trained } ggplot(mpg, aes(class, hwy)) + geom_boxplot() + guides(x.sec = guide_axis_label_trans(~paste("Ha!", .x)))
Created on 2020-04-15 by the reprex package (v0.3.0)
This works great, but I'm wondering how to modify the following line to use the values of a different variable: guides(x.sec = guide_axis_label_trans(~paste("Ha!", .x)))
I want something like guides(x.sec = guide_axis_label_trans(~Unit)) to call the values of my "Unit" character vector.
Any guidance would be much appreciated. and a +1 for secondary axes for discrete scales!
thanks!
You should be able to run
guides(x.sec = guide_axis_label_trans(~paste0(levels(Unit))))
Worked for my issue. Works great for second y axis too. Glad I found this! Thanks @paleolimbot !
I think a guide extension is the way to go about this (actually, I think regular second axes should use this as well).
I also think it would be nice to move all secondary axis responsibility from the scales system to the guides system, once that has been overhauled.
Hi,
The guide_axis_label_trans hack worked wonders for me and fully synergises with the axis.text.x.top (and similar) theme elements!
Is there a plan to integrate this into a ggplot2 official release? Or is it already done and I have missed it?
Many thanks! Enrique
Has anyone seen a fix for this when using facets in ggplot? Thanks Michelle
Is there a way to add a secondary axis title using the guide_axis_label_trans workaround?
Is there any good news about that? It's been 2019, now it's 2023...
I find another way to build a second axis by package patchwork. you can build a new plot only contain your axis and patch togather.
Example for a new x axis in top: library(patchwork) p1=ggplot(data,aes(x,1)+scale_x_discrete(position = 'top')+ theme(plot.margin = unit(c(0,1,0,1),'cm'),axis.line.y = element_blank(), axis.text.y = element_blank(),axis.ticks.y = element_blank())+ labs(y=NULL,x=NULL)
p1/p2 + plot_layout(heights = c(0,1))
p2 is your plot need to add axis
you also can change axes style as you want (like ggprism)