Skip to content
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

A way to specify defaults for subguide, subsccale and normalize? #225

Closed
venpopov opened this issue Apr 6, 2024 · 2 comments
Closed

A way to specify defaults for subguide, subsccale and normalize? #225

venpopov opened this issue Apr 6, 2024 · 2 comments

Comments

@venpopov
Copy link

venpopov commented Apr 6, 2024

Thanks for solving #219. I'm trying to come up with a easier to way to write all of this:

data.frame(prior = c("student_t(3,2,0.75)", "normal(0,5)")) %>% 
  parse_dist(prior) %>% 
  ggplot(aes(xdist = .dist_obj)) +
  stat_slab(fill = NA, color = "red",
            subguide = subguide_outside(title = "density"),
            subscale = subscale_thickness(expand = expansion(c(0, 0.05))),
            normalize = 'groups') +
  facet_wrap(~prior, scales = "free") +
  ggdist::theme_ggdist() +
  theme(plot.margin = margin(5.5, 5.5, 5.5, 50),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank())

I like this format where the y scale is completely replaced by the thickness scale as it looks more like what I would get from normal use of ggplot2, but I don't want to type all of that, and I would like to also define defaults for our package. Some of the things above I can of course extract into a custom theme:

theme_dist <- function() {
  ggdist::theme_ggdist() +
    theme(plot.margin = margin(5.5, 5.5, 5.5, 50),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank())
}

leading to the more compact:

data.frame(prior = c("student_t(3,2,0.75)", "normal(0,5)")) %>% 
  parse_dist(prior) %>% 
  ggplot(aes(xdist = .dist_obj)) +
  stat_slab(fill = NA, color = "red",
            subguide = subguide_outside(title = "density"),
            subscale = subscale_thickness(expand = expansion(c(0, 0.05))),
            normalize = 'groups') +
  facet_wrap(~prior, scales = "free") +
  theme_dist()

but I haven't figure out a way to define the subguide, subscale and normalize arguments to use the above settings by default. Of course, I could make a wrapper for stat_slab:

stat_slab2 <- function(..., 
  subguide = subguide_outside(title = "density"),
  subscale = subscale_thickness(expand = expansion(c(0, 0.05))),
  normalize = 'groups') {
  
  stat_slab(..., subguide = subguide, subscale = subscale, normalize = normalize)
}

allowing me to use

data.frame(prior = c("student_t(3,2,0.75)", "normal(0,5)")) %>% 
  parse_dist(prior) %>% 
  ggplot(aes(xdist = .dist_obj)) +
  stat_slab2(fill = NA, color = 'red') +
  facet_wrap(~prior, scales = "free") +
  theme_dist()

but I was wondering if there's a way to specify those arguments without making a wrapper function?

(in practice we are making an autoplot() function, for which this doesn't matter, but for myself I want to leave the option for more control while still using defaults I like)

@mjskay mjskay closed this as completed in 1b6fe2c Apr 6, 2024
mjskay added a commit that referenced this issue Apr 6, 2024
@mjskay
Copy link
Owner

mjskay commented Apr 6, 2024

Hmm yeah. If I was doing this I would probably either define a custom function like you have, or at least save a subguide and subscale for reuse, like:

sg = subguide_outside(title = "density")
sc = subscale_thickness(expand = expansion(c(0, 0.05)))

# later  ..
stat_slab(subscale = sc, subguide = sg)

But I can see the value of setting at least subguides and subscales globally. Fortunately the function search path for string suffixes passed to those functions already uses the global environment. This made it easy to implement a version of what base ggplot does to allow scale defaults to be set. ggplot lets you set default scales by creating a function with the same name as the default (e.g. scale_x_continuous = ...), and I've implemented something similar with subguides and subscales.

You can now set subscale_thickness, subguide_slab, subguide_dots, and subguide_spike to change the corresponding defaults; e.g.:

theme_set(
  ggdist::theme_ggdist() +
  theme(plot.margin = margin(5.5, 5.5, 5.5, 50),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank())
)

# need `ggdist::` for to prevent infinite recursion here
subscale_thickness = ggdist::subscale_thickness(expand = expansion(c(0, 0.05)))
# need theme = theme_ggdist() because otherwise the guide uses the default
# axis theme elements, which are set to blank above
subguide_slab = ggdist::subguide_outside(title = "density", theme = theme_ggdist())

data.frame(prior = c("student_t(3,2,0.75)", "normal(0,5)")) |>
  parse_dist(prior) |>
  ggplot(aes(xdist = .dist_obj)) +
  stat_slab(fill = NA, color = "red", normalize = "groups") +
  facet_wrap(~prior, scales = "free")

image

I don't think I will provide a similar mechanism for normalize (or per #223, subscale_by), for several reasons: (1) it's not a lengthy parameter, (2) it's not a theme-related parameter (so it feels less sensible to modify it at a global level --- too much potential to break others' code), and (3) I think at that point it would be better to have a generic mechanism for updating geom/stat parameters, which would be more of a ggplot2-level thing. TBH you probably already could do that by just modifying the $default_params list of a geom object (similar to what ggplot2::update_geom_defaults() does for setting default aesthetic mappings). However, I don't really recommend this, as it has too much potential to break other people's code.

@venpopov
Copy link
Author

venpopov commented Apr 7, 2024

Thanks, this works. Ideally it would have been nice to be able to setup that from the theme, but I see your point that this is a ggplot issue, not a ggdist issue. I can set these as global variables from the theme function, but that's not good practice, just like I agree without you that alterin default aestatics can break others code. So I might just go with the wrapper function for stat_slab, which seems the cleanest solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants