[R] Obtaining the true aspect ratio for a lattice plot

Ryan Hafen rhafen at stat.purdue.edu
Wed Mar 10 19:48:05 CET 2010


Thanks much - very nice.  I hadn't thought of that approach.

I dug around in the lattice source and found another way to do it so  
for what it's worth, I'll post it - maybe it will be useful to  
someone.  Basically, I made a function from code found in  
plot.trellis() that gets the grid layout for a lattice object  
(layout=c(_, _) and aspect need to be specified for this to work).   
Then functions getHeight() or getWidth() calculate correct height or  
width for a given width or height by calculating the dimensions of all  
the non-data-region parts of the plot. etc.


trellis.getlayout <- function(x,
    panel.height = lattice.getOption("layout.heights")$panel,
    panel.width = lattice.getOption("layout.widths")$panel
) {

     if (!is.null(x$plot.args))
     {
         supplied <- names(x$plot.args)
         if ("panel.height" %in% supplied && missing(panel.height))  
panel.height <- x$plot.args$panel.height
         if ("panel.width"  %in% supplied && missing(panel.width))   
panel.width  <- x$plot.args$panel.width
     }

     original.condlevels <-
         used.condlevels <-
             lapply(x$condlevels, function(x) seq_along(x))
     used.condlevels <-
         mapply("[", used.condlevels, x$index.cond,
                MoreArgs = list(drop = FALSE),
                SIMPLIFY = FALSE)
     used.condlevels <- used.condlevels[x$perm.cond]
     inverse.permutation <- order(x$perm.cond) # used later

    cond.max.levels <- sapply(used.condlevels, length)
     number.of.cond <- length(cond.max.levels)

     panel.layout <- lattice:::compute.layout(x$layout,  
cond.max.levels, skip = x$skip)

     if(panel.layout[1] == 0)
        stop("invalid layout")

     axis.text <- trellis.par.get("axis.text")

     if (!x$aspect.fill)
         panel.height[[1]] <- x$aspect.ratio * panel.width[[1]]

     legend <- lattice:::evaluate.legend(x$legend)

     xaxis.cex <-
         if (is.logical(x$x.scales$cex)) rep(axis.text$cex, length = 2)
         else x$x.scales$cex
     xaxis.rot <-
         if (is.logical(x$x.scales$rot)) c(0, 0)
         else x$x.scales$rot

     yaxis.cex <-
         if (is.logical(x$y.scales$cex)) rep(axis.text$cex, length = 2)
         else x$y.scales$cex
     yaxis.rot <-
         if (!is.logical(x$y.scales$rot)) x$y.scales$rot
         else if (x$y.scales$relation != "same" && is.logical(x 
$y.scales$labels)) c(90, 90)
         else c(0, 0)

     cols.per.page <- panel.layout[1]
     rows.per.page <- panel.layout[2]

     x.alternating <- rep(x$x.scales$alternating, length =  
cols.per.page)
     y.alternating <- rep(x$y.scales$alternating, length =  
rows.per.page)
     x.relation.same <- x$x.scales$relation == "same"
     y.relation.same <- x$y.scales$relation == "same"

     main <-
         lattice:::grobFromLabelList(lattice:::getLabelList(x$main,
                                         
trellis.par.get("par.main.text")),
                           name = trellis.grobname("main"))
     sub <-
         lattice:::grobFromLabelList(lattice:::getLabelList(x$sub,
                                         
trellis.par.get("par.sub.text")),
                           name = trellis.grobname("sub"))
     xlab <-
         lattice:::grobFromLabelList(lattice:::getLabelList(x$xlab,
                                         
trellis.par.get("par.xlab.text"),
                                        x$xlab.default),
                           name = trellis.grobname("xlab"))
     ylab <-
         lattice:::grobFromLabelList(lattice:::getLabelList(x$ylab,
                                         
trellis.par.get("par.ylab.text"),
                                        x$ylab.default),
                           name = trellis.grobname("ylab"), orient = 90)

     par.strip.text <- trellis.par.get("add.text")
     par.strip.text$lines <- 1
     if (!is.null(x$par.strip.text))
         par.strip.text[names(x$par.strip.text)] <- x$par.strip.text

     trellis.par.set(p$par.settings) # set padding, etc.
     layoutCalculations <-
         lattice:::calculateGridLayout(x,
                             rows.per.page, cols.per.page,
                             number.of.cond,
                             panel.height, panel.width,
                             main, sub,
                             xlab, ylab,
                             x.alternating, y.alternating,
                             x.relation.same, y.relation.same,
                             xaxis.rot, yaxis.rot,
                             xaxis.cex, yaxis.cex,
                             par.strip.text,
                             legend)

     page.layout <- layoutCalculations$page.layout

     list(lyt=page.layout, ncols=cols.per.page, nrows=rows.per.page)
}

getWidth <- function(x, height) {
    require(grid)
    lyt <- trellis.getlayout(x)
    page.layout <- lyt$lyt
    nrows <- lyt$nrows
    ncols <- lyt$ncols

    H <- height

    h1 <- sum(unlist(lapply(page.layout$heights, function(x)  
as.numeric(convertY(x, "inches")))))
    w1 <- sum(unlist(lapply(page.layout$widths, function(x)  
as.numeric(convertX(x, "inches")))))

    w2 <- (H - h1) / (x$aspect.ratio * nrows)
    W <- w1 + w2*ncols
    W
}

getHeight <- function(x, width) {
    require(grid)
    lyt <- trellis.getlayout(x)
    page.layout <- lyt$lyt
    nrows <- lyt$nrows
    ncols <- lyt$ncols

    W <- width

    h1 <- sum(unlist(lapply(page.layout$heights, function(x)  
as.numeric(convertY(x, "inches")))))
    w1 <- sum(unlist(lapply(page.layout$widths, function(x)  
as.numeric(convertX(x, "inches")))))

    h2 <- (W - w1) * x$aspect.ratio / ncols
    H <- h1 + h2*nrows
    H
}

# example
library(lattice)

# get rid of extra padding around plot
mypars <- list(
    layout.heights = list(top.padding = 0, main.key.padding = 0,  
key.axis.padding = 0, axis.xlab.padding = 0, xlab.key.padding = 0,  
key.sub.padding = 0, bottom.padding = 0.5),
    layout.widths = list(left.padding = 0, key.ylab.padding = 0,  
ylab.axis.padding = 0, axis.key.padding = 0, right.padding = 0),
    axis.components=list(top=list(pad2 = 0), right=list(pad1 = 0, pad2  
= 0))
)


p <- xyplot(rnorm(100) ~ rnorm(100) | sample(c(0, 1), 100,  
replace=TRUE), aspect=1.5, layout=c(2, 1),
xlab="this is some extra text\n
to show that the\n
overall aspect ratio\n
of a plot is not always\n
easily determined by\n
the aspect ratio specified for the data",
par.settings=mypars
)

pdf("a.pdf", height=6, width=getWidth(p, 6))
plot(p)
dev.off()



On Mar 9, 2010, at 10:51 PM, Felix Andrews wrote:

> Basically you want to achieve an aspect ratio of 0.5 (say) when
> specifying aspect = "fill". You can calculate the aspect ratio after a
> lattice plot has been displayed:
>
> currAspect <- function() {
>    trellis.focus("panel", 1, 1, highlight = FALSE)
>    sz <- current.panel.limits("mm")
>    trellis.unfocus()
>    diff(sz$y) / diff(sz$x)
> }
>
> foo <-
> xyplot(rnorm(100) ~ rnorm(100), aspect="fill", ## NB "fill"
> xlab="this is some extra text\n
> to show that the\n
> overall aspect ratio\n
> of a plot is not always\n
> easily determined by\n
> the aspect ratio specified for the data")
>
> dev.new(width = 10, height = 5)
> print(foo)
> currAspect()
> #[1] 0.19003
>
> One problem is that fonts in pdf() seem to be very different to screen
> devices. You could probably work out which components of the plot have
> a fixed size, and therefore work out the requried aspect ratio... but
> I kinda like brute force:
>
> aspectObjective <- function(height, width, target, ...) {
>    tmp <- tempfile()
>    pdf(tmp, width = width, height = height, ...)
>    print(trellis.last.object())
>    asp <- currAspect()
>    dev.off()
>    file.remove(tmp)
>    abs(asp - target)
> }
>
> print(foo)
> height <- optimize(aspectObjective, c(1, 20), width = 10,
>                   target = 0.5, tol = 0.01)$min
>
> height
> #[1] 7.6928
>
> pdf("test.pdf", width = 10, height = height)
> print(foo)
> currAspect()
> #[1] 0.50013
> dev.off()
>
>
>
> On 10 March 2010 07:16, Ryan Hafen <rhafen at stat.purdue.edu> wrote:
>> I almost always supply my own aspect ratio when plotting using  
>> lattice.
>>  When I plot these to pdf, I would like to specify pdf dimensions  
>> that will
>> result in minimal margins around the plot.  In my application,  
>> resorting to
>> a pdf cropper after plotting is not an option - I must do it in R.   
>> The
>> problem is that I cannot determine the correct aspect ratio for the  
>> overall
>> plot (accounting for strip labels, keys, titles, labels, etc.) that  
>> would
>> help me determine the correct pdf dimensions.  I can roughly  
>> estimate what
>> the overall aspect ratio will be using the layout, etc., but this  
>> is not
>> good enough.  Is there a way to calculate the true overall aspect  
>> ratio for
>> the entire plot?
>>
>> Example: If I make a simple plot and specify aspect=0.5, you would  
>> think
>> that dimensions width=10, height=5 would be a good approximation,  
>> but for
>> example:
>>
>> library(lattice)
>> pdf("plot.pdf", width=10, height=5)
>> xyplot(rnorm(100) ~ rnorm(100), aspect=0.5,
>> xlab="this is some extra text\n
>> to show that the\n
>> overall aspect ratio\n
>> of a plot is not always\n
>> easily determined by\n
>> the aspect ratio specified for the data")
>> dev.off()
>>
>> doesn't work.  It looks like the true aspect ratio of the resulting
>> non-margin area in the pdf is about 0.86.  I'd like to know this in  
>> R before
>> making the pdf so I can specify dimensions accordingly.  Any ideas?
>>
>> ______________________________________________
>> R-help at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-help
>> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
>> and provide commented, minimal, self-contained, reproducible code.
>>
>
>
>
> -- 
> Felix Andrews / 安福立
> Postdoctoral Fellow
> Integrated Catchment Assessment and Management (iCAM) Centre
> Fenner School of Environment and Society [Bldg 48a]
> The Australian National University
> Canberra ACT 0200 Australia
> M: +61 410 400 963
> T: + 61 2 6125 4670
> E: felix.andrews at anu.edu.au
> CRICOS Provider No. 00120C
> -- 
> http://www.neurofractal.org/felix/



More information about the R-help mailing list