How to add arrow using grid package - r

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.

Related

Issues with adding a Line and arrow

I'm trying to add a text, line and arrow to highlight a certain event in my plot. I followed the example I found and I do get an arrow and a line, however the quality looks very poor. The line should be smooth and the arrow sharp.
I'm using annotate() here for the line, the arrow and the text.
annotate(
"text", x="feb 2020", y=173000, label = "Text", vjust =1, size =4, col="#535353")+
annotate(
"curve", x="feb 2020", y=175000, xend="mrt 2020", yend=200000,
arrow= arrow(length= unit(0.2, "cm"), type = "closed" ), col="#535353", curvature=-0.4, size=1
)
I also tried using geom_curve() with similar settings, the result wasn't much better.
Thanks in advance!
This probably depends on the device. Compare and contrast the default png() device on windows versus the ragg::agg_png() device. The latter has nicer anti-aliasing properties.
Dummy plot:
library(ggplot2)
plt <- ggplot() +
geom_curve(aes(x = 1, y = 1, xend = 2, yend = 2),
curvature = 0.2,
arrow = arrow(type = "closed"))
The default windows png device:
png("default_windows.png", width = 200, height = 200)
print(plt)
dev.off()
With the ragg package:
ragg::agg_png("ragg_windows.png", width = 200, height = 200)
print(plt)
dev.off()

How does The Economist make these lines near the title using using ggplot?

I really like the aesthetics of The Economist magazine and I use the theme_economist often. However, I am curious as to how they create the red lines in the top left in a lot of their charts. See image below and where I circled.
This question is a mix of "how to annotate outside the plot area" and "how to annotate in npc coordinates". Therefore, I offer two options.
Both unfortunately require a bit of trial and error in order to correctly place the segment. For option 1, it is the y coordinate which we have to "guess", and for option 2 it's x!
In order to make y slightly less guess work, I tried an approach to position is relative to the default axis breaks. using the fabulous information from this answer. This is of course not necessary, one can also simply trial and error.
For option 2, I modified a function from user Allan Cameron's answer here. He mentions a way to figure out x and y, I guess one could use the title, and then place the annotation based on that.
library(ggplot2)
p <-
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
ggtitle("lorem ipsum") +
theme(plot.margin = margin(t = 1.5, unit = "lines")) # this is always necessary
# OPTION 1
# semi-programmatic approach to figure out y coordinates
y_defaultticks <- with(mtcars, labeling::extended(range(wt)[1], range(wt)[2], m = 5))
y_default <- y_defaultticks[2] - y_defaultticks[1]
y_seg <- max(mtcars$wt) + 0.75 * y_default
p +
annotate(geom = "segment", x = - Inf, xend = 12, y = y_seg, yend = y_seg,
color = "red", size = 5) +
coord_cartesian(clip = "off", ylim = c(NA, max(mtcars$wt)),
xlim = c(min(mtcars$mpg), NA))
# OPTION 2
annotate_npc <- function(x, y, height, width, ...) {
grid::grid.draw(grid::rectGrob(
x = unit(x, "npc"), y = unit(y, "npc"), height = unit(height, "npc"), width = unit(width, "npc"),
gp = grid::gpar(...)
))
}
p
annotate_npc(x = 0.07, y = 1, height = 0.05, width = 0.05, fill = "red", col = NA)
Created on 2021-01-02 by the reprex package (v0.3.0)

How to draw rectangle using grid package

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()

How to apply cross-hatching to a polygon using the grid graphical system?

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.

Control font thickness without changing font size

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.

Resources