589 lines
22 KiB
Plaintext
589 lines
22 KiB
Plaintext
|
% File src/library/grid/vignettes/plotexample.Rnw
|
||
|
% Part of the R package, https://www.R-project.org
|
||
|
% Copyright 2001-13 Paul Murrell and the R Core Team
|
||
|
% Distributed under GPL 2 or later
|
||
|
|
||
|
\documentclass[a4paper]{article}
|
||
|
%\VignetteIndexEntry{Writing grid Code}
|
||
|
%\VignettePackage{grid}
|
||
|
\newcommand{\code}[1]{\texttt{#1}}
|
||
|
\newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}}
|
||
|
\newcommand{\grid}{\pkg{grid}}
|
||
|
\newcommand{\grob}{\code{grob}}
|
||
|
\newcommand{\gTree}{\code{gTree}}
|
||
|
\newcommand{\R}{{\sffamily R}}
|
||
|
\newcommand{\I}[1]{#1}
|
||
|
\setlength{\parindent}{0in}
|
||
|
\setlength{\parskip}{.1in}
|
||
|
\setlength{\textwidth}{140mm}
|
||
|
\setlength{\oddsidemargin}{10mm}
|
||
|
|
||
|
\newcommand{\aside}[1]{\begin{list}{}
|
||
|
{\setlength{\leftmargin}{1in}
|
||
|
\setlength{\rightmargin}{1in}
|
||
|
\setlength{\itemindent}{0in}}
|
||
|
\item \textsc{Aside:} \emph{#1}
|
||
|
\end{list}}
|
||
|
|
||
|
\title{Writing \grid{} Code}
|
||
|
\author{Paul Murrell}
|
||
|
|
||
|
\begin{document}
|
||
|
\maketitle
|
||
|
|
||
|
<<echo=FALSE, results=hide>>=
|
||
|
library(grDevices)
|
||
|
library(stats) # for runif()
|
||
|
library(grid)
|
||
|
ps.options(pointsize=12)
|
||
|
options(width=60)
|
||
|
@
|
||
|
|
||
|
The \grid{} system contains a degree of complexity in order
|
||
|
to allow things like editing graphical objects, ``packing'' graphical
|
||
|
objects, and so on. This means that many of the
|
||
|
predefined Grid graphics functions are
|
||
|
relatively complicated\footnote{Although there are exceptions; some
|
||
|
functions, such as \code{grid.show.viewport}, are purely for producing
|
||
|
illustrative diagrams and remain simple and procedural.}.
|
||
|
|
||
|
One design aim of \grid{} is to allow users to create simple graphics
|
||
|
simply and not to force them to use complicated concepts or write
|
||
|
complicated code unless they actually need to. Along similar lines,
|
||
|
it is intended that people should be able to prototype even complex
|
||
|
graphics very simply and then refine the implementation into a more
|
||
|
sophisticated form if necessary.
|
||
|
|
||
|
With the predefined graphics functions being fully-developed and
|
||
|
complicated implementations, there is a lack of examples of simple,
|
||
|
prototype code. Furthermore, given that the aim is to allow a range
|
||
|
of ways to produce the same graphical output, there is a need for
|
||
|
examples which demonstrate the various stages, from simple to complex,
|
||
|
that a piece of \grid{} code can go through.
|
||
|
|
||
|
This document describes the construction of a scatterplot object, like
|
||
|
that shown below, going from the simplest, prototype implementation to
|
||
|
the most complex and sophisticated. It demonstrates that if you only
|
||
|
want simple graphics output then you can do it pretty simply and
|
||
|
quickly. It also demonstrates how to write functions that allow your
|
||
|
graphics to be used by other people. Finally, it demonstrates how to
|
||
|
make your graphics fully interactive (or at least as interactive as
|
||
|
Grid will let you make it).
|
||
|
|
||
|
@
|
||
|
This document should be read {\em after} the \grid{} Users'
|
||
|
Guide. Here we are assuming that the reader has an understanding
|
||
|
of viewports, layouts, and units. For the later sections of the
|
||
|
document, it will also be helpful to have an understanding of
|
||
|
\R{}'s \code{S3} object system.
|
||
|
|
||
|
\section*{Procedural \grid{}}
|
||
|
|
||
|
The simplest way to produce graphical output in Grid is just like
|
||
|
producing standard R graphical output. You simply issue a series of
|
||
|
graphics commands and each command adds more ink to the plot. The
|
||
|
purpose of the commands is simply to produce graphics output; in
|
||
|
particular, we are not concerned with any values returned by the
|
||
|
plotting functions. I will call this \emph{procedural graphics}.
|
||
|
|
||
|
In order to draw a simple scatterplot, we can issue a series
|
||
|
of commands which draw the various components of the plot.
|
||
|
|
||
|
Here are some random data to plot.
|
||
|
|
||
|
<<>>=
|
||
|
x <- runif(10)
|
||
|
y <- runif(10)
|
||
|
@
|
||
|
\noindent
|
||
|
The first step in creating the plot involves defining a ``data'' region.
|
||
|
This is a region which has sensible scales on the axes for plotting the
|
||
|
data and margins around the outside
|
||
|
for the axes to fit in, with a space for a title at the top.
|
||
|
|
||
|
<<datavp>>=
|
||
|
data.vp <- viewport(x = unit(5, "lines"),
|
||
|
y = unit(4, "lines"),
|
||
|
width = unit(1, "npc") - unit(7, "lines"),
|
||
|
height = unit(1, "npc") - unit(7, "lines"),
|
||
|
just = c("left", "bottom"),
|
||
|
xscale = range(x) + c(-0.05, 0.05)*diff(range(x)),
|
||
|
yscale = range(y) + c(-0.05, 0.05)*diff(range(y)))
|
||
|
@
|
||
|
\noindent
|
||
|
Now we create the data region and draw the components of the plot
|
||
|
relative to it: points, axes, labels, and a title.
|
||
|
|
||
|
<<procplot>>=
|
||
|
pushViewport(data.vp)
|
||
|
grid.points(x, y)
|
||
|
grid.rect()
|
||
|
grid.xaxis()
|
||
|
grid.yaxis()
|
||
|
grid.text("x axis", y = unit(-3, "lines"),
|
||
|
gp = gpar(fontsize = 14))
|
||
|
grid.text("y axis", x = unit(-4, "lines"),
|
||
|
gp = gpar(fontsize = 14), rot = 90)
|
||
|
grid.text("A Simple Plot",
|
||
|
y = unit(1, "npc") + unit(1.5, "lines"),
|
||
|
gp = gpar(fontsize = 16))
|
||
|
popViewport()
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<procplot>>
|
||
|
@
|
||
|
\section*{Facilitating Annotation}
|
||
|
|
||
|
Issuing a series of commands to produce a plot, like in the previous
|
||
|
section, allows the user to have a great deal of flexibility.
|
||
|
It is always possible to recreate viewports in order to add
|
||
|
further annotations. For example, the following code
|
||
|
recreates the data region in order to place the date
|
||
|
at the bottom right corner.
|
||
|
|
||
|
<<ann1>>=
|
||
|
pushViewport(data.vp)
|
||
|
grid.text(date(), x = unit(1, "npc"), y = 0,
|
||
|
just = c("right", "bottom"), gp = gpar(col="grey"))
|
||
|
popViewport()
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<procplot>>
|
||
|
<<ann1>>
|
||
|
@
|
||
|
|
||
|
When more complex arrangements of viewports are involved, there may be
|
||
|
a bewildering array of viewports created, which may make it difficult
|
||
|
for other users to revisit a particular region of a plot. A
|
||
|
\code{lattice} plot is a good example. In such cases, it will be more
|
||
|
cooperative to use \code{upViewport()} rather than
|
||
|
\code{popViewport()} and leave the viewports that were created during
|
||
|
the drawing of the plot. Other users can then use \code{vpPath}s to
|
||
|
navigate to the desired region. For example, here is a slight
|
||
|
modification of the original series of commands, where the original
|
||
|
data viewport is given a name and \code{upViewport()} is used at the
|
||
|
end.
|
||
|
|
||
|
<<results=hide>>=
|
||
|
data.vp <- viewport(name = "dataregion",
|
||
|
x = unit(5, "lines"),
|
||
|
y = unit(4, "lines"),
|
||
|
width = unit(1, "npc") - unit(7, "lines"),
|
||
|
height = unit(1, "npc") - unit(7, "lines"),
|
||
|
just = c("left", "bottom"),
|
||
|
xscale = range(x) + c(-0.05, 0.05)*diff(range(x)),
|
||
|
yscale = range(y) + c(-0.05, 0.05)*diff(range(y)))
|
||
|
pushViewport(data.vp)
|
||
|
grid.points(x, y)
|
||
|
grid.rect()
|
||
|
grid.xaxis()
|
||
|
grid.yaxis()
|
||
|
grid.text("x axis", y = unit(-3, "lines"),
|
||
|
gp = gpar(fontsize = 14))
|
||
|
grid.text("y axis", x = unit(-4, "lines"),
|
||
|
gp = gpar(fontsize = 14), rot = 90)
|
||
|
grid.text("A Simple Plot",
|
||
|
y = unit(1, "npc") + unit(1.5, "lines"),
|
||
|
gp = gpar(fontsize = 16))
|
||
|
upViewport()
|
||
|
@
|
||
|
|
||
|
The date is now added using \code{downViewport()} to get to the data
|
||
|
region.
|
||
|
|
||
|
<<results=hide>>=
|
||
|
downViewport("dataregion")
|
||
|
grid.text(date(), x = unit(1, "npc"), y = 0,
|
||
|
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
||
|
upViewport()
|
||
|
@
|
||
|
\section*{Writing a \grid{} Function}
|
||
|
|
||
|
Here is the scatterplot code wrapped up as a simple function.
|
||
|
|
||
|
<<funcplot>>=
|
||
|
splot <- function(x = runif(10), y = runif(10), title = "A Simple Plot") {
|
||
|
data.vp <- viewport(name = "dataregion",
|
||
|
x = unit(5, "lines"),
|
||
|
y = unit(4, "lines"),
|
||
|
width = unit(1, "npc") - unit(7, "lines"),
|
||
|
height = unit(1, "npc") - unit(7, "lines"),
|
||
|
just = c("left", "bottom"),
|
||
|
xscale = range(x) + c(-.05, .05)*diff(range(x)),
|
||
|
yscale = range(y) + c(-.05, .05)*diff(range(y)))
|
||
|
pushViewport(data.vp)
|
||
|
grid.points(x, y)
|
||
|
grid.rect()
|
||
|
grid.xaxis()
|
||
|
grid.yaxis()
|
||
|
grid.text("y axis", x = unit(-4, "lines"),
|
||
|
gp = gpar(fontsize = 14), rot = 90)
|
||
|
grid.text(title, y = unit(1, "npc") + unit(1.5, "lines"),
|
||
|
gp = gpar(fontsize = 16))
|
||
|
upViewport()
|
||
|
}
|
||
|
@
|
||
|
There are several advantages to creating a
|
||
|
function:
|
||
|
\begin{enumerate}
|
||
|
\item We get the standard advantages of a function:
|
||
|
we can reuse and maintain the plot code more easily.
|
||
|
\item We can slightly generalise the plot. In this case, we can use it for
|
||
|
different data and have a different title. We could
|
||
|
add more arguments to allow different margins, control over
|
||
|
the axis scales, and so on.
|
||
|
\item The plot can be embedded in other graphics output.
|
||
|
\end{enumerate}
|
||
|
Here is an example which uses the \code{splot()} function to
|
||
|
create a slightly modified scatterplot, embedded within
|
||
|
other \grid{} output.
|
||
|
|
||
|
<<embed, fig=TRUE, results=hide>>=
|
||
|
grid.rect(gp = gpar(fill = "grey"))
|
||
|
message <-
|
||
|
paste("I could draw all sorts",
|
||
|
"of stuff over here",
|
||
|
"then create a viewport",
|
||
|
"over there and stick",
|
||
|
"a scatterplot in it.", sep = "\n")
|
||
|
grid.text(message, x = 0.25)
|
||
|
grid.lines(x = unit.c(unit(0.25, "npc") + 0.5*stringWidth(message) +
|
||
|
unit(2, "mm"),
|
||
|
unit(0.5, "npc") - unit(2, "mm")),
|
||
|
y = 0.5,
|
||
|
arrow = arrow(angle = 15, type = "closed"),
|
||
|
gp = gpar(lwd = 3, fill = "black"))
|
||
|
pushViewport(viewport(x = 0.5, height = 0.5, width = 0.45, just = "left",
|
||
|
gp = gpar(cex = 0.5)))
|
||
|
grid.rect(gp = gpar(fill = "white"))
|
||
|
splot(1:10, 1:10, title = "An Embedded Plot")
|
||
|
upViewport()
|
||
|
@
|
||
|
|
||
|
It is still straightforward to annotate the scatterplot as long as
|
||
|
we have enough information about the viewports. In this case,
|
||
|
a non-strict \code{downViewport()} will still work (though note
|
||
|
that \code{upViewport({\bf 0})} is required to get right back to the
|
||
|
top level).
|
||
|
|
||
|
<<ann2, echo = FALSE, eval=FALSE>>=
|
||
|
downViewport("dataregion")
|
||
|
grid.text(date(), x = unit(1, "npc"), y = 0,
|
||
|
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
||
|
upViewport(0)
|
||
|
<<echo=FALSE, results=hide>>=
|
||
|
<<embed>>
|
||
|
<<ann2>>
|
||
|
@
|
||
|
\section*{Creating \grid{} Graphical Objects}
|
||
|
|
||
|
A \grid{} function like the one in the previous section provides
|
||
|
output which is very flexible and can be annotated in arbitrary ways
|
||
|
and can be embedded within other output. This is likely
|
||
|
to satisfy most uses.
|
||
|
|
||
|
However, there are some things that cannot be done (or at least would
|
||
|
be extremely hard to do) with such a function. The output produced by
|
||
|
the function cannot be addressed as a coherent whole. It is not
|
||
|
possible, for example, to to change the \code{x} and \code{y} data
|
||
|
used in the plot and have the points and axes update automatically.
|
||
|
There is no scatterplot object to save; the individual components
|
||
|
exist, but they are not bound together as a whole. If/when these
|
||
|
sorts of issues become important, it becomes necessary to create a
|
||
|
\grid{} graphical object (a \grob{}) to represent the plot.
|
||
|
|
||
|
The first step is to write a function which will create a \grob{}
|
||
|
-- a \emph{constructor} function. In most cases, this will involve
|
||
|
creating a special sort of \grob{} called a \gTree{}; this is just
|
||
|
a \grob{} that can have other \grob{}s as children. Here's an example
|
||
|
for creating an \code{splot} \grob{}. I have put bits of the
|
||
|
construction into separate functions, for reasons which will become
|
||
|
apparent later.
|
||
|
|
||
|
<<>>=
|
||
|
splot.data.vp <- function(x, y) {
|
||
|
viewport(name = "dataregion",
|
||
|
x = unit(5, "lines"),
|
||
|
y = unit(4, "lines"),
|
||
|
width = unit(1, "npc") - unit(7, "lines"),
|
||
|
height = unit(1, "npc") - unit(7, "lines"),
|
||
|
just = c("left", "bottom"),
|
||
|
xscale = range(x) + c(-.05, .05)*diff(range(x)),
|
||
|
yscale = range(y) + c(-.05, .05)*diff(range(y)))
|
||
|
}
|
||
|
|
||
|
splot.title <- function(title) {
|
||
|
textGrob(title, name = "title",
|
||
|
y = unit(1, "npc") + unit(1.5, "lines"),
|
||
|
gp = gpar(fontsize = 16), vp = "dataregion")
|
||
|
}
|
||
|
|
||
|
splot <- function(x, y, title, name=NULL, draw=TRUE, gp=gpar(), vp=NULL) {
|
||
|
spg <- gTree(x = x, y = y, title = title, name = name,
|
||
|
childrenvp = splot.data.vp(x, y),
|
||
|
children = gList(rectGrob(name = "border",
|
||
|
vp = "dataregion"),
|
||
|
xaxisGrob(name = "xaxis", vp = "dataregion"),
|
||
|
yaxisGrob(name = "yaxis", vp = "dataregion"),
|
||
|
pointsGrob(x, y, name = "points", vp = "dataregion"),
|
||
|
textGrob("x axis", y = unit(-3, "lines"), name = "xlab",
|
||
|
gp = gpar(fontsize = 14), vp = "dataregion"),
|
||
|
textGrob("y axis", x = unit(-4, "lines"), name = "ylab",
|
||
|
gp = gpar(fontsize = 14), rot = 90,
|
||
|
vp = "dataregion"),
|
||
|
splot.title(title)),
|
||
|
gp = gp, vp = vp,
|
||
|
cl = "splot")
|
||
|
if (draw) grid.draw(spg)
|
||
|
spg
|
||
|
}
|
||
|
@
|
||
|
|
||
|
There are four important additions to the argument list compared
|
||
|
to the original \code{splot()} function:
|
||
|
\begin{enumerate}
|
||
|
\item The \code{name} argument allows a string identifier to be
|
||
|
associated with the scatterplot object we create. This is important
|
||
|
for being able to specify the scatterplot when we try to edit it
|
||
|
after drawing it and/or when it is part of a larger \grob{} (see
|
||
|
later examples).
|
||
|
|
||
|
\item The \code{draw} argument makes it possible to use the function
|
||
|
in a procedural manner as before:
|
||
|
|
||
|
<<splotgrob, eval=FALSE, echo=FALSE>>=
|
||
|
sg <- splot(1:10, 1:10, "Same as Before", name = "splot", draw = FALSE)
|
||
|
<<>>=
|
||
|
splot(1:10, 1:10, "Same as Before", name = "splot")
|
||
|
downViewport("dataregion")
|
||
|
grid.text(date(), x = unit(1, "npc"), y = 0,
|
||
|
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
||
|
upViewport(0)
|
||
|
@
|
||
|
\item The \code{gp} argument allows the user to supply \code{gpar()}
|
||
|
settings for the scatterplot as a whole.
|
||
|
|
||
|
\item The \code{vp} argument allows the user to supply a viewport for
|
||
|
the \code{splot} \grob{} to be drawn in. This is especially useful
|
||
|
for specifying a \code{vpPath} when the \code{splot} is used as a
|
||
|
component of another \grob{} (see scatterplot matrix example below).
|
||
|
\end{enumerate}
|
||
|
|
||
|
The important parts of the \gTree{} definition are:
|
||
|
\begin{enumerate}
|
||
|
\item The \code{children} argument provides a list of \grob{}s which
|
||
|
are part of the scatterplot. When the scatterplot is drawn, all
|
||
|
children will be drawn. Notice that instead of the procedural
|
||
|
\code{grid.*()} functions we use \code{*Grob()} functions which just
|
||
|
produce \grob{}s and do not perform any drawing. Also notice that I
|
||
|
have given each of the children a name; this will make it possible
|
||
|
to access the components of the scatterplot (see later examples).
|
||
|
\item The \code{childrenvp} argument provides a viewport (or
|
||
|
\code{vpStack}, \code{vpList}, or \code{vpTree}) which will be
|
||
|
pushed before the children are drawn. The difference between this
|
||
|
argument and the \code{vp} argument common to all \grob{}s is that
|
||
|
the \code{vp} is pushed before drawing the children and then popped
|
||
|
after, whereas the \code{childrenvp} gets pushed \emph{and} then a
|
||
|
call to \code{upViewport()} is made before the children are drawn.
|
||
|
This allows the children to simply specify the viewport they should
|
||
|
be drawn in by way of a \code{vpPath} in their \code{vp} argument.
|
||
|
In this way, viewports remain available for further annotation such
|
||
|
as we have already seen in procedural code.
|
||
|
\item The \code{gp} and \code{vp} arguments are automatically handled
|
||
|
by the \gTree{} drawing methods so that \code{gpar()} settings will
|
||
|
be enforced and the viewport will be pushed when the \code{splot} is
|
||
|
drawn.
|
||
|
\item The \code{cl} argument means that the \grob{} created is a
|
||
|
special sort of \grob{} called \code{splot}. This will allow us to
|
||
|
write methods specifically for our scatterplot (see later examples).
|
||
|
\end{enumerate}
|
||
|
|
||
|
|
||
|
@
|
||
|
|
||
|
Now that we have a \grob{}, there are some more interesting things
|
||
|
that we can do with it. First of all, the \code{splot} \grob{}
|
||
|
provides a container for the \grob{}s which make up the scatterplot.
|
||
|
If we modify the \code{splot} \grob{}, it affects all of the children.
|
||
|
|
||
|
<<results=hide>>=
|
||
|
splot(1:10, 1:10, "Same as Before", name = "splot")
|
||
|
grid.edit("splot", gp = gpar(cex=0.5))
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splotgrob>>
|
||
|
sg <- editGrob(sg, gp = gpar(cex = 0.5))
|
||
|
grid.draw(sg)
|
||
|
@
|
||
|
|
||
|
We can access elements of the \code{splot} \grob{} to edit them
|
||
|
individually.
|
||
|
|
||
|
<<results=hide>>=
|
||
|
splot(1:10, 1:10, "Same as Before", name = "splot")
|
||
|
grid.edit(gPath("splot", "points"), gp = gpar(col = 1:10))
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splotgrob>>
|
||
|
sg <- editGrob(sg, gPath = "points", gp = gpar(col = 1:10))
|
||
|
grid.draw(sg)
|
||
|
|
||
|
@
|
||
|
|
||
|
With a little more work we can make the scatterplot a bit more dynamic.
|
||
|
The following describes a \code{editDetails()} method for the
|
||
|
\code{splot} \grob{}. This will be called whenever a scatterplot
|
||
|
is edited and will update the components of the scatterplot.
|
||
|
|
||
|
<<>>=
|
||
|
editDetails.splot <- function(x, specs) {
|
||
|
if (any(c("x", "y") %in% names(specs))) {
|
||
|
if (is.null(specs$x)) xx <- x$x else xx <- specs$x
|
||
|
if (is.null(specs$y)) yy <- x$y else yy <- specs$y
|
||
|
x$childrenvp <- splot.data.vp(xx, yy)
|
||
|
x <- addGrob(x, pointsGrob(xx, yy, name = "points",
|
||
|
vp = "dataregion"))
|
||
|
}
|
||
|
x
|
||
|
}
|
||
|
splot(1:10, 1:10, "Same as Before", name = "splot")
|
||
|
grid.edit("splot", x = 1:100, y = (1:100)^2)
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splotgrob>>
|
||
|
sg <- editGrob(sg, x = 1:100, y = (1:100)^2)
|
||
|
grid.draw(sg)
|
||
|
@
|
||
|
|
||
|
The \code{splot} \grob{} can also be used in the construction of other
|
||
|
\grob{}s. Here's a simple scatterplot matrix \grob{}\footnote{{\bf
|
||
|
Warning:} As the number of \grob{}s in a \gTree{} gets larger the
|
||
|
construction of the \gTree{} will get slow. If this happens, the
|
||
|
best solution is to just use a \grid{} function rather than a
|
||
|
\gTree{}, and wait for me to implement some ideas for speeding
|
||
|
things up!}.
|
||
|
|
||
|
<<fig=TRUE>>=
|
||
|
cellname <- function(i, j) paste("cell", i, j, sep = "")
|
||
|
|
||
|
splom.vpTree <- function(n) {
|
||
|
vplist <- vector("list", n^2)
|
||
|
for (i in 1:n)
|
||
|
for (j in 1:n)
|
||
|
vplist[[(i - 1)*n + j]] <-
|
||
|
viewport(layout.pos.row = i, layout.pos.col = j,
|
||
|
name = cellname(i, j))
|
||
|
vpTree(viewport(layout = grid.layout(n, n), name = "cellgrid"),
|
||
|
do.call("vpList", vplist))
|
||
|
}
|
||
|
|
||
|
cellpath <- function(i, j) vpPath("cellgrid", cellname(i, j))
|
||
|
|
||
|
splom <- function(df, name = NULL, draw = TRUE) {
|
||
|
n <- dim(df)[2]
|
||
|
glist <- vector("list", n*n)
|
||
|
for (i in 1:n)
|
||
|
for (j in 1:n) {
|
||
|
glist[[(i - 1)*n + j]] <-if (i == j)
|
||
|
textGrob(paste("diag", i, sep = ""),
|
||
|
gp = gpar(col = "grey"), vp = cellpath(i, j))
|
||
|
else if (j > i)
|
||
|
textGrob(cellname(i, j),
|
||
|
name = cellname(i, j),
|
||
|
gp = gpar(col = "grey"), vp = cellpath(i, j))
|
||
|
else
|
||
|
splot(df[,j], df[,i], "",
|
||
|
name = paste("plot", i, j, sep = ""),
|
||
|
vp = cellpath(i, j),
|
||
|
gp = gpar(cex = 0.5), draw = FALSE)
|
||
|
}
|
||
|
smg <- gTree(name = name, childrenvp = splom.vpTree(n),
|
||
|
children = do.call("gList", glist))
|
||
|
if (draw) grid.draw(smg)
|
||
|
smg
|
||
|
}
|
||
|
|
||
|
df <- data.frame(x = rnorm(10), y = rnorm(10), z = rnorm(10))
|
||
|
splom(df)
|
||
|
@
|
||
|
|
||
|
This \grob{} can be edited as usual:
|
||
|
|
||
|
<<>>=
|
||
|
splom(df)
|
||
|
grid.edit("plot21::xlab", label = "", redraw = FALSE)
|
||
|
grid.edit("plot32::ylab", label = "", redraw = FALSE)
|
||
|
grid.edit("plot21::xaxis", label = FALSE, redraw = FALSE)
|
||
|
grid.edit("plot32::yaxis", label = FALSE)
|
||
|
<<splomgrob, eval=FALSE, echo=FALSE>>=
|
||
|
smg <- splom(df, draw = FALSE)
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splomgrob>>
|
||
|
smg <- editGrob(smg, gPath = "plot21::xaxis", label = FALSE)
|
||
|
smg <- editGrob(smg, gPath = "plot21::xlab", label = "")
|
||
|
smg <- editGrob(smg, gPath = "plot32::yaxis", label = FALSE)
|
||
|
smg <- editGrob(smg, gPath = "plot32::ylab", label = "")
|
||
|
grid.draw(smg)
|
||
|
@
|
||
|
|
||
|
But of more interest, because this is a \grob{}, is the
|
||
|
\emph{programmatic} interface. With a \grob{} (as opposed to a
|
||
|
function) it is possible to modify the description of what is being
|
||
|
drawn via an API (as opposed to having to edit the original code). In
|
||
|
the following, we remove one of the ``spare'' cell labels and put in
|
||
|
its place the current date.
|
||
|
|
||
|
<<>>=
|
||
|
splom(df, name = "splom")
|
||
|
grid.remove("cell12")
|
||
|
grid.add("splom", textGrob(date(), name = "date",
|
||
|
gp = gpar(fontface = "italic"),
|
||
|
vp = "cellgrid::cell12"))
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splomgrob>>
|
||
|
smg <- removeGrob(smg, "cell12")
|
||
|
smg <- addGrob(smg, textGrob(date(), name = "date",
|
||
|
gp = gpar(fontface = "italic"),
|
||
|
vp = "cellgrid::cell12"))
|
||
|
grid.draw(smg)
|
||
|
@
|
||
|
|
||
|
With the date added as a component of the scatterplot matrix, it is
|
||
|
saved as part of the matrix. The next sequence saves the scatterplot
|
||
|
matrix, loads it again, extracts the bottom-left plot and the date
|
||
|
and just draws those two objects together.
|
||
|
|
||
|
<<>>=
|
||
|
splom(df, name = "splom")
|
||
|
grid.remove("cell12")
|
||
|
grid.add("splom", textGrob(date(), name = "date",
|
||
|
gp = gpar(fontface = "italic"),
|
||
|
vp = "cellgrid::cell12"))
|
||
|
smg <- grid.get("splom")
|
||
|
save(smg, file = "splom.RData")
|
||
|
load("splom.RData")
|
||
|
plot <- getGrob(smg, "plot31")
|
||
|
date <- getGrob(smg, "date")
|
||
|
plot <- editGrob(plot, vp = NULL, gp = gpar(cex = 1))
|
||
|
date <- editGrob(date, y = unit(1, "npc") - unit(1, "lines"), vp = NULL)
|
||
|
grid.newpage()
|
||
|
grid.draw(plot)
|
||
|
grid.draw(date)
|
||
|
|
||
|
<<fig=TRUE, echo=FALSE, results=hide>>=
|
||
|
<<splomgrob>>
|
||
|
smg <- removeGrob(smg, "cell12")
|
||
|
smg <- addGrob(smg, textGrob(date(), name = "date",
|
||
|
gp = gpar(fontface = "italic"),
|
||
|
vp = "cellgrid::cell12"))
|
||
|
save(smg, file = "splom.RData")
|
||
|
load("splom.RData")
|
||
|
plot <- getGrob(smg, "plot31")
|
||
|
date <- getGrob(smg, "date")
|
||
|
plot <- editGrob(plot, vp = NULL, gp = gpar(cex = 1))
|
||
|
date <- editGrob(date, y = unit(1, "npc") - unit(1, "lines"), vp = NULL)
|
||
|
grid.draw(plot)
|
||
|
grid.draw(date)
|
||
|
@
|
||
|
|
||
|
All of this may seem a bit irrelevant to interactive use, but it does
|
||
|
provide a basis for creating an editable plot interface as used in
|
||
|
\I{M.~Kondrin}'s \pkg{Rgrace} package (available on CRAN 2005--7).
|
||
|
|
||
|
\end{document}
|