margin coordinates of grid graphics in R - r

I'm about to create a hexbin (lattice) plot that includes vertical lines and margin text. The margin text of the MWE should be located above the line and it should simply state the x-value of the line. Here's what I've got so far:
library(hexbin)
test <- data.frame(VAR1 = rnorm(200), VAR2 = rnorm(200),
GROUP = c(rep(-1.5,60), rep(0.4,20), rep(1.9,120)))
plot(hexbinplot(test$VAR1 ~ test$VAR2,
panel = function(...){
panel.hexbinplot(...)
panel.abline(v = test$GROUP)},
legend = list(top =
list(fun = textGrob,
args = list(
x = unit(test$GROUP/5, "native"),
y = 0.5,
label = test$GROUP,
just = "center")))
))
As you can see from this example, I struggle finding out how to set the coordinates for the labels. Is there a way to use the "real" x-axis coordinates or do I have to somehow deal with the relative ones?

Following the advice of #baptiste , I've created a custom axis ticks and labels on the top which worked out perfectly and made the code much nicer. Here it is:
axisG <- function(side, ...){
if (side == "top"){
at <- unique(test$GROUP)
panel.axis(side = side, outside = TRUE, at = at, labels = at, rot = 0)
}
else axis.default(side = side, ...)
}
plot(hexbinplot(test$VAR1 ~ test$VAR2,
axis = axisG,
panel = function(...){
panel.hexbinplot(...)
panel.abline(v = test$GROUP)}
))

Related

display point labels in scatter3d

I plotted a 3d scatter plot in R using the scatter3d function.
Now, I want to plot the labels on every dot in the 3d scatter, such as every point has its ID next to it i.e., "1", "2" etc..
Here is what I tried:
library("car")
library("rgl")
scatter3d(geometry[,1],geometry[,2],geometry[,3] , surface=FALSE, labels = rownames(geometry), id.n=nrow(geometry))
This tutorial says that adding arguments labels=rownames(geometry), id.n=nrow(geometry) should display the labels on every dot but that did not work.
EDIT:
I uploaded the coordinate file here, you can read it like this
geometry = read.csv("geometry.txt",sep = " ")
colnames(geometry) = c("x","y","z")
EDIT:
Actually, even the example from the tutorial does not label the points and does not produce the plot displayed. There is probably something wrong with the package.
scatter3d(x = sep.l, y = pet.l, z = sep.w,
surface=FALSE, labels = rownames(iris), id.n=nrow(iris))
I can give you a quick fix if you want to use any other function other than scatter3d. This can be achieved using plot3d and text3d function. I have provided the basic code block of how it can be implemented. You can customize it to your needs.
plot3d(geometry[,1],geometry[,2],geometry[,3])
text3d(geometry[,1],geometry[,2],geometry[,3],rownames(geometry))
points3d(geometry[,1],geometry[,2],geometry[,3], size = 5)
After much messing around I got it (I also have the method for plot_ly if you,re interested)
test2 <- cbind(dataSet[,paste(d)],set.final$Groups,test)
X <- test2[,1]
Y <- test2[,2]
Z <- test2[,3]
# 3D plot with the regression plane
scatter3d(x = X, y = Y, z = Z, groups = test2$`set.final$Groups`,
grid = FALSE, fit = "linear",ellipsoid = FALSE, surface=FALSE,
surface.col = c("green", "blue", "red"),
#showLabels(x = x, y = y, z = z, labels=test2$test, method="identify",n = nrow(test2), cex=1, col=carPalette()[1], location=c("lr"))
#labels = test2$test,
id=list(method = "mahal", n = length(test2$test), labels = test2$test)
#id.n=nrow(test2$test)
)
#identify3d(x = X, y = Y, z = Z, labels = test2$test, n = length(test2$test), plot = TRUE, adj = c(-0.1, 0.5), tolerance = 20, buttons = c("right"))
rglwidget()

How to add text to a specific/fixed location in rasterVis levelplot

In fact, this question is consist of two questions targeting the same behaviour.
How can I add text (varies by each panel) to a fixed location in
panel area? I'm aware of panel.text and latticeExtra::layer
solution but it adds text using plotting area coordinates. For
instance, I want to add text to bottom-right corner of each panel
even if their scales are different.
How to add text out of levelplot panel area(s)? Method explained
here requires that levelplot has a plot_01.legend.top.vp area
to add text which I don't have and the trellis object was plotted
before. Besides, I want to add text to left of ylab shown in the
figure below. I used ylab here to state the meaning of rows but I
need a second ylab that represents y-axis values. I found another
question for this problem but It does not work.
The plot above is created by raster::stack object and a rasterVis::levelplot method. I consent to a dirty solution even if I prefer an elegant one. Also despite the question above, I'm open to other approaches that use levelplot.
A very similar issue is currently being discussed on R-sig-Geo, just have a look at the solution I provided there. Here is the corresponding sample code which lets you add custom text annotations inside or outside the panel regions of a trellis graph using trellis.focus(..., clip.off = TRUE) from lattice.
library(rasterVis)
library(grid)
## sample data
f <- system.file("external/test.grd", package="raster")
r <- raster(f)
s <- stack(r, r+500, r-500, r+200)
p <- levelplot(s, layout = c(2, 2), names.att = rep("", 4),
scales = list(y = list(rot = 90)))
## labels
cls <- c("col1", "col2")
rws <- c("row1", "row2")
png("~/rasterVis.png", width = 14, height = 16, units = "cm", res = 300L)
grid.newpage()
print(p, newpage = FALSE)
## loop over panels to be labelled (ie 1:3)
panels = trellis.currentLayout()
for (i in 1:3) {
# focus on current panel of interest and disable clipping
ids <- which(panels == i, arr.ind = TRUE)
trellis.focus("panel", ids[2], ids[1], clip.off = TRUE)
# add labels
if (i %in% c(1, 3)) {
if (i == 1) {
grid.text(cls[1], x = .5, y = 1.1) # add 'col1'
grid.text(rws[1], x = -.35, y = .5, rot = 90) # add 'row1'
} else {
grid.text(rws[2], x = -.35, y = .5, rot = 90) # add 'row2'
}
} else {
grid.text(cls[2], x = .5, y = 1.1) # add 'col2'
}
trellis.unfocus()
}
dev.off()
You may find some further information here:
https://stat.ethz.ch/pipermail/r-help/2005-June/072745.html
http://r.789695.n4.nabble.com/How-to-put-text-outside-an-xyplot-td975850.html

How to change the legend box width when plotting in R

I use the following script to generate a legend in R. But the legend box is too small... how do I increase the box width?
legend("topleft", lty = 1, legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),col = c("black","red","blue"))
You are probably resizing your graph after you plot it and the legend. If that is the case, and you want to keep the box, one option would be to plot the graph, resize it, and then generate the legend. Perhaps a better option would be to size the window to the desired width to start with:
# on Windows, you can use the `windows` function. elsewhere, try quartz or X11
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
#plot = FALSE,
#bty = "n")
)
You can also define exactly where you want the box to fall by providing a pair of x and y coordinates to the legend function. Those values would represent the upper left and bottom right corners of the box. The legend function will actually generate the coordinates for the upper-left hand corner of the box along with the width and height. By default it returns them invisibly, but you can assign them to an object, and If you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
legend(x = c(9.46, 31), y = c(346.32, 298),
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
lty = 1)
The legend function will actually generate the coordinates for the upper-left hand corner of the box (that's where I got 9.46 and 346.62) along with the width and height of the box. By default it returns them invisibly, but you can assign them to an object, and if you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
plot = FALSE)
# adjust as desired
leftx <- leg$rect$left
rightx <- (leg$rect$left + leg$rect$w) * 1.2
topy <- leg$rect$top
bottomy <- (leg$rect$top - leg$rect$h) * 1
# use the new coordinates to define custom
legend(x = c(leftx, rightx), y = c(topy, bottomy), lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"))
Part of the legend width is determined by the longest width of the labels you use, which is calculated via strwidth. Below an easy example how to halve or double the size by using legend(..., text.width = ...).
plot(1)
text = c("Sub_metering_1","Sub_metering_2","Sub_metering_3")
legend("topleft"
,lty = 1
,legend = text
,col = c("black","red","blue")
)
strwidth(text)
# [1] 0.1734099 0.1734099 0.1734099
# half the length
legend("bottomleft"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]/2
,col = c("black","red","blue")
)
# double the length
legend("center"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]*2
,col = c("black","red","blue")
)

Lattice plot with both a second x- and a second y-axis?

I would like to add a 2nd y-axis (right) and a 2nd x-axis (top) to the following (lattice) levelplot. These axes should only indicate certain rows and columns (no labels) and thus mimick base-graphics' rug function. How can this be done?
library(lattice)
library(latticeExtra)
## Generate a correlation matrix
d <- 50
L <- diag(1:d)
set.seed(271)
L[lower.tri(L)] <- runif(choose(d,2))
Sigma <- L %*% t(L)
P <- cor(Sigma)
## Panel function
my_panel <- function(...) {
panel.levelplot(...)
panel.abline(h = (1:4)*10, v = (1:4)*10, lty = 2)
panel.axis(side = "top", at = (1:50)-0.5, draw.labels = FALSE) # maybe a panel axis could do it? why not centered?
}
## Plot
obj1 <- levelplot(P, xlab = "Column", ylab = "Row",
col.regions = grey(c(seq(1, 0, length.out = 600))),
panel = my_panel)
obj2 <- xyplot(NA~NA, ylim = c(0, 50),
scales = list(x = list(at = (1:50)-0.5, labels = rep("", 50)),
y = list(at = (1:50)-0.5, labels = rep("", 50))))
doubleYScale(obj1, obj2, use.style = FALSE) # idea based on latticeExtra; only gives a 2nd y-axis, though
You were onto a good idea with panel.rug(), but were stymied by lattice's default clipping of its plotting to the panel's interior. To get around that, you can turn off clipping via the par.settings= argument. If you want to suppress the plotting of default axis tick marks on the right and top panel borders, you can do so using the tck= argument, as shown below.
my_panel <- function(...) {
panel.levelplot(...)
panel.abline(h = (1:4)*10, v = (1:4)*10, lty = 2)
## Have panel.rug print tick marks starting at 1 npc (edge of panel)
## and extending to 1.02 npc (slightly outside of panel). (See ?unit)
panel.rug(x = (1:51)-0.5, y = (1:51)-0.5,
start = 1, end = 1.02,
col="black")
}
levelplot(P, xlab = "Column", ylab = "Row",
col.regions = grey(c(seq(1, 0, length.out = 600))),
## Suppress default scales on right and top sides, by setting their
## tick lengths to zero
scales = list(tck=c(1,0)),
## Turn off clipping, so that panel.rug can plot outside of the panel
par.settings = list(clip = list(panel = "off")),
panel = my_panel)

How to change the legend title and position in a lattice plot

I'm using lsmip from lsmeans to plot my model,
library(lsmeans)
PhWs1 <- lsmip(GausNugget1, Photoperiod:Ws ~ Month,
ylab = "Observed log(number of leaves)", xlab = "Month",
main = "Interaction between Photoperiod and Water stress over the months (3 photoperiods)",
par.settings = list(fontsize = list(text = 15, points = 10)))
but I was not able to get a suggestion on the internet on how to handle the legend position, size, title, etc.
I used trellis.par.get() to see the parameters but I could not find the one related to my issue. As you can see from the graph, the legend should be "Photoperiod*Ws" but Ws is not visible.
I see two possibly complementing alternatives to approach this issue. The first would be to create a fully customized legend and pass it on to the key argument of xyplot (which lsmip is heavily based on). Here is an example taken from ?lsmip to clarify my point.
## default trellis point theme
trellis_points <- trellis.par.get("superpose.symbol")
## create customized key
key <- list(title = "Some legend title", # legend title
cex.title = 1.2,
x = .7, y = .9, # legend position
points = list(col = trellis_points$col[1:2], # points
pch = trellis_points$pch[1:2],
cex = 1.5),
text = list(c("A", "B"), cex = .9)) # text
## create results and extract lattice plot
d <- lsmip(warp.lm, wool ~ tension, plotit = FALSE,
main = "Some figure title", key = key)
p <- attr(d, "lattice")
p
As you can see, setting up a customized legend let's you modify all the different components of the legend - including labels, text and symbol sizes, legend spacing, etc. Have a deeper look at the key argument described in ?xyplot which describes the various modification options in detail.
Now, if you have a long legend title and you do not want to include the legend inside the plot area, you could also define separate viewports, thus allowing the legend to occupy more space at the right margin. Note the use of update to remove the initially created legend from p and the subsequent assembly of the single figure components using grid functionality.
## remove legend from figure
p <- update(p, legend = NULL)
## assemble figure incl. legend
library(grid)
png("plot.png", width = 14, height = 10, units = "cm", res = 300)
grid.newpage()
## add figure without legend
vp0 <- viewport(x = 0, y = 0, width = .75, height = 1,
just = c("left", "bottom"))
pushViewport(vp0)
print(p, newpage = FALSE)
## add legend
upViewport(0)
vp1 <- viewport(x = .7, y = 0, width = .3, height = 1,
just = c("left", "bottom"))
pushViewport(vp1)
draw.key(key, draw = TRUE)
dev.off()

Resources