From deepayan.sarkar at gmail.com  Wed Sep  6 05:28:57 2006
From: deepayan.sarkar at gmail.com (Deepayan Sarkar)
Date: Tue, 5 Sep 2006 20:28:57 -0700
Subject: [Rd] Two submitted packages
In-Reply-To: <20060905223031.BHI71699@po-d.temple.edu>
References: <20060905223031.BHI71699@po-d.temple.edu>
Message-ID: <eb555e660609052028t45899247lc443af40da9ebbda@mail.gmail.com>

On 9/5/06, Richard M. Heiberger <rmh at temple.edu> wrote:
> Deepayan wrote:
>
> > > xyplot(y ~ x | a,
> > >        data=tmp, ylim=c(1.5,4.5),
> > >        par.settings = list(clip = list(panel = "off")),
> > >        layout=c(2,2))
> >
> > I'm curious to know why this functionality is not enough.
>
>
> ## 1. Here is a more realistic example.
>
> tmp <- data.frame(x=1:5, y=1:5, a=factor(c(1,1,1,1,1), levels=1:4))
>
> ## This gives what I want.
> ## It looks like the upViewport/downViewport sequence is the control I
> ## should be using.  If this is a stable sequence, it answers my needs.
> xyplot(y ~ x,
>        data=tmp, ylim=c(1.5,4.5),
>        panel=function(...) {
>           panel.xyplot(...)
>             upViewport()
>             downViewport(trellis.vpname("panel",
>                                         row = 1, column = 1, clip.off = TRUE))
>           panel.axis(side="right", outside=TRUE)
>        },
>        layout=c(2,2),
>        main="right axis visible by control, points clipped by default")
>
> ## If I turned off clipping at the top, I would get the unwanted points
> ## plotted outside the figure region.
> xyplot(y ~ x,
>        data=tmp, ylim=c(1.5,4.5),
>        par.settings = list(clip = list(panel = "off")),
>        panel=function(...) {
>           panel.xyplot(...)
>           panel.axis(side="right", outside=TRUE)
>        },
>        layout=c(2,2),
>        main="right axis visible by control, points not clipped")

I see.

> ## This doesn't work, but it illustrates the syntax I would like.
> xyplot(y ~ x,
>        data=tmp, ylim=c(1.5,4.5),
>        panel=function(...) {
>           panel.xyplot(...)
>           panel.axis(side="right", outside=TRUE, clip="off")
>        },
>        layout=c(2,2),
>        main="right axis visible by control, points clipped by default")

This might be possible, but there is a potential for unanticipated
problems. On the other hand, it's easy to achieve the equivalent of
this with a couple of extra lines, e.g.

xyplot(y ~ x,
       data=tmp, ylim=c(1.5,4.5),
       panel=function(...) {
           cpl <- current.panel.limits()
           panel.xyplot(...)
           pushViewport(viewport(xscale = cpl$xlim,
                                 yscale = cpl$ylim,
                                 clip = "off"))
           ## put anything you want unclipped inside this:
           panel.axis(side="right", outside=TRUE)
           ## end of unclipped part
           upViewport()
       },
       layout=c(2,2))

This already works, and should be stable, but I'm not sure; Paul has
previously mentioned this feature that an unclipped viewport inside a
clipped viewport allows plotting outside the clipped one, but I don't
know if that's intentional. Even if this behaviour does change, the
opposite should be stable: turn off clipping globally and then insert
the part of the panel function you want clipped into a clipped
viewport, e.g.:

xyplot(y ~ x,
       data=tmp, ylim=c(1.5,4.5),
       par.settings = list(clip = list(panel = "off")),
       panel=function(...) {
           cpl <- current.panel.limits()
           pushViewport(viewport(xscale = cpl$xlim,
                                 yscale = cpl$ylim,
                                 clip = "on"))
           panel.xyplot(...)
           upViewport()
           panel.axis(side="right", outside=TRUE)
       },
       layout=c(2,2))

> ## 2
> > Unfortunately, there is currently no easy way to find out which column
> > and row the current panel occupies. I plan to make some changes before
> > R 2.4.0 is released that should expose that information, so please try
> > again in a few weeks.
>
> The way I have been using is to back up the calling sequence and
> use statements like
> {
>     ## This loop is needed because
>     ##    console usage has i=2,
>     ##    Rcmdr script window has i=5,
>     ##    Rcmdr justDoIt has i=20
>     for (i in seq(2, length=30)) {
>       sf2 <- sys.frame(i)
>       column.tmp <- try(get("column", pos=sf2), silent=TRUE)
>       if (class(column.tmp) != "try-error") break
>     }
>     if (class(column.tmp) == "try-error")
>       stop("panel.interaction2wt is too deeply nested in the system stack.")
>     row <- get("row", pos=sf2)
>     column <- get("column", pos=sf2)
>     cols.per.page <- get("cols.per.page", pos=sf2)
>     rows.per.page <- get("rows.per.page", pos=sf2)
>   }
>
> ## A cleaner way would be preferable.  Will the above stay valid?  The
> ## need for a loop to find out how far up the calling sequence to go
> ## caught me by surprise.  I don't understand why generating commands
> ## the way Rcmdr does it should have an effect on the depth of the
> ## calling sequence.

I hope to add 3 new features before R 2.4.0, two of which are relevant
for you: (1) more control over axis annotation and (2) an API to
access auxiliary information (like row/column/layout) from inside a
panel function. I should have a testable version in a week or so,
after which I'll be happy to consider suggestions for further
additions.

Deepayan




[Rd] Two submitted packages
Deepayan Sarkar