问题
I want to make specific figures that we need in oceanography. Unfortunately double, triple or more axes are not very good implemented in R. I don't need a double y axis as in doubleYScale of lattice extra. I need double or triple x axis. I could not find a way to use doubleYScale to my advantage. Maybe that's possible. Help would be appreciated a lot.
This is what I have now based on the data:
stackoverflow_fluo.csv: http://pastebin.com/embed_js.php?i=7KNEiytF
animals_stackoverflow.csv:http://pastebin.com/embed_js.php?i=CnEJaq6b
Important update: I forgot to mention that the depth values on the y-axis of both datasets are differently spaced.
library(latticeExtra)
#dataset 1
data1011 <- file.path('stackoverflow_fluo.csv')
jdatax1 = read.csv(data1011)
jdatax1$stat<-as.factor(jdatax1$Station)
#dataset2
data1012 <- file.path('animals_stackoverflow.csv')
jdatax2 = read.csv(data1012)
jdatax2$stat<-as.factor(jdatax2$stat)
#attempt multi axes
animals<-barchart( depth_good ~Mass | stat, data = jdatax2)
fluo<-xyplot( depth~chl | stat, data = jdatax1, type = "l")
doubleYScale(animals, fluo)
#plot
jpeg("double_y", width = 11, height = 8.5, units = 'in', res = 300)
doubleYScale(animals, fluo)
dev.off()
What I need is exactly like that except that the pink data (fluo) needs it's own axis. The bar chart has to be like this but in fact I would like to have the y axis reversed so that 0 is at the top. The actual data has also more stations, so it would be like 8 panels of data.
Looking forward to see what can be done with this! Thanks a lot!
EDIT: Added example. See here:
PS. I am not saying that I want something looking like that-.- or with too many axes. But two x would be nice -.-
回答1:
As far as I know, there's no prepackaged solution to the more general question here.
The example below presents a couple of approaches to adding an additional axis. The second and more general approach (which I'd be inclined to use even when adding an axis along the plot's boundary) works by first pushing a viewport and then adding an axis along its edge. By pushing a viewport an inch high (for example) it allows you to produce an axis that floats an inch above the plot. Pushing a viewport with a supplied xlim=
argument also allows you to set its native coordinate system, which allows you to sidestep some otherwise-required coordinate-system conversions.
There's much more in the moderately-commented code below, which I'll let you explore on your own!
library(lattice)
library(grid)
## Functions for converting units between axes
year2salinity <- function(year) {33 + (1/30)*(year-1900)}
salinity2year <- function(salinity) 1900 + 30*(salinity-33)
year2copepod <- function(year) {1000 + 100*(year-1900)}
## A better pretty(). (base::pretty() will often return limits that
## run beyond plot's ends.)
prettyBetween <- function(x,...) {
xx <- pretty(x,...)
xx[xx >= min(x) & xx <= max(x)]
}
## Custom axis-drawing function to be invoked via xyplot(..., axis=customAxis)
customAxis <- function(side, ...) {
if (side == "top") {
xlim <- current.panel.limits()$xlim
## Method #1 (Only works for axis along side of plot)
atSalinity <- prettyBetween(year2salinity(xlim))
panel.axis(side = side, outside = TRUE, at=salinity2year(atSalinity),
labels = as.character(atSalinity),
rot=0)
grid.text("Salinity", gp=gpar(cex=0.9),
y=unit(1, "npc") + unit(2.5, "lines"))
## Method #2 (Works for "floating" axis or -- with viewport height=0 --
## for axis along side of plot.)
atCopepod <- prettyBetween(year2copepod(xlim))
pushViewport(viewport(height = unit(4, "lines"),
y = 1, just = "bottom",
xscale = year2copepod(xlim)))
panel.axis(side = side, outside = TRUE, at=atCopepod,
labels = as.character(atCopepod),
line.col = "grey65", text.col = "grey35", rot=0)
## panel.axis doesn't draw the axis' "baseline", so we do it using grid.axis
grid.xaxis(at = atCopepod, label = FALSE,
main = FALSE, gp = gpar(col="grey65"))
grid.text(expression("Copepods m"^{-3}), gp=gpar(cex=0.9, col="grey35"),
y=unit(1, "npc") + unit(2.5, "lines"))
popViewport()
}
else {
axis.default(side = side, ...)
}
}
xyplot(nhtemp ~ time(nhtemp), aspect = "xy", type = "o",
xlab = "Year", ylab = "Temperature",
axis = customAxis,
main = "Yearly temperature, salinity, and copepod abundance",
scales = list(x=list(alternating=3)),
## Set up key.axis.padding (an element of each lattice plot's layout) to
## understand values in terms of lines...
lattice.options=list(layout.heights=list(key.axis.padding=list(x=1,units="lines"))),
## ... so that you can tell it you need 6 "lines" of space for axes
par.settings = list(layout.heights=list(key.axis.padding=6)))
Additional note, mostly for myself:
The code above requires calls to both panel.axis()
and grid.xaxis()
, which is not really ideal. The only reason we need to call grid.xaxis()
(and, for that matter, to define the function prettyBetween()
) is that panel.axis()
draws ticks and labels but not the axis baseline. If panel.axis()
had an option to do so, things here would be a lot simpler. To see what that would be like, run trace()
to append a bit of extra baseline-drawing code to each panel.axis()
call...
trace(panel.axis,
exit=expression(
grid.lines(x = unit(at[c(1,length(at))], "native"),
y = unit(c(1,1), "npc"),
gp = gp.line)))
.... after which calls to panel axis (with side=="top"
) will plot the baseline we'd like.
来源:https://stackoverflow.com/questions/33578103/double-x-axis-analog-to-doubleyscale-with-lattice-extra-or-similar