tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.51k stars 2.03k forks source link

feature request to provide faceting via aesthetics #2751

Closed itcarroll closed 5 years ago

itcarroll commented 6 years ago

I think it is consistent with the grammar of graphics to call the mapping of a variable to a panel an aesthetic. Would it be possible to provide an alternative to facet_wrap and facet_grid that draws on an additional argument to aes? I teach ggplot2 in workshops, and have trouble justifying the logic of facet_* (both the old formula notation and the new use of vars). I think it would be more natural to allow a panel argument to aes as follows below this example taken from the facet_wrap docs.

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  facet_wrap(vars(class))

My suggestion is that the identical outcome be achieved with:

ggplot(mpg, aes(x = displ, y = hwy, panel = class)) +
  geom_point()

The panel argument could accept two variables for facet_grid behavior, and the remaining customization arguments in facet_* could be accessed with a scale_panel and panellabs functions.

jemus42 commented 6 years ago

The thing that immediately comes to mind for me is that each existing aesthetic has a corresponding scale_ function, e.g. colo[u]r has scale_colo[u]r_*.
Would there be something similar to that for the panel aesthetic?
What would scale_panel_* functions do?

itcarroll commented 6 years ago

The arguments for facet_wrap are presently a combination of scale and label customizations. I think scale_panel would adopt:

And panellab (a al xlab, ylab) would get

But I admit, I am fuzzy on the general principle of scale_* functions.

clauswilke commented 6 years ago

My 2 cents: I agree specifying facets via aesthetics makes sense. This is what Altair does, I believe. However, I don't see the need to throw out the facet_*() functions. They could simply be extended to derive faceting instructions from aesthetics in addition to the formula interface and vars(). I see several arguments for this:

  1. The facet_*() interface is extremely widely used and needs to be maintained going forward. So whatever changes are made must be compatible with it.

  2. Using scale_panel_*() instead of facet_*() seems a bit forced to me. I don't immediately see how things like breaks, limits, titles, transformations map to facets. One could probably make it work somehow but it's not clear that it would be less confusing than what we currently have. I doubt people commonly think of facets in terms of scales.

  3. The general convention in ggplot2 is that the aesthetic mapping by itself doesn't cause things to be drawn. So it's not clear to me that setting faceting aesthetics should do anything unless we also add something like facet_wrap() or facet_grid(). And, different faceting mechanism would need different aesthetics (wrap would need one, grid would need two), so it would make sense to have aesthetics that are specific to the facet type used and that get activated only once the facet is added.

@itcarroll Do you think that something like this would be less confusing to your students than what we currently have:

ggplot(mpg, aes(x = displ, y = hwy, facet = class)) +
  geom_point() + facet_wrap()
thomasp85 commented 6 years ago

I’m personally very much against this for several reasons. There’s a fundamental difference in specifying the layout of the plot and the visual appearance of graphic elements and I think mixing the two is theoretically inconsistent. Further it seems to me that mixing the two will be much less powerful as it assumes there’s a direct 1-1 mapping of data to panel which is not the case for the provided facets (e.g. with adding margins) and especially not with facets in extensions such as facet_zoom

itcarroll commented 6 years ago

Reply to @clauswilke's points:

  1. Absolutely! I would like the aesthetic route as an alternative not a replacement.
  2. Is it a stretch? Wickham 2010 says it is the job of scales to "convert the data units to aesthetic units, which have meaning to the underlying drawing system." It seems very much like the same kind of thinking that goes into scale_shape_discrete.
  3. I can't think how I would explain the need for that + facet_wrap(), after I've just said that the x and y axes are part of the aesthetic and apply to any rendered geometry. Would a z axis need a + layout_3D() layer? (I have no idea how/if ggplot2 already handles a z axes :)

@thomasp85 By "layout" do you mean margins and such? If so, I agree that's separate but I disagree that panels should then be considered part of the layout. I think of them as another axis.

Thanks to all for considering! I've made my request and will shut up now, unless there's another question addressed specifically to me.

baptiste commented 6 years ago

@thomasp85 I once argued against this too, but this discussion with gadfly's creator eventually convinced me otherwise. In an abstract sense, facets are positioning complex glyphs on a page, where the complex glyph is an entire subplot, and the positioning is (usually) a rectangular layout — subplots are mapped from an abstract space (whatever small multiples mean to the creator) on to (x,y) positions. Thinking of facetting more broadly, one could imagine:

The standard layout of wrap/grid is a special case with a simple layout: bin the plots along a rectangular grid. Margins aren't inconsistent with this perspective: much like complex geoms (boxplot, etc.) that derive/morph new positions/aesthetics from a stat/position/coord, the extra panel would be generated by a meta-stat.

This perspective also opens the way to new concepts such as scaling facets – background colour, size, shape, transparency, etc. which could turn out to be a powerful extension of the grammar (for instance, highlighting a specific panel by setting the alpha of all the others to almost transparent).

thomasp85 commented 6 years ago

Incan get on board with the idea that it is considered a meta-aesthetic, but that is, in my view, another reason why it should live outside of geom aesthetic specification.

For your point about animations I completely agree which is why the frame aesthetic was removed from the gganimate API and substituted for a more powerful transition_*() class more in line with the facet function. The other approach was simply too simplistic

flying-sheep commented 5 years ago

this way one could also deal with a hybrid facet_{grid,wrap} I often want, i.e. facet_grid with additional per-facet titles. currently this is only hackable via

data <- data.frame(Clust = ..., Rank = ..., Gene = ..., Expr = ..., X = ..., Y = ...)
data_titles <- data %>% group_by(Clust, Rank) %>% summarise(Gene = Gene[[1]])
ggplot() +
  geom_point(aes(X, Y, colour = Expr), data) +
  geom_text(aes(label = Gene), data_titles, x = Inf, y = Inf, hjust = 1, vjust = 1) +
  facet_grid(vars(Clust), vars(Rank))
paleolimbot commented 5 years ago

You could implement this fairly easily in an extension:

library(ggplot2)

facet_aes <- function(facets = NULL, ...) {
  wrap <- facet_wrap(facets = vars(dummy), ...)
  ggproto("FacetAes", wrap)
}

ggplot_add.FacetAes <- function(object, plot, object_name) {
  object$params$facets <- plot$mapping["panel"]
  plot$mapping$panel <- NULL
  NextMethod()
}

ggplot(mpg, aes(cty, hwy, panel = class)) +
  geom_point() +
  facet_aes()

Created on 2019-06-17 by the reprex package (v0.2.1)

hadley commented 5 years ago

I now see facets as a special case of x and y positioning, but I think this is too major a change to major to ggplot2 at this point in its life cycle.

lock[bot] commented 4 years ago

This old issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with reprex) and link to this issue. https://reprex.tidyverse.org/