My weekly diary schedule has already slipped! In my defence, last
week was exceptional, because the major activity was neither
entrepreneurial nor academic, but practical and logistical: moving
house. A lengthy rant about the insanity of the English conveyancing
system is probably not of great interest to readers of this diary, so
I will save the accumulated feelings of helplessness and insecurity
for some other outlet.
Meanwhile, back to work. It's the start of teaching next week;
fortunately, I am teaching largely the same material as last year, so
now is the time that I can reap the benefits of preparation time that
spent on the
course over the last two years. Inevitably, there will be new
things to include and outdated material to remove or update, but by
and large I should be able to deliver the same content.
This is a relief, because of course this year I only have one fifth of
my time on academic-related activities. This means that various
things have to be sacrificed or delegated, not least some of my
extra-curricular activities such as being release manager of SBCL – so I'm very glad that Juho Snellman has volunteered to
step in and do that for the next while. (He suffered the by-now
traditional baptism of fire, dealing with amusing regressions late in
the 1.0.42.x series, and released
version 1.0.43 today; we'll see how his coefficient of grumpiness
evolves over the next few months).
In the land of industry, what I've mostly been doing is drawing
graphs. As the screenshot
in last week's my previous
entry suggests, I'm using R for data processing and
visualisation; I have datasets with large numbers of variables, and
the facilities for visualising those quickly and compactly with the lattice package
(implementing Becker and Cleveland's trellis
paradigm) are very convenient. By and large, progressing from
prototype visualisation to presentation- or publication-quality
visualisation is also straightforward, but I spent so long figuring
out one thing that I needed to do this week that I'll document it here
for posterity: that thing was to construct a graph using lattice with
an axis break. It's not that it's absurdly difficult – there
are plenty of hookable or parameterisable functions in the lattice
graph-drawing implementation; the difficult part is finding out
which functions to override, which hooks to use, and which
traps to avoid.
The problem as I have found it is that when drawing a lattice plot,
for these purposes, things happen in an inconvenient order. First,
the axes, tickmarks and labels are drawn, using the axis function
provided to the lattice call (or axis.default
by default; then the data are plotted using the panel function. So,
that would be fine; one could even hackily draw over the axis in the
panel function to implement the axis break, at least if one remembers
to turn clipping off with clip=list(panel="off") in
par.settings. Except that the axis function doesn't actually
draw the axis lines; instead, there's a non-overridable bit of
plot.trellis which draws the box around the plot, effectively
being the axis lines – and that happens after everything else.
So, piling hack upon hack: there's no way of not drawing the box.
There is, however, a way of drawing the box with a line thickness of
zero: pass axis.line=list(lwd=0) in par.settings as
well. Ah, but then the tick marks have zero thickness too. Oh, but
we can override that setting of axis.line$lwd within our custom
axis function. (Each of these realisations took a certain amount of
time, experimentation, and code reading to come to pass...). What it
boils down to, in the end, is a call like
xyplot(gmeans.zoo,
screens=1, col=c(2,3,4), lwd=2, lty=3, more=TRUE,
ylim=c(0.5,1.7), scales=list(
x=list(at=dates, labels=date.labels),
y=list(at=c(0.5,1.0,1.5),
labels=c("- 50%", "± 0%", "+ 50%", "+ 100%"))),
key=list(lines=list(col=c(2,3,4)),
text=list(lab=c("5m", "500k", "galileo"))),
xlab="Date",
par.settings = list(axis.line=list(lwd=0),
clip=list(panel="off", strip="off")),
axis=function(side, scales, components, ...) {
print(scales)
lims <- current.panel.limits()
trellis.par.set(axis.line=list(lwd=0.5))
panel.axis(side=side, outside=TRUE, at=scales$at,
labels=scales$labels,
draw.labels=side %in% c("bottom", "left"), rot=0)
panel.lines(lims$xlim[[1]], lims$ylim, col=1, lwd=1)
panel.lines(lims$xlim[[2]], lims$ylim, col=1, lwd=1)
panel.lines(c(lims$xlim[[1]], as.Date("2010-09-11")+0.45),
lims$ylim[[1]], col=1, lwd=1)
panel.lines(c(lims$xlim[[2]], as.Date("2010-09-11")+0.55),
lims$ylim[[1]], col=1, lwd=1)
panel.lines(c(lims$xlim[[1]], as.Date("2010-09-11")+0.45),
lims$ylim[[2]], col=1, lwd=1)
panel.lines(c(lims$xlim[[2]], as.Date("2010-09-11")+0.55),
lims$ylim[[2]], col=1, lwd=1)
},
panel=function(x,y,...) {
xs <- current.panel.limits()$xlim
ys <- current.panel.limits()$ylim
panel.xyplot(x,y,...)
panel.polygon(as.Date("2010-09-11")+c(0.4,0.6,0.6,0.4),
c(ys[1]+0.05,ys[1]+0.05,ys[2]-0.05,ys[2]-0.05),
border="white", col="white", alpha=1)
panel.lines(xs,1,col=1,lty=3)
panel.lines(as.Date("2010-09-11")+c(0.5,0.6),
c(ys[1]-0.025,ys[1]+0.025), col="black")
panel.lines(as.Date("2010-09-11")+c(0.4,0.5),
c(ys[2]-0.025,ys[2]+0.025), col="black")
panel.lines(as.Date("2010-09-11")+c(0.5,0.6),
c(ys[2]-0.025,ys[2]+0.025), col="black")
panel.lines(as.Date("2010-09-11")+c(0.4,0.5),
c(ys[1]-0.025,ys[1]+0.025), col=1, lwd=1)
panel.text(as.Date("2010-09-20"),
t(gmeans.zoo[1,])+c(0.01,0,-0.01),
sprintf("%2.0f%%", round(100*t(gmeans.zoo[1,]-1))),
pos=4)
})
allows me to draw a picture like
for us to show to interested parties.