?par says this about mgp[1:3]:
The margin line (in mex units) for the axis title, axis labels and axis line. Note that mgp[1] affects title whereas mgp[2:3] affect axis. The default is c(3, 1, 0).
?axis says something similar. However, it seems that tick labels are not in general placed at line mgp[2], but rather at line mgp[2] + (mgp[3] %% 1). Thus, axis seems to behave as documented only when mgp[3] is an integer. Is this a bug in axis, or is something else going on?
Here is an example showing expected and actual output of axis for integer and noninteger mgp[3]:
par(mar = c(5, 1, 5, 1))
plot.new()
plot.window(xlim = c(0, 1), ylim = c(0, 1))
box(lty = 3)
## 'axis' puts line and labels in right place when 'mgp[3]' is integer
mgp_bottom <- c(0, 2.5, 1) # 'mgp[1]' is arbitrary
axis(side = 1, mgp = mgp_bottom)
mtext(c("labels", "line"), side = 1, line = mgp_bottom[2:3])
## 'axis' puts line in right place when 'mgp[3]' is noninteger,
## but apparently not labels
mgp_top <- mgp_bottom - c(0, 0, 1e-3)
axis(side = 3, mgp = mgp_top)
mtext(c("labels", "line"), side = 3, line = mgp_top[2:3])
mtext("LABELS", side = 3, line = mgp_top[2] + (mgp_top[3] %% 1))
This does look like a bug. I think it happens here: https://github.com/wch/r-source/blob/6e61247f042985d5cb9f09034cb9e694a69082e0/src/library/graphics/src/plot.c#L944-L948 . In this context, gpptr(dd)->mgp[2] is the value of par("mgp")[3], and is a double. Here it's converted to an int, which loses the fractional part.
I don't really understand what the intention is in this calculation, so I'm not sure that simply changing lineoff to a double wouldn't cause problems elsewhere.
This has been reported to R's bugzilla, in PR#18194, i.e.,
https://bugs.r-project.org/show_bug.cgi?id=18194
and fixed in the R sources, svn 80947, now.
Related
I have the following code:
plot(x = 1, y = 1, xlim = c(1,2), ylim = c(1,2),
ylab = expression(bgroup("(",A[B]^{C},")")[~D[2]] / bgroup("(",E[F]^{G},")")[~H]))
Which leads to this plot:
Is it possible to make the division symbol ("/") taller so it properly divides two expression and doesn't look like it's shooting out of D2?
My attempt at manually drawing it with cex>1 lead to an ugly and fat division symbol. I don't want it fatter, just taller.
I am looking for solutions that use base plot methods.
If you don't mind using latex, you can export the plot using tickz. That will unleash the full power of latex formatting. E.g. with a standard sized division sign it looks like this:
library(devtools)
install_github('daqana/tikzDevice')
library(tikzDevice)
tikz('test.tex', width = 4, height = 3)
par(mar=c(3,6,3,3))
plot(x = 1, y = 1,
xlim = c(1,2), ylim = c(1,2),
ylab = '$(A_B^C)_{D_2} / (E_F^G)_{H}$')
dev.off()
or if you want an even bigger division sign, you can use one of the latex codes (in ascending order of size) \big/, \Big/, \bigg/, or \Bigg/:
tikz('test.tex', width = 4, height = 3)
par(mar=c(3,6,3,3))
plot(x = 1, y = 1,
xlim = c(1,2), ylim = c(1,2),
ylab = '$\\left(A_B^C\\right)_{D_2} \\bigg/ \\left(E_F^G\\right)_{H}$')
dev.off()
In the following example, the last x-axis label ("4.0") is omitted.
df <- data.frame(x = c(1, 2, 3.8), y = c(1, 2, 3))
#png(filename = "cutoff.png")
plot(df$x, df$y, xaxt = "n")
axis(side = 1, at = seq(0, 4, 0.5), labels = seq(0, 4, 0.5))
#dev.off()
How to prevent this behaviour?
You axis limit does not include 4; you need to overwrite the default limits of the plot (which it derives from the data) using xlim:
plot(df$x, df$y, xaxt = "n", xlim = c(1, 4))
Note that when using axis your specification of at will become your labels unless you overwrite that, so your script doesn't need to specify labels; your script can become:
axis(side = 1, at = seq(0, 4, 0.5))
As #griffinevo answered (+1), If you want the axis limits to go to 4, you must specify that using xlim. However, it is probably worth explaining how the default limits are computed. This is explained in the documentation, but in a slightly obscure place. On the help page ?par search for xaxs. There you will see
Style "r" (regular) first extends the data range by 4 percent at each
end and then finds an axis with pretty labels that fits within the
extended range.
In your case, the data ranges from 1 to 3.8. So plot will look for pretty labels inside the range
1 - 0.04*(3.8-1) = 0.888
to
3.8 + 0.04*(3.8-1) = 3.912
4 is outside of this range and so will not appear as an axis label. For completeness, it is worth noting that "pretty" sounds like just a word, but actually has a technical meaning here - related to the pretty function. If you look at the help page ?pretty You will see the description:
Compute a sequence of about n+1 equally spaced ‘round’ values which
cover the range of the values in x. The values are chosen so that they
are 1, 2 or 5 times a power of 10.
There is additional detail on the help page.
I have been unable to find a way to adjust the (vertical) distance between plot and main title in R using par. In this example:
plot(1, 1, main = "Title")
I can adjust the position of the axis titles using:
par(mgp = c(2.5, 1, 0))
But I see no way to similarly adjust the main title. I am aware that more manual control is possible using title or mtext, but I assume that there is a way setting the title distance using par as well, which would be more elegant for my purposes.
We can use title() function with negative line value to bring down the title.
See this example:
plot(1, 1)
title("Title", line = -2)
To summarize and explain visually how it works. Code construction is as follows:
par(mar = c(3,2,2,1))
barplot(...all parameters...)
title("Title text", adj = 0.5, line = 0)
explanation:
par(mar = c(low, left, top, right)) - margins of the graph area.
title("text" - title text
adj = from left (0) to right (1) with anything in between: 0.1, 0.2, etc...
line = positive values move title text up, negative - down)
Try this:
par(adj = 0)
plot(1, 1, main = "Title")
or equivalent:
plot(1, 1, main = "Title", adj = 0)
adj = 0 produces left-justified text, 0.5 (the default) centered text and 1 right-justified text. Any value in [0, 1] is allowed.
However, the issue is that this will also change the position of the label of the x-axis and y-axis.
I'm wondering how I can add an mtext() or a text() that can say: Note: CG[10] stands for central gravity? (of course, [10] is the subscript of CG.)
I'm trying the following mtext() but can't get the desired outcome:
plot(1:10, ty="n", ann=F)
mtext("Note:", bquote(bold(CG[10])), side = 1, line = 1, adj = 0, cex = 1.2, font = 2)
Well, a simple way is to use expression and * sign to separate the subscripted from ordinary (i.e., non-subscripted) text. So, in my case, this will be:
mtext(expression("Note: "*bold(CG[10])*" stand for Central Gravity"), side = 1, line = -5, adj = 0, cex = 1.2, font = 2)
And the desired outcome is:
I was wondering if I could shift the non-numeric values (e.g., "Trivial" see R code below) up on my axis on side 4 such that each non-numeric value be exactly positioned between the current tickmarks (I need to keep the current tickmarks) on this particular axis?
Note: you see I have placed " " for the highest non-numeric axis value, this is because I don't need it (may be there is way that " " could be deleted).
Here is the R code:
curve(dnorm(x),-3,3,bty="n")
axis(side=4,at = c(0,.1, .2, .3,.4),labels = c("Trivial", "Anecdotal", "Substantial","Strong", " "),las=1)
If you want them exactly between the tick marks, it may be easiest to put specify their positions exactly with a second call to axis to plot the labels.
curve(dnorm(x), -3, 3, bty = "n")
axis(side = 4, at = c(0, .1, .2, .3, .4),
labels = FALSE) # don't generate labels
axis(side = 4,
at = c(0.05, 0.15, 0.25, 0.35), # put the labels half-way between
labels = c("Trivial", "Anecdotal", "Substantial","Strong"),
tick = FALSE, # don't plot the tick marks
las = 1)