A quick question that I can't find answer on the web (or Wickham's book):
What is the unit of the size argument in ggplot2? For example, geom_text(size = 10) -- 10 in what units?
The same question applies to default unit in ggsave(height = 10, width = 10).
The answer is : The unit is the points. It is the unit of fontsize in the grid package. In ?unit, we find the following definition
"points" Points. There are 72.27 points per inch.
(but note the closely related "bigpts" Big Points. 72 bp = 1 in.)
Internally ggplot2 will multiply the font size by a magic number ggplot2:::.pt, defined as 1/0.352777778.
Here a demonstration, I create a letter using grid and ggplot2 with same size:
library(grid)
library(ggplot2)
ggplot(data=data.frame(x=1,y=1,label=c('A'))) +
geom_text(aes(x,y,label=label),size=100)
## I divide by the magic number to get the same size.
grid.text('A',gp=gpar(fontsize=100/0.352777778,col='red'))
Addendum Thanks to #baptiste
The "magic number"(defined in aaa-constants.r as .pt <- 1 / 0.352777778) is really just the conversion factor between "points" and "mm", that is 1/72 * 25.4 = 0.352777778. Unfortunately, grid makes the subtle distinction between "pts" and "bigpts", which explains why convertUnit(unit(1, "pt"), "mm", valueOnly=TRUE) gives the slightly different value of 0.3514598.
The 'ggplot2' package, like 'lattice' before it, is built on the grid package. You can get the available units at:
?grid::unit
?grid::convertX
?grid::convertY
grid::convertX(grid::unit(72.27, "points"), "inches")
(I use the formalism pkg::func because in most cases grid is loaded a a NAMESPACE but not attached when either lattice or `ggplot2 are loaded.)
I earlier posted a comment that I later deleted saying that size was in points. I did so after seeing that the size of the text with size=10 was roughly 10 mm. The "magic" number mentioned by agstudy is in fact within 1% of:
as.numeric(grid::convertX(grid::unit(1, "points"), "mm"))
#[1] 0.3514598
0.352777778/.Last.value
#[1] 1.00375
From ?aes_linetype_size_shape
# Size examples
# Should be specified with a numerical value (in millimetres),
# or from a variable source
height and width in ggsave relate to par("din") from ?par
din
R.O.; the device dimensions, (width, height), in inches. See also dev.size,
which is updated immediately when an on-screen device windows is re-sized.
So I guess size in aes is in millimetres and ggsave height and width are in inches.
Related
The fontsize is used to determine the size of text or points in R graphics. But what does it actually represent? For example,
grid::grid.newpage()
grid::grid.points(default.units = "npc",
gp = gpar(fontsize = 100))
Here, the point fontsize is 100. So, I have two questions:
What does the value mean, the radius, the diameter or the area of a point? Can I find any reference?
What is the unit of 100? In specific, does it mean 100 pointsize (1 fontsize = 1pt)? Otherwise, is it determined by device-specification? If so, 1 fontsize = ?pt.
Also, I have another question that, in language R, is there any handy functions to convert pt to pixel (px)? For example, we can convert a pt unit to a mm unit as in
convertUnit(unit(1, "pt"), "mm", valueOnly = TRUE)
So, is there any function to query the dimension of the screen and then do the transformation from pt to px?
Thanks!
The size of the symbols drawn by grid.points() depend on several factors, primarily pch, which determines the shape of the symbol, and size. By default, the latter is unit(1, "char"), which means the size further depends on the current font size (fontsize and cex).
Deep in the graphics engine source code (https://github.com/r-devel/r-svn/blob/master/src/main/engine.c#L2110) are some constants that further modify the nominal symbol size based on the symbol shape. For example, pch=1 multiplies size by .375 to get the circle radius.
Yes, fontsize=100 means 100pt.
The following code demonstrates the calculations by drawing a symbol and a rectangle with the same width:
library(grid)
grid.newpage()
pushViewport(viewport())
grid.points(.5, .5)
grid.rect(width=2*.375*unit(1, "char"))
grid.points(.5, .5, gp=gpar(fontsize=100))
grid.rect(width=2*.375*unit(100, "pt"))
Somewhat inexplicably, the length parameter in arrows is specified in inches (from ?arrows):
length length of the edges of the arrow head (in inches).
R source even goes so far as to explicitly make note that this measurement is in inches in a comment, highlighting how peculiar this design is.
That means the relative size of the arrows depends on dev.size(). What's not clear is how to translate inches into axis units (which are infinitely more useful in the first place). Here's a simplified version:
h = c(1, 2, 3)
xs = barplot(h, space = 0, ylim = c(0, 4))
arrows(xs, h - .5, xs, h + .5,
length = .5*mean(diff(xs)))
How this displays will depend on the device. E.g. here is the output on this device:
png('test.png', width = 5, height = 5)
And here it is on another:
png('test.png', width = 8, height = 8)
It's a bit of an optical illusion to tell on sight, but the arrows are indeed the same width in the two plots. How can I control this so that both plots (which convey the same data) display identically? More specifically, how can I make sure that the arrows are exactly .5 plot units in width?
I spent far too much time in the rabbit hole on this, but here goes. I'll document a bit of my journey first to aid others who happen upon this in the types of nooks and crannies to search when trying to pull yourself up by your bootstraps.
I started looking in the source of arrows, but to no avail, since it quickly dives into internal code. So I searched the R source for "C_arrows" to find what's happening; luckily, it's not too esoteric, as far as R internal code goes. Poking around it seems the workhorse is actually GArrow, but this was a dead end, as it seems the length parameter isn't really transformed there (IIUC this means the conversion to inches is done for the other coordinates and length is untouched). But I happened to notice some GConvert calls that looked closer to what I want and hoped to find some user-facing function that appeals to these directly.
This led me to go back to R and to simply run through the gamut of functions in the same package as arrows looking for anything that could be useful:
ls(envir = as.environment('package:grDevices'))
ls(envir = as.environment('package:graphics'))
Finally I found three functions in graphics: xinch, yinch, and xyinch (all found on ?xinch) are used for the opposite of my goal here -- namely, they take inches and convert them into device units (in the x, y, and x&y directions, respectively). Luckily enough, these functions are all very simple, e.g. the work horse of xinch is the conversion factor:
diff(par("usr")[1:2])/par("pin")[1L]
Examining ?par (for the 1,000,000th time), indeed pin and usr are exactly the graphical parameter we need (pin is new to me, usr comes up here and there):
pin The current plot dimensions, (width, height), in inches.
usr A vector of the form c(x1, x2, y1, y2) giving the extremes of the user coordinates of the plotting region.
Hence, we can convert from plot units to inches by inverting this function:
xinch_inv = function(dev_unit) {
dev_unit * par("pin")[1L]/diff(par("usr")[1:2])
}
h = c(1, 2, 3)
xs = barplot(h, space = 0, ylim = c(0, 4))
arrows(xs, h - .5, xs, h + .5,
# just convert plot units to inches
length = xinch_inv(.5*mean(diff(xs))))
Resulting in (5x5):
And (8x8):
One further note, it appears length is the length of each side of the arrow head -- using length = xinch_inv(.5), code = 3, angle = 90 results in segments as wide as the bars (i.e., 1).
On the off chance you're interested, I've packaged these in my package as xdev2in, etc.; GitHub only for now.
I've been trying to figure out how to make the little circles that represent mutation steps on a haplotype network bigger. For whatever reason, all the normal ways I'd think don't seem to be working. It seems like no matter what I do, the symbols remain tiny. What am I missing?
Here's a base bit of sample code:
data(woodmouse)
h <- haplotype(woodmouse)
net <- haploNet(h)
plot(net, size=attr(net,"freq")*3,bg=pal,labels=F, fast=F, legend=F,
show.mutation=T,threshold=0)
# using scale.ratio = 1, the mutations are visisble
plot(net, size=attr(net,"freq")*3,bg=pal,labels=F, fast=F, legend=F,
show.mutation=T,threshold=0,scale.ratio=3)
# but using scale.ratio=3, they get tiny / disappear
You can see the mutations here, but if I set scale.ratio to something bigger (a requirement with my own data), they essentially disappear.
I've tried passing a larger cex to plot (doesn't work) as well as setting cex globally with par (makes the whole plot smaller for some reason).
It seems like the circles are scaled with the lines, but I don't know how to control that. Is it even possible? Am I missing something really obvious?
cex controls the font size and will not help with graphics size. From the help page for the haploNet and plot.haploNet functions:
?haploNet
size: a numeric vector giving the diameter of the circles representing the haplotypes: this is in the same unit than the links and eventually recycled.
scale.ratio: the ratio of the scale of the links representing the number of steps on the scale of the circles representing the haplotypes. It may be needed to give a value greater than one to avoid overlapping circles.
This means that size of the links (circles representing mutations between haplotypes) is relative to the size of the haplotypes. To relatively enlarge the link size, you need to find a suitable combination of the two arguments.
set.seed(123)
net <- haploNet(haplotype(woodmouse[sample(15, size = 50, replace = TRUE), ]))
par(mfrow=c(1,2))
plot(net, size=attr(net,"freq"), labels=F, fast=F, legend=F,
show.mutation=T, threshold=0, scale.ratio=1)
plot(net, size=attr(net,"freq")*.2, labels=F, fast=F, legend=F,
show.mutation=T, threshold=0, scale.ratio=.2)
Enlarging circle size with haploNet runs into a risk that circles will overlap and the visible number of mutations will be incorrect. Use discretion with visualization and in case of problems, consider haplotype network calculation in TCS software where unsampled mutations are displayed with vertical bars or Network from Fluxus with proportional link lengths.
How can I get the width and height of a character in usr coordinates? I found something on R-help, but that appears not to make it completely clear. I assumed that
plot(NULL, xlim=c(-1,1), ylim=c(-1,1))
h <- par()$cxy
rect(-h[1]/2, -h[2]/2, h[1]/2, h[2]/2)
text(0,0,"M")
would be the answer but the rectangle is slightly too big. Additionally I want the size also to respect different cex values. Thanks for your time!
I finally discovered an answer in the par docu:
cxy
R.O.; size of default character (width, height) in user coordinate
units. par("cxy") is par("cin")/par("pin") scaled to user coordinates.
Note that c(strwidth(ch), strheight(ch)) for a given string ch is
usually much more precise.
Using strwidth and strheight instead of par()$cxy gives much better results.
plot(NULL, xlim=c(-1,1), ylim=c(-1,1))
h <- c(strwidth("M"), strheight("M"))
rect(-h[1]/2, -h[2]/2, h[1]/2, h[2]/2)
text(0,0,"M")
I would like to know the approximate dimensions of symbol in my plot area. I think that par()$ps only really refers to text size. So how is a symbol size calculated using the cex parameter? For example, below is a plot of a single point of size cex=10. Can i determine its size from the plot devices par parameters?
plot(50, 50, ylim=c(0,100), xlim=c(0,100), cex=10)
#click on outer x limits
p1 <- locator(n=1,typ="n")
p2 <- locator(n=1,typ="n")
#approx width in x units(~15)
abs(p1$x - p2$x)
Thanks for you help. -Marc
According to the documentation contained in ?par, we have that,
cin - R.O.; character size (width, height) in inches. These are the same measurements as cra, expressed in different units.
cra - R.O.; size of default character (width, height) in ‘rasters’ (pixels). Some devices have no concept of pixels and so assume an arbitrary pixel size, usually 1/72 inch. These are the same measurements as cin, expressed in different units.
On my machine, these values appear to be:
par("cin")
[1] 0.15 0.20
> par("cra")
[1] 10.8 14.4
So character magnification via cex ought to happen relative to these dimensions, presumably by scaling the horizontal and vertical dimensions separately (although I don't know that for sure).