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