ggplot2 icon indicating copy to clipboard operation
ggplot2 copied to clipboard

support sec.axis for scale_discrete

Open maxheld83 opened this issue 6 years ago • 28 comments

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.

maxheld83 avatar Mar 03 '19 16:03 maxheld83

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.

thiagoveloso avatar May 10 '19 15:05 thiagoveloso

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

topialla avatar Apr 15 '20 07:04 topialla

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.

clauswilke avatar Apr 15 '20 14:04 clauswilke

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)

paleolimbot avatar Apr 15 '20 14:04 paleolimbot

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

rsaporta avatar May 11 '20 18:05 rsaporta

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!

EnricGTorrents avatar May 19 '20 13:05 EnricGTorrents

I think it would be nice to have this feature.

matteodefelice avatar Jul 20 '20 14:07 matteodefelice

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

ghost avatar Oct 07 '20 17:10 ghost

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

saul-torres avatar Nov 02 '20 19:11 saul-torres

I would love this!

marton-balazs-kovacs avatar Jan 19 '21 16:01 marton-balazs-kovacs

Yes, also a great idea from my perspective!

smped avatar Mar 02 '21 14:03 smped

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

rocanja avatar Jun 23 '21 08:06 rocanja

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.

Fgazzelloni avatar Oct 25 '21 21:10 Fgazzelloni

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.

markhwhiteii avatar Mar 07 '22 19:03 markhwhiteii

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

hanneoberman avatar Mar 08 '22 10:03 hanneoberman

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

juliabandura avatar Mar 18 '22 18:03 juliabandura

Seconded.

chsuong avatar Apr 25 '22 19:04 chsuong

Would also like to be able to duplicate categorical axes

kimlorenz avatar Jun 29 '22 17:06 kimlorenz

I support this feature request. Thanks!

mmpust avatar Jul 29 '22 22:07 mmpust

Commenting to keep this in favor.

xiaofanliang avatar Oct 10 '22 15:10 xiaofanliang

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!

KateSchneider-FoodPol avatar Dec 07 '22 00:12 KateSchneider-FoodPol

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 !

kjfuller06 avatar Jan 24 '23 01:01 kjfuller06

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.

teunbrand avatar Jan 24 '23 19:01 teunbrand

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

enblacar avatar Feb 13 '23 09:02 enblacar

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

Meech81 avatar Mar 06 '23 12:03 Meech81

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

abesolberg avatar Mar 10 '23 13:03 abesolberg

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

Generalized avatar Jul 11 '23 03:07 Generalized

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)

liubiaodi avatar Jan 01 '24 06:01 liubiaodi