While I learned how to plot using grid, R studio showed the result differnet from an example in book. So I run the same code in R, and got the same result to an example in book. I don't know this reason...
below is code.
library(grid)
grid.newpage()
pushViewport(plotViewport(c(5, 4, 2, 2)))
pushViewport(dataViewport(pressure$temperature,pressure$pressure,name="plotRegion"))
grid.points(pressure$temperature, pressure$pressure,name="dataSymbols")
grid.rect()
grid.xaxis()
grid.yaxis()
grid.text("temperature", y=unit(-3, "lines"))
grid.text("pressure", x=unit(-3, "lines"), rot=90)
grid.edit("dataSymbols", pch=2)
upViewport(2)
grid.rect(gp=gpar(lty="dashed"))
downViewport("plotRegion")
grid.text("Pressure (mm Hg)\nversus\nTemperature (Celsius)",x=unit(150, "native"), y=unit(600, "native"))
R studio
R
This code uses the defaults defined in gpar. The help says:
The default parameter settings are defined by the ROOT viewport, which
takes its settings from the graphics device. These defaults may differ
between devices (e.g., the default fill setting is different for a PNG
device compared to a PDF device).
With RStudio:
get.gpar()$fill
#[1] "white"
With RGui:
get.gpar()$fill
#[1] "transparent"
Thus, the RStudio device has different settings. You need to specify explicitly that you don't want the rectangles to be filled.
library(grid)
grid.newpage()
pushViewport(plotViewport(c(5, 4, 2, 2)))
pushViewport(dataViewport(pressure$temperature,pressure$pressure,name="plotRegion"))
grid.points(pressure$temperature, pressure$pressure,name="dataSymbols")
grid.rect(gp = gpar(fill = NA))
grid.xaxis()
grid.yaxis()
grid.text("temperature", y=unit(-3, "lines"))
grid.text("pressure", x=unit(-3, "lines"), rot=90)
grid.edit("dataSymbols", pch=2)
upViewport(2)
grid.rect(gp=gpar(lty="dashed", fill = NA))
downViewport("plotRegion")
grid.text("Pressure (mm Hg)\nversus\nTemperature (Celsius)",x=unit(150, "native"), y=unit(600, "native"))
Related
I am working with that book and R-Studio. I tried to render a grid graphic as described in the book:
library(grid)
pushViewport(plotViewport(c(5, 4, 2, 2)))
pushViewport(dataViewport(pressure$temperature,pressure$pressure,name="plotRegion"))
grid.points(pressure$temperature, pressure$pressure,name="dataSymbols")
grid.rect()
grid.xaxis()
grid.yaxis()
grid.text("temperature", y=unit(-3, "lines"))
grid.text("pressure", x=unit(-3, "lines"), rot=90)
grid.edit("dataSymbols", pch=2)
upViewport(1)
grid.rect(gp=gpar(lty="dashed"))
downViewport("plotRegion")
grid.text("Pressure (mm Hg)\nversus\nTemperature (Celsius)",x=unit(150, "native"), y=unit(600, "native"))
When running that code, it looks like the following:
According to the book, the result should look like this
What do I wrong? Was also running dev.off() without any effect....
I have access to the dataset pressure
I need to arrange several plots for a figure. I am creating individual plots using base and grid graphics. In order to arrange them in a single figure I have been using grid.echo(), grid.grab() to convert to grobs and then arrangeGrob() and grid.arrange() to build the final figure. A few weeks ago my tentative figure was working fine but now when I rerun the code it produces a figure with whitespace in the margins of the plots.
I add a minimal example that shows the problem that I am facing...
##minimal example
library(grid)
library(gridExtra)
library(gridGraphics)
##test plot
plot_n1<-plot(1:10,1:10, asp=1)
##convert test plot to grob
grid.echo()
test_p<-grid.grab()
##simulate several plots arranged in a more complex layout
multi<-arrangeGrob(test_p, test_p, test_p, test_p, ncol=1, heights=c(1/4,1/4,1/4,1/4))
##create graph
png(filename="minimal_multiplot.png", res=300, width=20, height=20, units="cm")
grid.arrange(test_p, multi, ncol=2, widths=c(2/3,1/3))
dev.off()
What am I doing wrong?
There does indeed appear to be a problem when converting a graphics plot into a grid plot, then using grid.grab() to grab and then draw the plot into a smaller regions (i.e., using your method). For instance, using viewports to define a slightly smaller region (coloured grey in the image below), axis material is missing.
# Packages
library(grid)
library(gridGraphics)
plot(1:10,1:10)
grid.echo()
test_p = grid.grab()
grid.newpage()
pushViewport(viewport(x = 0, width = .85, just = "left"))
grid.rect(gp = gpar(col = NA, fill = "grey90"))
grid.draw(test_p)
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))
But Paul Murrell (author of the gridGraphics package) offers an alternative (see the examples at ?gridGraphics::grid.echo, and pp. 156-157 of The gridGraphics package in The R Journal v7/1). One can define a function that draws the plot, then that function becomes the argument of grid.echo() at the time of drawing the plot within the viewport. newpage = FALSE stops grid from opening a new page. Note that none of the axis material is chopped off.
pf = function() {
plot(1:10,1:10)
}
grid.newpage()
pushViewport(viewport(x = 0, width = .85, just = "left"))
grid.rect(gp = gpar(col =NA, fill = "grey90"))
grid.echo(pf, newpage=FALSE)
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))
So to get your desired plot, I would do something like this - but still using viewports.
pf = function() {
par(mar=c(7.2, 7.2, 1, 1), mex = .3, tcl = .15, mgp = c(3, .15, 0))
plot(1:10, 1:10, cex.axis = .75, cex.lab = .75)
}
grid.newpage()
pushViewport(viewport(layout = grid.layout(3, 2,
widths = unit(c(2, 1), "null"),
heights = unit(c(1, 1, 1), "null"))))
pushViewport(viewport(layout.pos.col = 1, layout.pos.row = 1:3))
grid.echo(pf, newpage = FALSE)
upViewport()
for(i in 1:3) {
pushViewport(viewport(layout.pos.col = 2, layout.pos.row = i))
grid.echo(pf, newpage = FALSE)
upViewport()
}
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))
Several functions in R's base graphical system, including rect() and polygon(), support cross-hatching via their angle= and density= arguments:
x = c(0, 0.5, 1, 0.5)
y = c(0.5, 1, 0.5, 0)
par(mar=c(0,0,0,0))
plot.new()
polygon(x, y, angle=45, density=10)
How might I apply similar cross-hatching to a polygon drawn by the grid graphical system's grid.polygon() function:
library(grid)
grid.newpage()
grid.polygon(x,y)
I've looked in the documentation for ?grid.polygon and ?gpar, and have skimmed through Paul Murrel's book on R graphics, and have so far come up empty. Am I missing something obvious? If not, is there some simple hack which will make this possible?
Here's an example with gridSVG adapted from Paul Murrell's presentation
library(gridSVG)
library(grid)
x = c(0, 0.5, 1, 0.5)
y = c(0.5, 1, 0.5, 0)
grid.newpage()
grid.polygon(x,y, name="goodshape")
pat <- pattern(linesGrob(gp=gpar(col="black",lwd=3)),
width = unit(5, "mm"), height = unit(5, "mm"),
dev.width = 1, dev.height = 1)
# Registering pattern
registerPatternFill("pat", pat)
# Applying pattern fill
grid.patternFill("goodshape", label = "pat")
grid.export("test-pattern.svg")
more complex grobs are allowed as well, since svg takes care of the clipping.
I am using a package (treemap) that uses grid package to produce a treemap. However, I would like to plot several of these treemaps together, to add different color schemes to these plots. tmPlot function uses grid.newpage function, which clears the graphics window. I have not found a way to save grid.newpage objects as you can do for ggplot2objects. Is there a way to plot several grid.newpage objects to a same window?
## Example
library(treemap)
# load Gross national income data
data(GNI2010)
size <- aggregate(GNI ~ continent, GNI2010, sum)
size <- size[with(size, order(GNI, decreasing = T)),]
cont <- size$continent
widths <- c(sum(size[c(1,3,5),]$GNI),
sum(size$GNI) - sum(size[c(1,3,5),]$GNI))
heights <- c(sum(size[c(1,2),]$GNI),
sum(size[c(3,4),]$GNI),
sum(size[c(5,6),]$GNI))
palettes <- c("Greens", "Blues", "Reds", "Oranges", "Purples", "Greys")
i <- 1 # This is to be replaced by for loop
x <- subset(GNI2010, continent == cont[i], cex = 5)
# create treemap
layout(matrix(1:6, 3, byrow = TRUE), widths = widths, heights = heights)
x1 <- tmPlot(x,
index=c("iso3"),
vSize="population",
vColor="GNI",
type="value", title = "",
position.legend = "none",
palette = palettes[i])
grid.text(cont[i], 0.5, 0.5, gp=gpar(fontsize=20, font = 2, col = "white"))
## x1 is does not make a plot as such and tmPlot overwrites layout
I understand that my solution to scale the plots based on GNI sum is not right. I might make another question about that later, once I figure out how to plot these treemaps in a same window.
EDIT: I think the answer to this question is "no". Currently you cannot save grid.newpage objects by name, neither can you save several of these on a page, because the function "erases the current device or moves to a new page" as said in the description. However, it is possible to find work arounds. tmPlot package does not currently (as of 23 March, 2013) support viewports, but the development version does.
Thanks for your question. The output of tmPlot is indeed not a saved plot.
In the next update I will add argument vp, by which a viewport can be specified to draw in. Only if it is not specified, grid.newpage is called.
UPDATE: You could check and test the development version at https://github.com/mtennekes/treemap
To apply the example of Bryan Hanson:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
tmPlot(GNI2010,
index="continent",
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,1))
tmPlot(GNI2010,
index=c("continent", "iso3"),
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,2))
Here's an approach that is very flexible for any grid graphics:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(a, vp = vplayout(1,1))
print(b, vp = vplayout(1,2))
Where a and b are your saved plot objects. So test each plot individually ahead of time, save them as a, b, ... then plot them as above.
Oh, and if tmPlot always does grid.newpage then check to see if it has a has new.page argument which you can set to FALSE, or make a copy of the function and comment out the newpage.
I'm looking for a way to control the line thickness of text plotted in R without having the dimensions of the characters change. Here's an example (not using R):
The middle word has a thickness of twice the top, yet the dimensions are the same (so no scaling happened). The bottom word is actually two words: a red word overlain on a heavy white word, to create color separation (especially useful for annotating a busy plot).
Here's a set of commands I threw together to try and replicate the figure above:
png("font.png",width=1.02, height=1.02, units="in", res=150)
par(ps=10, font=1, bg="light gray", col="black", mai=rep(0.02,4), pin=c(1,1))
plot.new()
box()
text(0.5,0.85,"FONT",cex=1)
text(0.5,0.6,"FONT",cex=2)
text(0.5,0.3,"FONT",cex=2,col="white")
text(0.5,0.3,"FONT",cex=1,col="red")
text(0.5,0.1,"FONT",cex=1, font=2, col="white")
text(0.5,0.1,"FONT",cex=1, font=1, col="red")
dev.off()
giving:
So the effect is the same as changing the font-face to bold, but the size difference is not big enough to be noticeable when overlain. The par help page doesn't appear to have a specific setting for this. Anyone have any ideas?
Note changing size in ggplot2 doesn't produce the effect I want either, last time I checked.
You could try adding multiple versions of the text slightly shifted in a circular pattern,
library(grid)
stextGrob <- function (label, r=0.02, x = unit(0.5, "npc"), y = unit(0.5, "npc"),
just = "centre", hjust = NULL, vjust = NULL, rot = 0, check.overlap = FALSE,
default.units = "npc", name = NULL, gp = gpar(), vp = NULL){
let <- textGrob("a", gp=gp, vp=vp)
wlet <- grobWidth(let)
hlet <- grobHeight(let)
tg <- textGrob(label=label, x=x, y=y, gp=gpar(col="red"),
just = just, hjust = hjust, vjust = vjust, rot = rot,
check.overlap = check.overlap,
default.units = default.units)
tgl <- c(lapply(seq(0, 2*pi, length=36), function(theta){
textGrob(label=label,x=x+cos(theta)*r*wlet,
y=y+sin(theta)*r*hlet, gp=gpar(col="white"),
just = just, hjust = hjust, vjust = vjust, rot = rot,
check.overlap = check.overlap,
default.units = default.units)
}), list(tg))
g <- gTree(children=do.call(gList, tgl), vp=vp, name=name, gp=gp)
}
grid.stext <- function(...){
g <- stextGrob(...)
grid.draw(g)
invisible(g)
}
grid.newpage()
grid.rect(gp=gpar(fill="grey"))
grid.stext("Yeah", gp=gpar(cex=4))
There's a version using base graphics lurking in the archives of R-help, from which this is inspired.
Another option using a temporary postscript file, converted to a shape by grImport,
library(grImport)
cat("%!PS
/Times-Roman findfont
100 scalefont
setfont
newpath
0 0 moveto
(hello) show", file="hello.ps")
PostScriptTrace("hello.ps", "hello.xml")
hello <- readPicture("hello.xml")
grid.rect(gp=gpar(fill="grey"))
grid.picture(hello,use.gc = FALSE, gp=gpar(fill="red", lwd=8, col="white"))
I imagine something similar could be done with a temporary raster graphic file, blurred by some image processing algorithm and displayed as raster below the text.
You could try:
text(...,"FONT", vfont = c('serif','bold'))
Although I'm not sure how you'd do the third version of FONT.