teunbrand / ggh4x

ggplot extension: options for tailored facets, multiple colourscales and miscellaneous
https://teunbrand.github.io/ggh4x/
Other
541 stars 33 forks source link

[FR] different themes for levels in facet_nested #40

Closed pank closed 3 years ago

pank commented 3 years ago

facet_nested is really great. I use for having nested "boxes" on my y-axis.

It would be very useful if different theme elements could be used for different levels of the nested facet so that inner facets could have a different background, say. It's not too hard to do this afterward with grid, but it would be much nicer to use theme.

Would something like that be feasible? I am also happy to try to write a patch.

Example:

library(ggplot2)
library(grid)
library(ggh4x)
library(patchwork)

change_inner_fill <- function(ggobj,
                               inner_re = "strip.[0-9]+-[0-9]+-[0-9]+-2",
                               fill = "green") {
    ## https://stackoverflow.com/a/53457599
    gg <- grid.force(ggplotGrob(ggobj))
    grobs_df <- do.call(cbind.data.frame, grid.ls(gg, print = FALSE))
    grobs_df <- within(grobs_df, {
        gPath_full <- paste(gPath, name, sep = "::")
        gPath_full <- gsub(pattern = "layout::", 
                           replacement = "", 
                           x = gPath_full, 
                           fixed = TRUE)})
    strips <- grep(paste0(inner_re, ".*rect.*"),
                   grobs_df$gPath_full, value = TRUE)
    for (i in seq_along(strips)){
        gg <- editGrob(gg, strips[i], gp = gpar(fill = fill))
    }
invisible(gg)
}

df <- data.frame(outer = c("A","B", "B", "C", "C", "C"),
                   inner = paste("Lorem ipsum dolor sit amet", 1:6),
                   x=1, y =1)

theme_set(theme_get() + theme(strip.text.y.left =  element_text(angle = 0),))

g1 <- ggplot(df, aes(x,y)) +
    geom_point() +
    facet_nested(outer + inner ~ .,
                 scales = "free_y",
                 switch = "y",
                 space = "free_y") +
    scale_y_discrete(label = NULL)
grid.draw(change_inner_fill(g1))
## It would be nice to instead write g1 + them(strip.background.y.level-2 = element_rect(fill = "green"))

image

teunbrand commented 3 years ago

Hello there,

I've seen more requests like this, in private email and on stackoverflow. This makes me think that it probably is a more widely requested feature.

It can probably be implemented, but I'd have to define separate theme elements for every layer I'd support. E.g. strip.text.x.bottom.level0, strip.text.x.bottom.level1, strip.background.x.bottom.level0, strip.background.x.bottom.level1 etc., and this is just for two levels for strips on one side of the plot. You can probably see this become cumbersome pretty quickly.

The alternative I had in mind, is to implement guide_strips() in a similar fashion as ggplot2 has guide_axis(), guide_legend() and guide_colourbar(). These can then have variants like guide_strips_nested() and the like, and can probably be made to facilitate different styles per layer.

In any case, I don't have the time to implement something like this in the near future, but I might do this once I have some extra time.

Best, Teun

teunbrand commented 3 years ago

Alright I've merged some strip related changes into the main branch. The following should now work with the github version:

library(ggplot2)
library(ggh4x)

df <- data.frame(outer = c("A","B", "B", "C", "C", "C"),
                 inner = paste("Lorem ipsum dolor sit amet", 1:6),
                 x=1, y =1)

theme_set(theme_get() + theme(strip.text.y.right =  element_text(angle = 0)))

ggplot(df, aes(x,y)) +
  geom_point() +
  facet_nested(
    outer + inner ~ .,
    scales = "free_y",
    space = "free_y",
    strip = strip_nested(
      background_y = list(NULL, element_rect(fill = "green")),
      by_layer_y = TRUE
    )
  )

Created on 2021-06-29 by the reprex package (v1.0.0)

If you find any bugs, please let me know :)

wvictor14 commented 11 months ago

Am wondering if it is possible to apply an alternating theme to only one layer of nested strips?

like this:

image

teunbrand commented 11 months ago

No there isn't, you'd have to set this manually I'm afraid.