| 61990 |
ripley |
1 |
% File src/library/grid/vignettes/plotexample.Rnw
|
|
|
2 |
% Part of the R package, http://www.R-project.org
|
|
|
3 |
% Copyright 2001-13 Paul Murrell and the R Core Team
|
|
|
4 |
% Distributed under GPL 2 or later
|
|
|
5 |
|
| 28502 |
murrell |
6 |
\documentclass[a4paper]{article}
|
|
|
7 |
%\VignetteIndexEntry{Writing grid Code}
|
|
|
8 |
%\VignettePackage{grid}
|
| 49741 |
murrell |
9 |
\newcommand{\code}[1]{\texttt{#1}}
|
| 49817 |
hornik |
10 |
\newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}}
|
|
|
11 |
\newcommand{\grid}{\pkg{grid}}
|
| 49741 |
murrell |
12 |
\newcommand{\grob}{\code{grob}}
|
|
|
13 |
\newcommand{\gTree}{\code{gTree}}
|
|
|
14 |
\newcommand{\R}{{\sffamily R}}
|
| 28502 |
murrell |
15 |
\setlength{\parindent}{0in}
|
|
|
16 |
\setlength{\parskip}{.1in}
|
| 28707 |
murrell |
17 |
\setlength{\textwidth}{140mm}
|
|
|
18 |
\setlength{\oddsidemargin}{10mm}
|
| 28502 |
murrell |
19 |
|
|
|
20 |
\newcommand{\aside}[1]{\begin{list}{}
|
|
|
21 |
{\setlength{\leftmargin}{1in}
|
|
|
22 |
\setlength{\rightmargin}{1in}
|
|
|
23 |
\setlength{\itemindent}{0in}}
|
| 55298 |
ripley |
24 |
\item \textsc{Aside:} \emph{#1}
|
| 28502 |
murrell |
25 |
\end{list}}
|
|
|
26 |
|
|
|
27 |
\title{Writing \grid{} Code}
|
|
|
28 |
\author{Paul Murrell}
|
|
|
29 |
|
|
|
30 |
\begin{document}
|
|
|
31 |
\maketitle
|
|
|
32 |
|
| 28707 |
murrell |
33 |
<<echo=FALSE, results=hide>>=
|
| 39581 |
murrell |
34 |
library(grDevices)
|
|
|
35 |
library(stats) # for runif()
|
| 28707 |
murrell |
36 |
library(grid)
|
|
|
37 |
ps.options(pointsize=12)
|
|
|
38 |
options(width=60)
|
| 55298 |
ripley |
39 |
@
|
| 28707 |
murrell |
40 |
|
| 28502 |
murrell |
41 |
The \grid{} system contains a degree of complexity in order
|
|
|
42 |
to allow things like editing graphical objects, ``packing'' graphical
|
|
|
43 |
objects, and so on. This means that many of the
|
|
|
44 |
predefined Grid graphics functions are
|
|
|
45 |
relatively complicated\footnote{Although there are exceptions; some
|
| 49741 |
murrell |
46 |
functions, such as \code{grid.show.viewport}, are purely for producing
|
| 28502 |
murrell |
47 |
illustrative diagrams and remain simple and procedural.}.
|
|
|
48 |
|
| 55298 |
ripley |
49 |
One design aim of \grid{} is to allow users to create simple graphics
|
|
|
50 |
simply and not to force them to use complicated concepts or write
|
|
|
51 |
complicated code unless they actually need to. Along similar lines,
|
|
|
52 |
it is intended that people should be able to prototype even complex
|
|
|
53 |
graphics very simply and then refine the implementation into a more
|
| 42476 |
ripley |
54 |
sophisticated form if necessary.
|
| 28502 |
murrell |
55 |
|
| 55298 |
ripley |
56 |
With the predefined graphics functions being fully-developed and
|
|
|
57 |
complicated implementations, there is a lack of examples of simple,
|
|
|
58 |
prototype code. Furthermore, given that the aim is to allow a range
|
|
|
59 |
of ways to produce the same graphical output, there is a need for
|
|
|
60 |
examples which demonstrate the various stages, from simple to complex,
|
| 28502 |
murrell |
61 |
that a piece of \grid{} code can go through.
|
|
|
62 |
|
| 55298 |
ripley |
63 |
This document describes the construction of a scatterplot object, like
|
|
|
64 |
that shown below, going from the simplest, prototype implementation to
|
|
|
65 |
the most complex and sophisticated. It demonstrates that if you only
|
|
|
66 |
want simple graphics output then you can do it pretty simply and
|
|
|
67 |
quickly. It also demonstrates how to write functions that allow your
|
|
|
68 |
graphics to be used by other people. Finally, it demonstrates how to
|
|
|
69 |
make your graphics fully interactive (or at least as interactive as
|
|
|
70 |
Grid will let you make it).
|
| 28502 |
murrell |
71 |
|
|
|
72 |
@
|
|
|
73 |
This document should be read {\em after} the \grid{} Users'
|
|
|
74 |
Guide. Here we are assuming that the reader has an understanding
|
|
|
75 |
of viewports, layouts, and units. For the later sections of the
|
|
|
76 |
document, it will also be helpful to have an understanding of
|
| 49741 |
murrell |
77 |
\R{}'s \code{S3} object system.
|
| 28502 |
murrell |
78 |
|
|
|
79 |
\section*{Procedural \grid{}}
|
|
|
80 |
|
| 55298 |
ripley |
81 |
The simplest way to produce graphical output in Grid is just like
|
|
|
82 |
producing standard R graphical output. You simply issue a series of
|
|
|
83 |
graphics commands and each command adds more ink to the plot. The
|
|
|
84 |
purpose of the commands is simply to produce graphics output; in
|
|
|
85 |
particular, we are not concerned with any values returned by the
|
|
|
86 |
plotting functions. I will call this \emph{procedural graphics}.
|
| 28502 |
murrell |
87 |
|
|
|
88 |
In order to draw a simple scatterplot, we can issue a series
|
|
|
89 |
of commands which draw the various components of the plot.
|
|
|
90 |
|
|
|
91 |
Here are some random data to plot.
|
|
|
92 |
|
|
|
93 |
<<>>=
|
|
|
94 |
x <- runif(10)
|
|
|
95 |
y <- runif(10)
|
|
|
96 |
@
|
|
|
97 |
\noindent
|
|
|
98 |
The first step in creating the plot involves defining a ``data'' region.
|
|
|
99 |
This is a region which has sensible scales on the axes for plotting the
|
|
|
100 |
data and margins around the outside
|
|
|
101 |
for the axes to fit in, with a space for a title at the top.
|
|
|
102 |
|
|
|
103 |
<<datavp>>=
|
| 55298 |
ripley |
104 |
data.vp <- viewport(x = unit(5, "lines"),
|
|
|
105 |
y = unit(4, "lines"),
|
|
|
106 |
width = unit(1, "npc") - unit(7, "lines"),
|
|
|
107 |
height = unit(1, "npc") - unit(7, "lines"),
|
|
|
108 |
just = c("left", "bottom"),
|
| 55315 |
ripley |
109 |
xscale = range(x) + c(-0.05, 0.05)*diff(range(x)),
|
|
|
110 |
yscale = range(y) + c(-0.05, 0.05)*diff(range(y)))
|
| 28502 |
murrell |
111 |
@
|
|
|
112 |
\noindent
|
| 55298 |
ripley |
113 |
Now we create the data region and draw the components of the plot
|
|
|
114 |
relative to it: points, axes, labels, and a title.
|
| 28502 |
murrell |
115 |
|
|
|
116 |
<<procplot>>=
|
|
|
117 |
pushViewport(data.vp)
|
|
|
118 |
grid.points(x, y)
|
|
|
119 |
grid.rect()
|
|
|
120 |
grid.xaxis()
|
|
|
121 |
grid.yaxis()
|
| 55298 |
ripley |
122 |
grid.text("x axis", y = unit(-3, "lines"),
|
|
|
123 |
gp = gpar(fontsize = 14))
|
|
|
124 |
grid.text("y axis", x = unit(-4, "lines"),
|
|
|
125 |
gp = gpar(fontsize = 14), rot = 90)
|
| 42476 |
ripley |
126 |
grid.text("A Simple Plot",
|
| 55298 |
ripley |
127 |
y = unit(1, "npc") + unit(1.5, "lines"),
|
|
|
128 |
gp = gpar(fontsize = 16))
|
| 28502 |
murrell |
129 |
popViewport()
|
|
|
130 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
131 |
<<procplot>>
|
|
|
132 |
@
|
|
|
133 |
\section*{Facilitating Annotation}
|
|
|
134 |
|
| 42476 |
ripley |
135 |
Issuing a series of commands to produce a plot, like in the previous
|
| 28502 |
murrell |
136 |
section, allows the user to have a great deal of flexibility.
|
| 42476 |
ripley |
137 |
It is always possible to recreate viewports in order to add
|
| 28502 |
murrell |
138 |
further annotations. For example, the following code
|
|
|
139 |
recreates the data region in order to place the date
|
|
|
140 |
at the bottom right corner.
|
|
|
141 |
|
|
|
142 |
<<ann1>>=
|
|
|
143 |
pushViewport(data.vp)
|
| 55298 |
ripley |
144 |
grid.text(date(), x = unit(1, "npc"), y = 0,
|
|
|
145 |
just = c("right", "bottom"), gp = gpar(col="grey"))
|
| 28502 |
murrell |
146 |
popViewport()
|
|
|
147 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
148 |
<<procplot>>
|
|
|
149 |
<<ann1>>
|
|
|
150 |
@
|
|
|
151 |
|
| 42476 |
ripley |
152 |
When more complex arrangements of viewports are involved, there may be
|
| 28502 |
murrell |
153 |
a bewildering array of viewports created, which may make it difficult
|
| 55298 |
ripley |
154 |
for other users to revisit a particular region of a plot. A
|
|
|
155 |
\code{lattice} plot is a good example. In such cases, it will be more
|
|
|
156 |
cooperative to use \code{upViewport()} rather than
|
|
|
157 |
\code{popViewport()} and leave the viewports that were created during
|
|
|
158 |
the drawing of the plot. Other users can then use \code{vpPath}s to
|
|
|
159 |
navigate to the desired region. For example, here is a slight
|
|
|
160 |
modification of the original series of commands, where the original
|
|
|
161 |
data viewport is given a name and \code{upViewport()} is used at the
|
|
|
162 |
end.
|
| 28502 |
murrell |
163 |
|
|
|
164 |
<<results=hide>>=
|
| 55298 |
ripley |
165 |
data.vp <- viewport(name = "dataregion",
|
|
|
166 |
x = unit(5, "lines"),
|
|
|
167 |
y = unit(4, "lines"),
|
|
|
168 |
width = unit(1, "npc") - unit(7, "lines"),
|
|
|
169 |
height = unit(1, "npc") - unit(7, "lines"),
|
|
|
170 |
just = c("left", "bottom"),
|
| 55315 |
ripley |
171 |
xscale = range(x) + c(-0.05, 0.05)*diff(range(x)),
|
|
|
172 |
yscale = range(y) + c(-0.05, 0.05)*diff(range(y)))
|
| 28502 |
murrell |
173 |
pushViewport(data.vp)
|
|
|
174 |
grid.points(x, y)
|
|
|
175 |
grid.rect()
|
|
|
176 |
grid.xaxis()
|
|
|
177 |
grid.yaxis()
|
| 55298 |
ripley |
178 |
grid.text("x axis", y = unit(-3, "lines"),
|
|
|
179 |
gp = gpar(fontsize = 14))
|
|
|
180 |
grid.text("y axis", x = unit(-4, "lines"),
|
|
|
181 |
gp = gpar(fontsize = 14), rot = 90)
|
| 42476 |
ripley |
182 |
grid.text("A Simple Plot",
|
| 55298 |
ripley |
183 |
y = unit(1, "npc") + unit(1.5, "lines"),
|
|
|
184 |
gp = gpar(fontsize = 16))
|
| 28502 |
murrell |
185 |
upViewport()
|
|
|
186 |
@
|
|
|
187 |
|
| 55298 |
ripley |
188 |
The date is now added using \code{downViewport()} to get to the data
|
|
|
189 |
region.
|
| 28502 |
murrell |
190 |
|
|
|
191 |
<<results=hide>>=
|
|
|
192 |
downViewport("dataregion")
|
| 55298 |
ripley |
193 |
grid.text(date(), x = unit(1, "npc"), y = 0,
|
|
|
194 |
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
| 28502 |
murrell |
195 |
upViewport()
|
|
|
196 |
@
|
|
|
197 |
\section*{Writing a \grid{} Function}
|
|
|
198 |
|
|
|
199 |
Here is the scatterplot code wrapped up as a simple function.
|
|
|
200 |
|
|
|
201 |
<<funcplot>>=
|
| 55298 |
ripley |
202 |
splot <- function(x = runif(10), y = runif(10), title = "A Simple Plot") {
|
|
|
203 |
data.vp <- viewport(name = "dataregion",
|
|
|
204 |
x = unit(5, "lines"),
|
|
|
205 |
y = unit(4, "lines"),
|
|
|
206 |
width = unit(1, "npc") - unit(7, "lines"),
|
|
|
207 |
height = unit(1, "npc") - unit(7, "lines"),
|
|
|
208 |
just = c("left", "bottom"),
|
|
|
209 |
xscale = range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
210 |
yscale = range(y) + c(-.05, .05)*diff(range(y)))
|
|
|
211 |
pushViewport(data.vp)
|
|
|
212 |
grid.points(x, y)
|
|
|
213 |
grid.rect()
|
|
|
214 |
grid.xaxis()
|
|
|
215 |
grid.yaxis()
|
|
|
216 |
grid.text("y axis", x = unit(-4, "lines"),
|
|
|
217 |
gp = gpar(fontsize = 14), rot = 90)
|
|
|
218 |
grid.text(title, y = unit(1, "npc") + unit(1.5, "lines"),
|
|
|
219 |
gp = gpar(fontsize = 16))
|
|
|
220 |
upViewport()
|
| 28502 |
murrell |
221 |
}
|
|
|
222 |
@
|
| 42476 |
ripley |
223 |
There are several advantages to creating a
|
| 28502 |
murrell |
224 |
function:
|
|
|
225 |
\begin{enumerate}
|
| 42476 |
ripley |
226 |
\item We get the standard advantages of a function:
|
| 28502 |
murrell |
227 |
we can reuse and maintain the plot code more easily.
|
| 42476 |
ripley |
228 |
\item We can slightly generalise the plot. In this case, we can use it for
|
| 28502 |
murrell |
229 |
different data and have a different title. We could
|
|
|
230 |
add more arguments to allow different margins, control over
|
|
|
231 |
the axis scales, and so on.
|
|
|
232 |
\item The plot can be embedded in other graphics output.
|
|
|
233 |
\end{enumerate}
|
| 49741 |
murrell |
234 |
Here is an example which uses the \code{splot()} function to
|
| 42476 |
ripley |
235 |
create a slightly modified scatterplot, embedded within
|
| 28502 |
murrell |
236 |
other \grid{} output.
|
|
|
237 |
|
|
|
238 |
<<embed, fig=TRUE, results=hide>>=
|
| 55298 |
ripley |
239 |
grid.rect(gp = gpar(fill = "grey"))
|
|
|
240 |
message <-
|
|
|
241 |
paste("I could draw all sorts",
|
|
|
242 |
"of stuff over here",
|
|
|
243 |
"then create a viewport",
|
|
|
244 |
"over there and stick",
|
|
|
245 |
"a scatterplot in it.", sep = "\n")
|
|
|
246 |
grid.text(message, x = 0.25)
|
|
|
247 |
grid.lines(x = unit.c(unit(0.25, "npc") + 0.5*stringWidth(message) +
|
|
|
248 |
unit(2, "mm"),
|
|
|
249 |
unit(0.5, "npc") - unit(2, "mm")),
|
|
|
250 |
y = 0.5,
|
|
|
251 |
arrow = arrow(angle = 15, type = "closed"),
|
|
|
252 |
gp = gpar(lwd = 3, fill = "black"))
|
|
|
253 |
pushViewport(viewport(x = 0.5, height = 0.5, width = 0.45, just = "left",
|
|
|
254 |
gp = gpar(cex = 0.5)))
|
|
|
255 |
grid.rect(gp = gpar(fill = "white"))
|
|
|
256 |
splot(1:10, 1:10, title = "An Embedded Plot")
|
| 28502 |
murrell |
257 |
upViewport()
|
|
|
258 |
@
|
|
|
259 |
|
| 42476 |
ripley |
260 |
It is still straightforward to annotate the scatterplot as long as
|
| 28502 |
murrell |
261 |
we have enough information about the viewports. In this case,
|
| 49741 |
murrell |
262 |
a non-strict \code{downViewport()} will still work (though note
|
|
|
263 |
that \code{upViewport({\bf 0})} is required to get right back to the
|
| 28502 |
murrell |
264 |
top level).
|
|
|
265 |
|
| 55298 |
ripley |
266 |
<<ann2, echo = FALSE, eval=FALSE>>=
|
| 28502 |
murrell |
267 |
downViewport("dataregion")
|
| 55298 |
ripley |
268 |
grid.text(date(), x = unit(1, "npc"), y = 0,
|
|
|
269 |
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
| 28502 |
murrell |
270 |
upViewport(0)
|
|
|
271 |
<<echo=FALSE, results=hide>>=
|
|
|
272 |
<<embed>>
|
|
|
273 |
<<ann2>>
|
|
|
274 |
@
|
|
|
275 |
\section*{Creating \grid{} Graphical Objects}
|
|
|
276 |
|
| 42476 |
ripley |
277 |
A \grid{} function like the one in the previous section provides
|
| 28502 |
murrell |
278 |
output which is very flexible and can be annotated in arbitrary ways
|
|
|
279 |
and can be embedded within other output. This is likely
|
|
|
280 |
to satisfy most uses.
|
|
|
281 |
|
|
|
282 |
However, there are some things that cannot be done (or at least would
|
|
|
283 |
be extremely hard to do) with such a function. The output produced by
|
| 55298 |
ripley |
284 |
the function cannot be addressed as a coherent whole. It is not
|
|
|
285 |
possible, for example, to to change the \code{x} and \code{y} data
|
|
|
286 |
used in the plot and have the points and axes update automatically.
|
|
|
287 |
There is no scatterplot object to save; the individual components
|
|
|
288 |
exist, but they are not bound together as a whole. If/when these
|
|
|
289 |
sorts of issues become important, it becomes necessary to create a
|
|
|
290 |
\grid{} graphical object (a \grob{}) to represent the plot.
|
| 28502 |
murrell |
291 |
|
|
|
292 |
The first step is to write a function which will create a \grob{}
|
| 55240 |
ripley |
293 |
-- a \emph{constructor} function. In most cases, this will involve
|
| 28502 |
murrell |
294 |
creating a special sort of \grob{} called a \gTree{}; this is just
|
|
|
295 |
a \grob{} that can have other \grob{}s as children. Here's an example
|
| 49741 |
murrell |
296 |
for creating an \code{splot} \grob{}. I have put bits of the
|
| 42476 |
ripley |
297 |
construction into separate functions, for reasons which will become
|
| 28502 |
murrell |
298 |
apparent later.
|
|
|
299 |
|
|
|
300 |
<<>>=
|
|
|
301 |
splot.data.vp <- function(x, y) {
|
| 55298 |
ripley |
302 |
viewport(name = "dataregion",
|
|
|
303 |
x = unit(5, "lines"),
|
|
|
304 |
y = unit(4, "lines"),
|
|
|
305 |
width = unit(1, "npc") - unit(7, "lines"),
|
|
|
306 |
height = unit(1, "npc") - unit(7, "lines"),
|
|
|
307 |
just = c("left", "bottom"),
|
|
|
308 |
xscale = range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
309 |
yscale = range(y) + c(-.05, .05)*diff(range(y)))
|
| 28502 |
murrell |
310 |
}
|
|
|
311 |
|
|
|
312 |
splot.title <- function(title) {
|
| 55298 |
ripley |
313 |
textGrob(title, name = "title",
|
|
|
314 |
y = unit(1, "npc") + unit(1.5, "lines"),
|
|
|
315 |
gp = gpar(fontsize = 16), vp = "dataregion")
|
| 28502 |
murrell |
316 |
}
|
|
|
317 |
|
|
|
318 |
splot <- function(x, y, title, name=NULL, draw=TRUE, gp=gpar(), vp=NULL) {
|
| 55298 |
ripley |
319 |
spg <- gTree(x = x, y = y, title = title, name = name,
|
|
|
320 |
childrenvp = splot.data.vp(x, y),
|
|
|
321 |
children = gList(rectGrob(name = "border",
|
|
|
322 |
vp = "dataregion"),
|
|
|
323 |
xaxisGrob(name = "xaxis", vp = "dataregion"),
|
|
|
324 |
yaxisGrob(name = "yaxis", vp = "dataregion"),
|
|
|
325 |
pointsGrob(x, y, name = "points", vp = "dataregion"),
|
|
|
326 |
textGrob("x axis", y = unit(-3, "lines"), name = "xlab",
|
|
|
327 |
gp = gpar(fontsize = 14), vp = "dataregion"),
|
|
|
328 |
textGrob("y axis", x = unit(-4, "lines"), name = "ylab",
|
|
|
329 |
gp = gpar(fontsize = 14), rot = 90,
|
|
|
330 |
vp = "dataregion"),
|
|
|
331 |
splot.title(title)),
|
|
|
332 |
gp = gp, vp = vp,
|
|
|
333 |
cl = "splot")
|
|
|
334 |
if (draw) grid.draw(spg)
|
|
|
335 |
spg
|
| 28502 |
murrell |
336 |
}
|
|
|
337 |
@
|
|
|
338 |
|
|
|
339 |
There are four important additions to the argument list compared
|
| 49741 |
murrell |
340 |
to the original \code{splot()} function:
|
| 28502 |
murrell |
341 |
\begin{enumerate}
|
| 49741 |
murrell |
342 |
\item The \code{name} argument allows a string identifier to be
|
| 55298 |
ripley |
343 |
associated with the scatterplot object we create. This is important
|
|
|
344 |
for being able to specify the scatterplot when we try to edit it
|
|
|
345 |
after drawing it and/or when it is part of a larger \grob{} (see
|
|
|
346 |
later examples).
|
| 28502 |
murrell |
347 |
|
| 55298 |
ripley |
348 |
\item The \code{draw} argument makes it possible to use the function
|
|
|
349 |
in a procedural manner as before:
|
|
|
350 |
|
| 28502 |
murrell |
351 |
<<splotgrob, eval=FALSE, echo=FALSE>>=
|
| 55298 |
ripley |
352 |
sg <- splot(1:10, 1:10, "Same as Before", name = "splot", draw = FALSE)
|
| 28502 |
murrell |
353 |
<<>>=
|
| 55298 |
ripley |
354 |
splot(1:10, 1:10, "Same as Before", name = "splot")
|
| 28502 |
murrell |
355 |
downViewport("dataregion")
|
| 55298 |
ripley |
356 |
grid.text(date(), x = unit(1, "npc"), y = 0,
|
|
|
357 |
just = c("right", "bottom"), gp = gpar(col = "grey"))
|
| 28502 |
murrell |
358 |
upViewport(0)
|
|
|
359 |
@
|
| 49741 |
murrell |
360 |
\item The \code{gp} argument allows the user to supply \code{gpar()}
|
| 55298 |
ripley |
361 |
settings for the scatterplot as a whole.
|
|
|
362 |
|
|
|
363 |
\item The \code{vp} argument allows the user to supply a viewport for
|
|
|
364 |
the \code{splot} \grob{} to be drawn in. This is especially useful
|
|
|
365 |
for specifying a \code{vpPath} when the \code{splot} is used as a
|
|
|
366 |
component of another \grob{} (see scatterplot matrix example below).
|
| 28502 |
murrell |
367 |
\end{enumerate}
|
|
|
368 |
|
|
|
369 |
The important parts of the \gTree{} definition are:
|
|
|
370 |
\begin{enumerate}
|
| 55298 |
ripley |
371 |
\item The \code{children} argument provides a list of \grob{}s which
|
|
|
372 |
are part of the scatterplot. When the scatterplot is drawn, all
|
|
|
373 |
children will be drawn. Notice that instead of the procedural
|
|
|
374 |
\code{grid.*()} functions we use \code{*Grob()} functions which just
|
|
|
375 |
produce \grob{}s and do not perform any drawing. Also notice that I
|
|
|
376 |
have given each of the children a name; this will make it possible
|
|
|
377 |
to access the components of the scatterplot (see later examples).
|
| 49741 |
murrell |
378 |
\item The \code{childrenvp} argument provides a viewport (or
|
| 55298 |
ripley |
379 |
\code{vpStack}, \code{vpList}, or \code{vpTree}) which will be
|
|
|
380 |
pushed before the children are drawn. The difference between this
|
|
|
381 |
argument and the \code{vp} argument common to all \grob{}s is that
|
|
|
382 |
the \code{vp} is pushed before drawing the children and then popped
|
|
|
383 |
after, whereas the \code{childrenvp} gets pushed \emph{and} then a
|
|
|
384 |
call to \code{upViewport()} is made before the children are drawn.
|
|
|
385 |
This allows the children to simply specify the viewport they should
|
|
|
386 |
be drawn in by way of a \code{vpPath} in their \code{vp} argument.
|
|
|
387 |
In this way, viewports remain available for further annotation such
|
|
|
388 |
as we have already seen in procedural code.
|
|
|
389 |
\item The \code{gp} and \code{vp} arguments are automatically handled
|
|
|
390 |
by the \gTree{} drawing methods so that \code{gpar()} settings will
|
|
|
391 |
be enforced and the viewport will be pushed when the \code{splot} is
|
|
|
392 |
drawn.
|
|
|
393 |
\item The \code{cl} argument means that the \grob{} created is a
|
|
|
394 |
special sort of \grob{} called \code{splot}. This will allow us to
|
|
|
395 |
write methods specifically for our scatterplot (see later examples).
|
| 28502 |
murrell |
396 |
\end{enumerate}
|
|
|
397 |
|
|
|
398 |
|
|
|
399 |
@
|
|
|
400 |
|
| 55298 |
ripley |
401 |
Now that we have a \grob{}, there are some more interesting things
|
|
|
402 |
that we can do with it. First of all, the \code{splot} \grob{}
|
|
|
403 |
provides a container for the \grob{}s which make up the scatterplot.
|
| 49741 |
murrell |
404 |
If we modify the \code{splot} \grob{}, it affects all of the children.
|
| 28502 |
murrell |
405 |
|
|
|
406 |
<<results=hide>>=
|
| 55298 |
ripley |
407 |
splot(1:10, 1:10, "Same as Before", name = "splot")
|
|
|
408 |
grid.edit("splot", gp = gpar(cex=0.5))
|
| 28502 |
murrell |
409 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
410 |
<<splotgrob>>
|
| 55298 |
ripley |
411 |
sg <- editGrob(sg, gp = gpar(cex = 0.5))
|
| 28502 |
murrell |
412 |
grid.draw(sg)
|
| 42476 |
ripley |
413 |
@
|
| 28502 |
murrell |
414 |
|
| 49741 |
murrell |
415 |
We can access elements of the \code{splot} \grob{} to edit them
|
| 28502 |
murrell |
416 |
individually.
|
|
|
417 |
|
|
|
418 |
<<results=hide>>=
|
| 55298 |
ripley |
419 |
splot(1:10, 1:10, "Same as Before", name = "splot")
|
|
|
420 |
grid.edit(gPath("splot", "points"), gp = gpar(col = 1:10))
|
| 28502 |
murrell |
421 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
422 |
<<splotgrob>>
|
| 55298 |
ripley |
423 |
sg <- editGrob(sg, gPath = "points", gp = gpar(col = 1:10))
|
| 28502 |
murrell |
424 |
grid.draw(sg)
|
|
|
425 |
|
|
|
426 |
@
|
|
|
427 |
|
|
|
428 |
With a little more work we can make the scatterplot a bit more dynamic.
|
| 49741 |
murrell |
429 |
The following describes a \code{editDetails()} method for the
|
|
|
430 |
\code{splot} \grob{}. This will be called whenever a scatterplot
|
| 28502 |
murrell |
431 |
is edited and will update the components of the scatterplot.
|
|
|
432 |
|
|
|
433 |
<<>>=
|
|
|
434 |
editDetails.splot <- function(x, specs) {
|
| 55298 |
ripley |
435 |
if (any(c("x", "y") %in% names(specs))) {
|
|
|
436 |
if (is.null(specs$x)) xx <- x$x else xx <- specs$x
|
|
|
437 |
if (is.null(specs$y)) yy <- x$y else yy <- specs$y
|
|
|
438 |
x$childrenvp <- splot.data.vp(xx, yy)
|
|
|
439 |
x <- addGrob(x, pointsGrob(xx, yy, name = "points",
|
|
|
440 |
vp = "dataregion"))
|
|
|
441 |
}
|
| 28502 |
murrell |
442 |
x
|
|
|
443 |
}
|
| 55298 |
ripley |
444 |
splot(1:10, 1:10, "Same as Before", name = "splot")
|
|
|
445 |
grid.edit("splot", x = 1:100, y = (1:100)^2)
|
| 28502 |
murrell |
446 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
447 |
<<splotgrob>>
|
| 55298 |
ripley |
448 |
sg <- editGrob(sg, x = 1:100, y = (1:100)^2)
|
| 28502 |
murrell |
449 |
grid.draw(sg)
|
|
|
450 |
@
|
|
|
451 |
|
| 55298 |
ripley |
452 |
The \code{splot} \grob{} can also be used in the construction of other
|
|
|
453 |
\grob{}s. Here's a simple scatterplot matrix \grob{}\footnote{{\bf
|
|
|
454 |
Warning:} As the number of \grob{}s in a \gTree{} gets larger the
|
|
|
455 |
construction of the \gTree{} will get slow. If this happens, the
|
|
|
456 |
best solution is to just use a \grid{} function rather than a
|
|
|
457 |
\gTree{}, and wait for me to implement some ideas for speeding
|
|
|
458 |
things up!}.
|
| 28502 |
murrell |
459 |
|
|
|
460 |
<<fig=TRUE>>=
|
| 55298 |
ripley |
461 |
cellname <- function(i, j) paste("cell", i, j, sep = "")
|
| 28502 |
murrell |
462 |
|
|
|
463 |
splom.vpTree <- function(n) {
|
| 55298 |
ripley |
464 |
vplist <- vector("list", n^2)
|
|
|
465 |
for (i in 1:n)
|
|
|
466 |
for (j in 1:n)
|
|
|
467 |
vplist[[(i - 1)*n + j]] <-
|
|
|
468 |
viewport(layout.pos.row = i, layout.pos.col = j,
|
|
|
469 |
name = cellname(i, j))
|
|
|
470 |
vpTree(viewport(layout = grid.layout(n, n), name = "cellgrid"),
|
| 28502 |
murrell |
471 |
do.call("vpList", vplist))
|
|
|
472 |
}
|
| 42476 |
ripley |
473 |
|
| 55298 |
ripley |
474 |
cellpath <- function(i, j) vpPath("cellgrid", cellname(i, j))
|
| 28502 |
murrell |
475 |
|
| 55298 |
ripley |
476 |
splom <- function(df, name = NULL, draw = TRUE) {
|
|
|
477 |
n <- dim(df)[2]
|
|
|
478 |
glist <- vector("list", n*n)
|
|
|
479 |
for (i in 1:n)
|
|
|
480 |
for (j in 1:n) {
|
|
|
481 |
glist[[(i - 1)*n + j]] <-if (i == j)
|
|
|
482 |
textGrob(paste("diag", i, sep = ""),
|
|
|
483 |
gp = gpar(col = "grey"), vp = cellpath(i, j))
|
|
|
484 |
else if (j > i)
|
|
|
485 |
textGrob(cellname(i, j),
|
|
|
486 |
name = cellname(i, j),
|
|
|
487 |
gp = gpar(col = "grey"), vp = cellpath(i, j))
|
|
|
488 |
else
|
|
|
489 |
splot(df[,j], df[,i], "",
|
|
|
490 |
name = paste("plot", i, j, sep = ""),
|
|
|
491 |
vp = cellpath(i, j),
|
|
|
492 |
gp = gpar(cex = 0.5), draw = FALSE)
|
|
|
493 |
}
|
|
|
494 |
smg <- gTree(name = name, childrenvp = splom.vpTree(n),
|
|
|
495 |
children = do.call("gList", glist))
|
|
|
496 |
if (draw) grid.draw(smg)
|
|
|
497 |
smg
|
| 28502 |
murrell |
498 |
}
|
|
|
499 |
|
| 55298 |
ripley |
500 |
df <- data.frame(x = rnorm(10), y = rnorm(10), z = rnorm(10))
|
| 28502 |
murrell |
501 |
splom(df)
|
|
|
502 |
@
|
|
|
503 |
|
|
|
504 |
This \grob{} can be edited as usual:
|
|
|
505 |
|
|
|
506 |
<<>>=
|
|
|
507 |
splom(df)
|
| 55298 |
ripley |
508 |
grid.edit("plot21::xlab", label = "", redraw = FALSE)
|
|
|
509 |
grid.edit("plot32::ylab", label = "", redraw = FALSE)
|
|
|
510 |
grid.edit("plot21::xaxis", label = FALSE, redraw = FALSE)
|
|
|
511 |
grid.edit("plot32::yaxis", label = FALSE)
|
| 30260 |
murrell |
512 |
<<splomgrob, eval=FALSE, echo=FALSE>>=
|
| 55298 |
ripley |
513 |
smg <- splom(df, draw = FALSE)
|
| 28502 |
murrell |
514 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
515 |
<<splomgrob>>
|
| 55298 |
ripley |
516 |
smg <- editGrob(smg, gPath = "plot21::xaxis", label = FALSE)
|
|
|
517 |
smg <- editGrob(smg, gPath = "plot21::xlab", label = "")
|
|
|
518 |
smg <- editGrob(smg, gPath = "plot32::yaxis", label = FALSE)
|
|
|
519 |
smg <- editGrob(smg, gPath = "plot32::ylab", label = "")
|
| 28502 |
murrell |
520 |
grid.draw(smg)
|
|
|
521 |
@
|
|
|
522 |
|
| 55298 |
ripley |
523 |
But of more interest, because this is a \grob{}, is the
|
|
|
524 |
\emph{programmatic} interface. With a \grob{} (as opposed to a
|
|
|
525 |
function) it is possible to modify the description of what is being
|
|
|
526 |
drawn via an API (as opposed to having to edit the original code). In
|
|
|
527 |
the following, we remove one of the ``spare'' cell labels and put in
|
|
|
528 |
its place the current date.
|
| 28502 |
murrell |
529 |
|
|
|
530 |
<<>>=
|
| 55298 |
ripley |
531 |
splom(df, name = "splom")
|
| 28502 |
murrell |
532 |
grid.remove("cell12")
|
| 55298 |
ripley |
533 |
grid.add("splom", textGrob(date(), name = "date",
|
|
|
534 |
gp = gpar(fontface = "italic"),
|
|
|
535 |
vp = "cellgrid::cell12"))
|
| 28502 |
murrell |
536 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
537 |
<<splomgrob>>
|
|
|
538 |
smg <- removeGrob(smg, "cell12")
|
| 55298 |
ripley |
539 |
smg <- addGrob(smg, textGrob(date(), name = "date",
|
|
|
540 |
gp = gpar(fontface = "italic"),
|
|
|
541 |
vp = "cellgrid::cell12"))
|
| 28502 |
murrell |
542 |
grid.draw(smg)
|
|
|
543 |
@
|
|
|
544 |
|
|
|
545 |
With the date added as a component of the scatterplot matrix, it is
|
|
|
546 |
saved as part of the matrix. The next sequence saves the scatterplot
|
| 42476 |
ripley |
547 |
matrix, loads it again, extracts the bottom-left plot and the date
|
| 28502 |
murrell |
548 |
and just draws those two objects together.
|
|
|
549 |
|
|
|
550 |
<<>>=
|
| 55298 |
ripley |
551 |
splom(df, name = "splom")
|
| 28502 |
murrell |
552 |
grid.remove("cell12")
|
| 55298 |
ripley |
553 |
grid.add("splom", textGrob(date(), name = "date",
|
|
|
554 |
gp = gpar(fontface = "italic"),
|
|
|
555 |
vp = "cellgrid::cell12"))
|
| 28502 |
murrell |
556 |
smg <- grid.get("splom")
|
| 55298 |
ripley |
557 |
save(smg, file = "splom.RData")
|
| 28502 |
murrell |
558 |
load("splom.RData")
|
|
|
559 |
plot <- getGrob(smg, "plot31")
|
|
|
560 |
date <- getGrob(smg, "date")
|
| 55298 |
ripley |
561 |
plot <- editGrob(plot, vp = NULL, gp = gpar(cex = 1))
|
|
|
562 |
date <- editGrob(date, y = unit(1, "npc") - unit(1, "lines"), vp = NULL)
|
| 28502 |
murrell |
563 |
grid.newpage()
|
|
|
564 |
grid.draw(plot)
|
|
|
565 |
grid.draw(date)
|
|
|
566 |
|
|
|
567 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
568 |
<<splomgrob>>
|
|
|
569 |
smg <- removeGrob(smg, "cell12")
|
| 55298 |
ripley |
570 |
smg <- addGrob(smg, textGrob(date(), name = "date",
|
|
|
571 |
gp = gpar(fontface = "italic"),
|
|
|
572 |
vp = "cellgrid::cell12"))
|
|
|
573 |
save(smg, file = "splom.RData")
|
| 28502 |
murrell |
574 |
load("splom.RData")
|
|
|
575 |
plot <- getGrob(smg, "plot31")
|
|
|
576 |
date <- getGrob(smg, "date")
|
| 55298 |
ripley |
577 |
plot <- editGrob(plot, vp = NULL, gp = gpar(cex = 1))
|
|
|
578 |
date <- editGrob(date, y = unit(1, "npc") - unit(1, "lines"), vp = NULL)
|
| 28502 |
murrell |
579 |
grid.draw(plot)
|
|
|
580 |
grid.draw(date)
|
|
|
581 |
@
|
|
|
582 |
|
| 55298 |
ripley |
583 |
All of this may seem a bit irrelevant to interactive use, but it does
|
|
|
584 |
provide a basis for creating an editable plot interface as used in
|
|
|
585 |
M.~Kondrin's \pkg{Rgrace} package (available on CRAN 2005--7).
|
| 28502 |
murrell |
586 |
|
| 42476 |
ripley |
587 |
\end{document}
|