Adjusting the relative space of a facets (without regard to coordinate space)

378 views
Skip to first unread message

jshore

unread,
Nov 17, 2010, 10:46:44 AM11/17/10
to ggplot2
Apologies, but I am cross-posting, having placed on stackoverflow
previously.

I have a primary graph and some secondary information that I want to
facet in another graph below it. Facetting works great except I do not
know how to control the relative space used by one facet versus
another. Am aware of space='free' but this is only useful if the
ranges correspond to the desired relative sizing.

So for instance, I may want a graph where the first facet occupies 80%
and the second 20%. Here is simple example:

data <- rbind(
data.frame(x=1:500, y=rnorm(500,sd=1), type='A'),
data.frame(x=1:500, y=rnorm(500,sd=5), type='B'))
ggplot() +
geom_line(aes(x=x, y=y, colour=type), data=data) +
facet_grid(type ~ ., scale='free_y')

The above creates 2 facets of equal vertical dimension. Adding in
space='free' in the facet_grid function changes the dimensions such
that the lower facet is roughly 5x larger than the upper (for obvious
reasons):

ggplot() +
geom_line(aes(x=x, y=y, colour=type), data=data) +
facet_grid(type ~ ., space='free', scale='free_y')


Supposing I want the upper to be 2x as large, with the same data set
and ordering of facets. How can I accomplish this?

Is the only way to do this with some trickery in rescaling the data
set and manually overriding axis labels (and if so, how)?

This is a problem I encounter a lot as want one of the facets to have
much more detail than others.

Thanks

--
Jonathan Shore

James McCreight

unread,
Nov 17, 2010, 11:03:38 AM11/17/10
to jshore, ggplot2
Hi Jonathan-

It seems like people want facets to do all sorts of things these days! 

according to Hadley's book, page 115: 
"Faceting generates small multiples each showing a different subset of the data."

Facets are not intended to be so customizable. Facets do not replace viewports. see today's discussion

inverting y-axis for only one of a faceted group
 

you're going to have to use viewports or an approach with facets as I suggested, where you plot the desired panels separately with facets and then combine with inkscape or illustrator.

cheers,

James




--
You received this message because you are subscribed to the ggplot2 mailing list.
Please provide a reproducible example: http://gist.github.com/270442

To post: email ggp...@googlegroups.com
To unsubscribe: email ggplot2+u...@googlegroups.com
More options: http://groups.google.com/group/ggplot2



--
-
******************************************************************************
James McCreight                               mccreigh -(at)- colorado -(dot)- edu 
NASA Postdoctoral Fellow
cell: (831) 261-5149
VoIP (to cell): (720) 897-7546

Ben Bolker

unread,
Nov 17, 2010, 11:15:01 AM11/17/10
to ggp...@googlegroups.com
And let me put in this reminder about grid.arrange(), in the gridExtra
package, which makes it simple to arrange different grid graphics within
a specified layout ...

On 10-11-17 11:03 AM, James McCreight wrote:
> Hi Jonathan-
>
> It seems like people want facets to do all sorts of things these days!
>
> according to Hadley's book, page 115:
> "Faceting generates small multiples each showing a different subset of
> the data."
>
> Facets are not intended to be so customizable. Facets do not replace
> viewports. see today's discussion
>
> inverting y-axis for only one of a faceted group

> <http://groups.google.com/group/ggplot2/t/1ce180106d16d946>

> <mailto:ggp...@googlegroups.com>
> To unsubscribe: email ggplot2+u...@googlegroups.com
> <mailto:ggplot2%2Bunsu...@googlegroups.com>

Jonathan Shore

unread,
Nov 17, 2010, 11:19:37 AM11/17/10
to James McCreight, ggplot2
I am familiar with viewports, however, by generating a second graph in a separate viewport introduces the following problems:

- the axes are likely not to line up
- the graphs themselves may have differing dimension due to axis labeling or legends

Viewports do not seem to be an adequate solution unless I am missing something.

Jonathan Shore

unread,
Nov 17, 2010, 11:30:45 AM11/17/10
to ggplot2
Thanks for the responses.  

I should clarify that I am looking to have aligned axes.   I don't see how this can be accomplished rendering in separate graphs.    

Given that facets already support relative dimensions via space='free', if I knew where to adjust the code or graph spec could manually override the space calculation.   Is this something that could be overridden in one of the visible data-structures?

Where would be the appropriate place to discuss / send a feature request?

Thanks

Jonathan  

On Nov 17, 2010, at 11:03 AM, James McCreight wrote:

Kohske Takahashi

unread,
Nov 17, 2010, 11:47:32 AM11/17/10
to Jonathan Shore, ggplot2
hi,

probably it is easy to hack. I attached an ad-hoc hack and patch below.
actually I agree with James and concept of ggplot2, because modifying spaces of facet is confusing for readers and makes difficult to compare between subsets.
but also I understand such function is useful in some case for pragmatic reason.
so I have no idea if these kinds of hack would be merged into the future version of ggplot2.


  FacetGrid$new <- function(., facets = . ~ ., margins = FALSE, scales = "fixed", space = "fixed", labeller = "label_value", as.table = TRUE, widths = NULL, heights = NULL) {

    scales <- match.arg(scales, c("fixed", "free_x", "free_y", "free"))

    free <- list(

      x = any(scales %in% c("free_x", "free")),

      y = any(scales %in% c("free_y", "free"))

    )

    space <- match.arg(space, c("fixed", "free"))

    

    if (is.formula(facets)) facets <- deparse(facets

    .$proto(

      facets = facets, margins = margins,

      free = free, space_is_free = (space == "free"),

      scales = NULL, labeller = list(labeller), as.table = as.table,

      space_widths = widths, space_heights = heights

    )

  }

  

    FacetGrid# Create grobs for each component of the panel guides

  add_guides <- function(., data, panels_grob, coord, theme) {


    aspect_ratio <- theme$aspect.ratio

    

    # If user hasn't set aspect ratio, and we have fixed scales, then

    # ask the coordinate system if it wants to specify one

    if (is.null(aspect_ratio) && !.$free$x && !.$free$y) {

      xscale <- .$scales$x[[1]]

      yscale <- .$scales$y[[1]]

      ranges <- coord$compute_ranges(list(x = xscale, y = yscale))

      aspect_ratio <- coord$compute_aspect(ranges)

    }

    

    if (is.null(aspect_ratio)) {

      aspect_ratio <- 1

      respect <- FALSE

    } else {

      respect <- TRUE

    }


    nr <- nrow(panels_grob)

    nc <- ncol(panels_grob)

    

    coord_details <- matrix(list(), nrow = nr, ncol = nc)

    for (i in seq_len(nr)) {

      for(j in seq_len(nc)) {

        scales <- list(

          x = .$scales$x[[j]]$clone(), 

          y = .$scales$y[[i]]$clone()

        )        

        coord_details[[i, j]] <- coord$compute_ranges(scales)

      }

    }

    

    # Horizontal axes

    axes_h <- list()

    for(i in seq_along(.$scales$x)) {

      axes_h[[i]] <- coord$guide_axis_h(coord_details[[1, i]], theme)

    }

    axes_h_height <- do.call("max2", llply(axes_h, grobHeight))

    axeshGrid <- grobGrid(

      "axis_h", axes_h, nrow = 1, ncol = nc,

      heights = axes_h_height, clip = "off"

    )

    

    

    # Vertical axes

    axes_v <- list()

    for(i in seq_along(.$scales$y)) {

      axes_v[[i]] <- coord$guide_axis_v(coord_details[[i, 1]], theme)

    }    

    axes_v_width <- do.call("max2", llply(axes_v, grobWidth))

    axesvGrid <- grobGrid(

      "axis_v", axes_v, nrow = nr, ncol = 1,

      widths = axes_v_width, as.table = .$as.table, clip = "off"

    )

    

    # Strips

    labels <- .$labels_default(.$shape, theme)

    

    strip_widths <- llply(labels$v, grobWidth)

    strip_widths <- do.call("unit.c", llply(1:ncol(strip_widths), 

      function(i) do.call("max2", strip_widths[, i])))

    stripvGrid <- grobGrid(

      "strip_v", t(labels$v), nrow = nrow(labels$v), ncol = ncol(labels$v),

      widths = strip_widths, as.table = .$as.table

    )


    strip_heights <- llply(labels$h, grobHeight)

    strip_heights <- do.call("unit.c", llply(1:nrow(strip_heights),

       function(i) do.call("max2", strip_heights[i, ])))

    striphGrid <- grobGrid(

      "strip_h", t(labels$h), nrow = nrow(labels$h), ncol = ncol(labels$h),

      heights = strip_heights

    )

      

    # Add background and foreground to panels

    panels <- matrix(list(), nrow=nr, ncol = nc)

    for(i in seq_len(nr)) {

      for(j in seq_len(nc)) {

        fg <- coord$guide_foreground(coord_details[[i, j]], theme)

        bg <- coord$guide_background(coord_details[[i, j]], theme)


        panels[[i,j]] <- grobTree(bg, panels_grob[[i, j]], fg)

      }

    }


    if(.$space_is_free) {

      size <- function(y) unit(diff(y$output_expand()), "null")

      panel_widths <- do.call("unit.c", llply(.$scales$x, size))

      panel_heights <- do.call("unit.c", llply(.$scales$y, size))

    } else {

      if (!is.null(.$space_widths)) {

        panel_widths <- do.call("unit.c", lapply(.$space_widths, function(x)unit(x, "null")))

      } else {

        panel_widths <- unit(1, "null")

      }

      if (!is.null(.$space_heights)) {

        panel_heights <- do.call("unit.c", lapply(.$space_heights, function(x)unit(x, "null")))

      } else {

        panel_heights <- unit(1 * aspect_ratio, "null")

      }

    }

    


    panelGrid <- grobGrid(

      "panel", t(panels), ncol = nc, nrow = nr,

      widths = panel_widths, heights = panel_heights, as.table = .$as.table,

      respect = respect

    )

       

    # Add gaps and compute widths and heights

    fill_tl <- spacer(nrow(labels$h), 1)

    fill_tr <- spacer(nrow(labels$h), ncol(labels$v))

    fill_bl <- spacer(1, 1)

    fill_br <- spacer(1, ncol(labels$v))

    

    all <- rbind(

      cbind(fill_tl,   striphGrid, fill_tr),

      cbind(axesvGrid, panelGridstripvGrid),

      cbind(fill_bl,   axeshGridfill_br

    )

    # theme$panel.margin, theme$panel.margin

    

    # from left to right

    hgap_widths <- do.call("unit.c", compact(list(

      unit(0, "cm"), # no gap after axis

      rep.unit2(theme$panel.margin, nc - 1), # gap after all panels except last

      unit(rep(0, ncol(stripvGrid) + 1), "cm") # no gap after strips 

    )))

    hgap <- grobGrid("hgap"

      ncol = ncol(all), nrow = nrow(all),

      widths = hgap_widths

    )

    

    # from top to bottom

    vgap_heights <- do.call("unit.c", compact(list(

      unit(rep(0, nrow(striphGrid) + 1), "cm"), # no gap after strips 

      rep.unit2(theme$panel.margin, nr - 1), # gap after all panels except last

      unit(0, "cm") # no gap after axis

    )))

    

    vgap <- grobGrid("vgap",

      nrow = nrow(all), ncol = ncol(all) * 2,

      heights = vgap_heights

    )

    

    rweave(cweave(all, hgap), vgap)

  }



qplot(mpg, wt, data=mtcars) + facet_grid(cyl ~ vs, widths=c(1,2), heights=c(1,1,3))




here is a patch

diff --git a/R/facet-grid-.r b/R/facet-grid-.r
index ae6fce1..a72f29c 100644
--- a/R/facet-grid-.r
+++ b/R/facet-grid-.r
@@ -1,5 +1,5 @@
 FacetGrid <- proto(Facet, {
-  new <- function(., facets = . ~ ., margins = FALSE, scales = "fixed", space = "fixed", labeller = "label_value", as.table = TRUE) {
+  new <- function(., facets = . ~ ., margins = FALSE, scales = "fixed", space = "fixed", labeller = "label_value", as.table = TRUE, widths = NULL, heights = NULL) {
     scales <- match.arg(scales, c("fixed", "free_x", "free_y", "free"))
     free <- list(
       x = any(scales %in% c("free_x", "free")),
@@ -11,7 +11,8 @@ FacetGrid <- proto(Facet, {
     .$proto(
       facets = facets, margins = margins,
       free = free, space_is_free = (space == "free"),
-      scales = NULL, labeller = list(labeller), as.table = as.table
+      scales = NULL, labeller = list(labeller), as.table = as.table,
+      space_widths = widths, space_heights = heights
     )
   }
   
@@ -134,9 +135,18 @@ FacetGrid <- proto(Facet, {
       panel_widths <- do.call("unit.c", llply(.$scales$x, size))
       panel_heights <- do.call("unit.c", llply(.$scales$y, size))
     } else {
-      panel_widths <- unit(1, "null")
-      panel_heights <- unit(1 * aspect_ratio, "null")
+      if (!is.null(.$space_widths)) {
+        panel_widths <- do.call("unit.c", lapply(.$space_widths, function(x)unit(x, "null")))
+      } else {
+        panel_widths <- unit(1, "null")
+      }
+      if (!is.null(.$space_heights)) {
+        panel_heights <- do.call("unit.c", lapply(.$space_heights, function(x)unit(x, "null")))
+      } else {
+        panel_heights <- unit(1 * aspect_ratio, "null")
+      }
     }
+    
 
     panelGrid <- grobGrid(
       "panel", t(panels), ncol = nc, nrow = nr,


--
Kohske Takahashi <takahash...@gmail.com>

Research Center for Advanced Science and Technology,
The University of  Tokyo, Japan.
http://www.fennel.rcast.u-tokyo.ac.jp/profilee_ktakahashi.html

Jonathan Shore

unread,
Nov 17, 2010, 11:59:35 AM11/17/10
to Kohske Takahashi, ggplot2
Brilliant!  Thanks

James McCreight

unread,
Nov 17, 2010, 12:02:32 PM11/17/10
to Kohske Takahashi, Jonathan Shore, ggplot2
Jonathan, I couldnt agree more with your points! I'd love to see the functionality you envision. Takahashi, thanks for pointing the way. 

If people choose to hack on facet and could put all their codes in a convenient place, that would be tremendous. I would consider this approach next time i need something new from facet, instead of the inkscape illustrator approach. I already have a list of things from the past and there are 2 already today.

baptiste auguie

unread,
Nov 17, 2010, 1:00:03 PM11/17/10
to James McCreight, Kohske Takahashi, Jonathan Shore, ggplot2
On Wed, Nov 17, 2010 at 6:02 PM, James McCreight <mccr...@colorado.edu> wrote:
Jonathan, I couldnt agree more with your points! I'd love to see the functionality you envision. Takahashi, thanks for pointing the way. 

If people choose to hack on facet and could put all their codes in a convenient place, that would be tremendous.

the ggExtra package (googlecode, R-forge) was created with this idea in mind. I'd welcome any contribution, any time.

Note that there have been several partial answers to the query of aligning plot axes using Grid viewports, some of them are on the list archive, and a basic version is in the ggExtra package -- align.plots().

baptiste

Jonathan Shore

unread,
Nov 17, 2010, 1:16:04 PM11/17/10
to baptiste auguie, ggplot2
On Nov 17, 2010, at 1:00 PM, baptiste auguie wrote:


On Wed, Nov 17, 2010 at 6:02 PM, James McCreight <mccr...@colorado.edu> wrote:
Jonathan, I couldnt agree more with your points! I'd love to see the functionality you envision. Takahashi, thanks for pointing the way. 

If people choose to hack on facet and could put all their codes in a convenient place, that would be tremendous.

the ggExtra package (googlecode, R-forge) was created with this idea in mind. I'd welcome any contribution, any time.

Note that there have been several partial answers to the query of aligning plot axes using Grid viewports, some of them are on the list archive, and a basic version is in the ggExtra package -- align.plots().

baptiste


Thanks.  I like the modification that Takahashi did to facets, which essentially exploits a feature already present (that of facets with different dimension), except control over relative dimension is provided.


 
I would consider this approach next time i need something new from facet, instead of the inkscape illustrator approach. I already have a list of things from the past and there are 2 already today.



On Wed, Nov 17, 2010 at 9:47 AM, Kohske Takahashi <takahash...@gmail.com> wrote:
hi,

probably it is easy to hack. I attached an ad-hoc hack and patch below.
actually I agree with James and concept of ggplot2, because modifying spaces of facet is confusing for readers and makes difficult to compare between subsets.
but also I understand such function is useful in some case for pragmatic reason.


I agree with the view in the case where one is looking at subsets of the same data that one would not want to confuse with different spacing.  However,  for distinct coordinate spaces that share a dimension, being able to specify the sizing of the facet is a valuable tool in focusing on detail.

Case in point would be a financial timeseries where want to have the largest facet for the series and a second facet for various indicators.   Both the series and its indicators share the time dimension, but have distinct y coordinate spaces.

Thanks for the modification.  Works great!

Jonathan

Jie Huang

unread,
Jan 17, 2018, 1:53:44 PM1/17/18
to ggplot2
great!
Reply all
Reply to author
Forward
0 new messages