I need to draw a rectangle on my diagram to highlight different changes. I need to use grid package. I tried to use the grid.rect but it doesn't work. I want that my rectangle looks like on the picture.
On the left part of the picture you can see my diagram and of the right part of the picture I've added the rectangle (in Paint) like I want it will be.
library(grid)
library(lattice)
library(sandwich)
data("Investment")
Investment <- as.data.frame(Investment)
trellis.par.set(theme = canonical.theme("postscript", color=FALSE))
grid.newpage()
pushViewport(viewport(x=0, width=.4, just="left"))
print(barchart(table(Investment$Interest)),
newpage=FALSE)
popViewport()
pushViewport(viewport(x=.4, width=.5, just="left"))
print(xyplot(Investment ~ Price, data=Investment,
auto.key=list(space="right"),
par.settings=list(superpose.symbol=list(pch=c(1, 3, 16),
fill="white"))),
newpage=FALSE)
popViewport()
It is not completely clear where you are trying to draw the rectangle, but the code below will add the rectangle to approximately match your picture. You can tune the position.
Use your code just as you had it. I will start by repeating your print statement and then adding the rectangle.
print(xyplot(Investment ~ Price, data=Investment,
auto.key=list(space="right"),
par.settings=list(superpose.symbol=list(pch=c(1, 3, 16),
fill="white"))),
newpage=FALSE)
grid.rect(x = unit(0.42, "npc"), y = unit(0.35, "npc"),
width = unit(0.2, "npc"), height = unit(0.2, "npc"),
gp=gpar(col="red"))
popViewport()
Related
This is mostly a follow-up question on a previous one.
Given that in ggplot2 and grid there are different linetypes and spacings vary between line sizes, what is their relationship?
There are two things I do not quite understand.
How is the line size defined? If I were to draw a straight vertical line and substitute it by a rectangle, what should be the width of the rectangle to get the equivalent of the line's size? Especially, how does the lwd = 1 or lwd = 10 I pass to par()/gpar() relate to absolute dimensions (pixels, mm, inches, points)?
The gpar() documentation refers to the par() documentation which states the following:
The line width, a positive number, defaulting to 1. The interpretation is device-specific, and some devices do not implement line widths less than one.
Which is fair enough but I couldn't really find the necessary device specific documentation for common devices.
I think I might assume that the spacings of different linetypes are proportional to their size, but how exactly are the 'dotdash', 'dashed', 'dotted' etc. proportions of dash-length to spacing-length defined?
In the plot below, how can I predict or calculate the dash/spacing lengths in advance?
library(ggplot2)
df <- data.frame(
x = rep(c(0, 1), 4),
y = rep(1:4, each = 2),
size = rep(c(2, 10), each = 4),
linetype = rep(c(2,2,3,3), 2)
)
# The `I()` function automatically assigns identity scales
ggplot(df, aes(x, y, size = I(size), linetype = I(linetype))) +
geom_line(aes(group = y))
I think this is mostly a documentation question, so I'd be happy if you could point me to the correct pages. Otherwise, an answer to my two questions above or a demonstration thereof would also be nice.
EDIT: ggplot has a variable called .pt which they use often to multiply a line size with. That probably means that in grid the linesize is something / .pt, but in what units?
Another great question Teunbrand. I have a partial answer here which seems to give valid results but feels a bit imprecise.
The obvious way to get conversion between lwd and length units is to measure them programatically. For example, to check the lwd of the X11 device, you can do this:
library(grid)
x11()
grid.newpage()
# draw a thick black line that goes right across the page
grid.draw(linesGrob(x = unit(c(-0.1, 1.1), "npc"),
y = unit(c(0.5, 0.5), "npc"),
gp = gpar(lwd = 10)))
# Capture as a bitmap
bmp_line <- dev.capture()
# Work out the thickness of the line in pixels as proportion of page height
lwd_10_prop <- sum(bmp_line != "white")/length(bmp_line)
# Now draw a black rectGrob of known height with lwd of 0 and transparent for completeness
grid.newpage()
grid.draw(rectGrob(width = unit(1.1, "npc"),
height = unit(10, "mm"),
gp = gpar(lwd = 0, col = "#00000000", fill = "black")))
# Capture as a bitmap and measure the width as proportion of device pixels
bmp_rect <- dev.capture()
mm_10_prop <- sum(bmp_rect != "white")/length(bmp_rect)
# Get the ratio of lwd to mm
lwd_as_mm <- lwd_10_prop / mm_10_prop
dev.off()
lwd_as_mm
#> [1] 0.2702296
Which tells us that an lwd of 1 is 0.2702296 mm on this device
We can test this by plotting a red rectangle of our calculated width over a green line near the top of our page, then plotting the same green line over the same red rectangle near the bottom of the page. If and only if they are exactly the same width will we have a completely green line and a completely red line on our page:
grid.newpage()
grid.draw(linesGrob(x = unit(c(-0.1, 1.1), "npc"),
y = unit(c(0.75, 0.75), "npc"),
gp = gpar(lwd = 5, col = "green")))
grid.draw(rectGrob(y = unit(0.75, "npc"),
width = unit(1.1, "npc"),
height = unit(5 * lwd_as_mm, "mm"),
gp = gpar(lwd = 0, col = "#00000000", fill = "red")))
grid.draw(rectGrob(y = unit(0.25, "npc"),
width = unit(1.1, "npc"),
height = unit(5 * lwd_as_mm, "mm"),
gp = gpar(lwd = 0, col = "#00000000", fill = "red")))
grid.draw(linesGrob(x = unit(c(-0.1, 1.1), "npc"),
y = unit(c(0.25, 0.25), "npc"),
gp = gpar(lwd = 5, col = "green")))
Of course, we can improve precision by increasing the thickness of our lines when measuring how wide they are in pixels.
Although the result is supposed to be device-independent, it's worth noting that in the above example I took the results from the X11 device but plotted them in the rstudio device, so the equivalence seems to hold for both devices.
I want to add the arrow using grid package which will highlight the important part of my diagram. I want that the arrow will look like on the picture on the right side.
On the left part is my diagram created by the code below and on the right part is my diagram with the arrow (I've added it using Paint). It's my goal.
library(grid)
library(lattice)
library(sandwich)
data("Investment")
Investment <- as.data.frame(Investment)
pushViewport(plotViewport(c(5, 4, 2, 2)))
pushViewport(dataViewport(Investment$Investment,
Investment$GNP,
name="Zaleznosc Investment od GNP"))
grid.points(Investment$Investment, Investment$GNP, gp=gpar(cex=0.5))
grid.rect()
grid.xaxis()
grid.yaxis()
grid.text("Investment", y=unit(-3, "line"))
grid.text("GNP", x=unit(-3, "line"), rot=90)
popViewport()
You can use the code that you have, but before popViewport() add the code to add your arrow.
grid.lines(x = unit(c(0.42, 0.74), "npc"),
y = unit(c(0.8, 0.86), "npc"),
gp = gpar(fill="black"),
arrow = arrow(length = unit(0.2, "inches"),
ends="last", type="closed"))
Also, to follow up on a comment of #alistaire grid graphics are a bit hard to use. What you are trying to do is mostly easy in base graphics.
plot(GNP ~ Investment, data=Investment)
arrows(250, 2600, 380, 2750, code = 2, lwd=2)
The only thing that is not quite perfect is the type of arrowhead. The base arrows does not give you much control over that. If you don't mind adding a package, the shape package lets you choose the style of arrowhead.
library(shape)
plot(GNP ~ Investment, data=Investment)
Arrows(250, 2600, 380, 2750, code = 2,
arr.type="triangle", arr.width=0.4)
In his comment, #alistaire has mentioned ggplot2 as an alternative to working with grid directly.
For the sake of completeness, here is a ggplot2 version which uses the annotate() function to place an arrow on the chart.
data(Investment, package = "sandwich")
library(ggplot2)
ggplot(as.data.frame(Investment)) +
aes(Investment, GNP) +
geom_point(shape = 1L) +
theme_bw() +
annotate("segment", x = 250, xend = 380, y = 2600, yend = 2800,
arrow = arrow(length = unit(0.2, "inches"), ends = "last", type = "closed"))
Note that ggplot2 reexports the arrow() function from the grid package.
How do you combine two ggplots g1 and g2 with one on the left and one on the right, 90° rotated (only the right one) ?
I have already looked at grid and gridExtra package but I don't find my way through all of this.
Some adjustments might be necessary for the width and height of the second plot, but this seems to work:
p <- qplot(1:10)
library(grid)
grid.newpage()
print(p, vp=viewport(0, 0, width = unit(0.5, "npc"), just = c('left', 'bottom')))
print(p, vp=viewport(0.5, 0, angle = 90, height = unit(0.8, "npc"), width = 0.55, just = c('left', 'top')))
I would like to put a title on a page of plots created using R lattice. For example I can put four plots on a page as follows:
#load lattice
require(lattice).
# data
a<-c(1,3,4)
b<-c(1,2,3)
# make plots
plt1<-xyplot(a~b,main="plt1")
plt2<-xyplot(a~b,main="plt2")
plt3<-xyplot(a~b,main="plt3")
plt4<-xyplot(a~b,main="plt4")
# plot plots
plot(plt1, split=c(1,1,2,2),newpage=FALSE)
plot(plt2, split=c(1,2,2,2),newpage=FALSE)
plot(plt3, split=c(2,1,2,2),newpage=FALSE)
plot(plt4, split=c(2,2,2,2),newpage=FALSE)
Now how do a put the title "My Page of Plots" centered in the top margin above plt1 and plt3?
You could use grid to push a viewport and add the title:
library(grid)
vp2 <- viewport(x = 0.5, y = 1, width = 1, height = .1, just = c("center", "top"))
pushViewport(vp2)
grid.rect(gp = gpar(vol = "blue")) # just to see dimensions/position of the viewport
grid.text("My Title", gp = gpar(cex = 2))
You have to play with the position and dimensions of the viewport a bit. And ideally you would also add a top margin to your lattice call, such that you create some white space for your title.
To draw a "crossed" rectangle of height 2 times larger than its width using the low-level graphics package facilities I call:
xlim <- c(0, 500)
ylim <- c(0, 1000)
plot.new()
plot.window(xlim, ylim, asp=1)
rect(xlim[1], ylim[1], xlim[2], ylim[2])
lines(c(xlim[1], xlim[2]), c(ylim[1], ylim[2]))
lines(c(xlim[1], xlim[2]), c(ylim[2], ylim[1]))
The figure has a nice feature: the aspect ratio is preserved so that if I change the size of the plot window, I get the same height-to-width proportions.
How can I obtain an equivalent result with grid graphics?
You should create a viewport that uses Square Normalised Parent Coordinates,
see ?unit:
"snpc": (...) This is useful for making things which are a proportion
of the viewport, but have to be square (or have a fixed aspect ratio).
Here is the code:
library('grid')
xlim <- c(0, 500)
ylim <- c(0, 1000)
grid.newpage() # like plot.new()
pushViewport(viewport( # like plot.window()
x=0.5, y=0.5, # a centered viewport
width=unit(min(1,diff(xlim)/diff(ylim)), "snpc"), # aspect ratio preserved
height=unit(min(1,diff(ylim)/diff(xlim)), "snpc"),
xscale=xlim, # cf. xlim
yscale=ylim # cf. ylim
))
# some drawings:
grid.rect(xlim[1], ylim[1], xlim[2], ylim[2], just=c(0, 0), default.units="native")
grid.lines(xlim, ylim, default.units="native")
grid.lines(xlim, rev(ylim), default.units="native")
The default.units argument in e.g. grid.rect forces the plotting functions
to use the native (xscale/yscale) viewport coordinates.
just=c(0, 0) indicates that xlim[1], ylim[1] denote the bottom-left node
of the rectangle.
In ggplot2 (which is grid based) you can fix the aspect ratio using coord_fixed():
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed(ratio = 0.5)
This will fix the ratio, and the ratio will be constant even when changing the size of the graphics window.
I'm not sure if this is helpful, as you asked for a low-level grid based solution. But I thought it might be useful none the less.