Skip to content

support sec.axis for scale_discrete #3171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
maxheld83 opened this issue Mar 3, 2019 · 28 comments · Fixed by #5620
Closed

support sec.axis for scale_discrete #3171

maxheld83 opened this issue Mar 3, 2019 · 28 comments · Fixed by #5620
Labels
feature a feature request or enhancement scales 🐍

Comments

@maxheld83
Copy link

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.

@thomasp85 thomasp85 added the feature a feature request or enhancement label Apr 11, 2019
@thiagoveloso
Copy link

thiagoveloso commented May 10, 2019

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.

@topialla
Copy link

Why was this closed? There still seems to be only this workaround available:
https://stackoverflow.com/a/45362497/1842673
Could this be implemented?

@clauswilke
Copy link
Member

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.

@paleolimbot
Copy link
Member

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)

@rsaporta
Copy link
Contributor

was searching for something similar and found the SO thread linking to here. +1 this useful feature request. Thanks @paleolimbot for the hack

@EnricGTorrents
Copy link

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!

@matteodefelice
Copy link

I think it would be nice to have this feature.

@ghost
Copy link

ghost commented Oct 7, 2020

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

@saul-torres
Copy link

I have just arrived here from SO.
Please, it would be great to have this option.

@marton-balazs-kovacs
Copy link

I would love this!

@smped
Copy link

smped commented Mar 2, 2021

Yes, also a great idea from my perspective!

@rocanja
Copy link

rocanja commented Jun 23, 2021

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).

@Fgazzelloni
Copy link

Fgazzelloni commented Oct 25, 2021

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.

@markhwhiteii
Copy link

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.

@hanneoberman
Copy link

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"
    )
  )

@juliabandura
Copy link

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

@chsuong
Copy link

chsuong commented Apr 25, 2022

Seconded.

@kimlorenz
Copy link

Would also like to be able to duplicate categorical axes

@mmpust
Copy link

mmpust commented Jul 29, 2022

I support this feature request. Thanks!

@xiaofanliang
Copy link

xiaofanliang commented Oct 10, 2022

Commenting to keep this in favor.

@KateSchneider-FoodPol
Copy link

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!

@kjfuller06
Copy link

kjfuller06 commented Jan 24, 2023

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 !

@teunbrand
Copy link
Collaborator

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.

@enblacar
Copy link

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

@Meech81
Copy link

Meech81 commented Mar 6, 2023

Has anyone seen a fix for this when using facets in ggplot?
Thanks
Michelle

@abesolberg
Copy link

Is there a way to add a secondary axis title using the guide_axis_label_trans workaround?

@Generalized
Copy link

Is there any good news about that? It's been 2019, now it's 2023...

@liubiaodi
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement scales 🐍
Projects
None yet
Development

Successfully merging a pull request may close this issue.