Skip to content

Specify xlim and ylim for each facet separately #187

kohske opened this Issue May 23, 2011 · 17 comments
kohske commented May 23, 2011

Maybe it is fine if xlim and ylim can be specified for each facet separately, at least, for facet_wrap.
Now, panel contains the information about ranges of each facet, so e.g.,

# lims like this:
  vs am xmin xmax ymin ymax
1  0  0    8    8 10.4 19.2
2  0  1    4    8 15.0 26.0
3  1  0    4    6 17.8 24.4
4  1  1    4    4 21.4 33.9

and specify by, e.g.,

+ facet_wrap(~vs+am, limits = lims)
hadley commented May 23, 2011

Yes, I like this idea, and I like that basic syntax. But how do limit and free scales interact? And what happens if you specify facet_wrap(~ vs, limits = lims).

Another related idea that I've been thinking about is giving layers the ability not to affect scale limits - this is useful when you are plotting spatial data and want to add a map on top. You don't want the map to affect the limits, because it may be much bigger than you data, and using coord to zoom in is a hassle. If that idea was implemented, modifying scales on a facet-by-facet basis would amount to using train_position = F for all layers, apart from a geom_blank which specified the dummy data to expand the scales as needed - which expand_limits already does.

Implementing scale limits in that way would also mean the behaviour of free scales etc could remain as is.

kohske commented May 23, 2011

Here is a testbed of manual limits for coord with facet_wrap.


hadley commented May 24, 2011

Hmmmm, that code just looks too complicated to me. It's a lot of extra complexity for not a lot of extra gain. I think giving layers the ability to control whether or not they affect position scaling is simpler to implement, more elegant (in terms of playing nicely with existing code for free limits) and allows a number of other interesting techniques that were previously not possibly.


I would like to add another, related request. Different panels with different limits may also require fine-tuning of the breaks on a per-panel basis (I do need this, often). Currently the only way I could achieve this was to mask grid.pretty() and play some ugly tricks to have it return different breaks for different data ranges.
It sure would be nice to be able to specify manual breaks for each facet, in those few special occasions. I don't expect there will ever be a properly grammatical syntax for this, though. Failing this, perhaps it should simply be part of opts()? I could imagine a new opts() for a ggplot being similar to the way lattice handles these cosmetic details,

  • specify the panel ordering
  • specify the panel widths, and interpanel spacings (allowing visual groupings, e.g. 2 by 2)
  • specify a list of panel limits, with optional recycling
  • specify the panel x and y breaks (- perhaps even more options, such as per-panel background colour, ...)
hadley commented May 25, 2011

That's actually possible in the devel version - you can take the output of ggplot_build and modify the per-panel labels there. I'm not sure if I want to commit to a formal API yet.

ggplot2 will never handle these details like lattice does, because I really dislike how that works - you should be describing things in terms of the underlying data, not by some arbitrary panel number.


It should be possible to specify all these details in terms of the facetting variables as opposed to the panel number; admittedly the syntax becomes more cumbersome. There may be a way to define a new data structure between data.frame and list to handle these options. Below is an illustration of what I have in mind, storing the values in a long format matrix.

my.panel.details <- function(){

## two facetting variables
row.var <- rep(letters[1:2], 2)
col.var <- rep(LETTERS[1:2], each=2)

## corresponding specifications
cbind(row.var=row.var, col.var=col.var,
limits = c(list(c(0, 10)), # per panel limits
list(c(0, Inf)), # Inf means 'use the defaults from the data'
list(c(-Inf, Inf)),
list(c(-Inf, Inf))),
breaks = c(list(seq(0,10)),
vspacing = c(5, 5), # vertical spacing between the panels, repeated by technical necessity
hspacing = c(0, 0), # horizontal spacing between the panels
width = c(1, 2), # using null units?
height = c(1, 1), # using null units?
background.color = c("grey95"))



opts(panel.details = my.panel.details())

jonsedar commented Mar 1, 2013

It's two years on, but I'd really like to be able to specify ylims for each facet as described above. Any chance of getting this included?

joey711 commented Apr 25, 2013

I second that last comment by @jonsedar. Any news on this?

gitspade commented May 7, 2013


marrcl commented May 21, 2013





A possible way to implement this feature is to add a attribute to layers on how they affect scale limits.
This attribute could have three values

  • Normal: The layer have the same behavior as in the current implementation of ggplot2
  • Ignore: The layer is ignored when computing the scale limits, as already proposed by @hadley above
  • Force: The layer force the scale limits based on his own data and the data on the other layers are not considered to compute the scales limits

Then we can easily set the scale limits by creating a layer, say geom_rect with the desired size. We can then control the visibility of the layer either by the aesthetic alpha, or a new attribute on layer controlling visibility. if we want to affect only on direction, say x, we can set the dimension of the geom to Inf and -Inf in this direction.

This is not a direct implementation of the feature requested, but it will do the job with a minimum of hacks. It will also enable the possibility of ignoring some layer. This is useful for maps as mentioned by @hadley but also, for example, to plot the prediction of a model together with data while only keeping the focus on the data.

hadley commented Feb 24, 2014

This sounds like a great feature, but unfortunately we don't currently have the development bandwidth to support it. If you'd like to submit a pull request that implements this feature, please follow the instructions in the development vignette.

@hadley hadley closed this Feb 24, 2014
pengchy commented May 8, 2015

I think the simple way is to modify the original data, remove the unwanted values

bshor commented May 21, 2015


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.