I am confused about the fact that specifying the mar
graphical parameter in the plot
function does not work. The following would give you two identical plots:
plot(1:10, 10:1, xlab = "x", ylab = "y", mar = c(10, 0, 0, 0))
plot(1:10, 10:1, xlab = "x", ylab = "y", mar = c(0, 0, 0, 0))
However, if I try to specify other graphical parameters, such as col.lab
in the example below, it works as expected. The below would give plots with different colours of the labels:
plot(1:10, 10:1, xlab = "x", ylab = "y", col.lab = "red")
plot(1:10, 10:1, xlab = "x", ylab = "y", col.lab = "black")
Confusingly, specifying the mar
graphical parameter outside of the function works fine, as in:
par.default <- par()
par(mar = c(10, 0, 0, 0))
plot(1:10, 10:1, xlab = "x", ylab = "y")
par(par.default)
Is there a way to fix this (in my eyes) unexpected behaviour?
There are three different types of graphical parameter used in R:
ptype 0
in the underlying C code).ptype 1
, meaning it can be set by the user via par
, but not inline via high-level functions).ptype 2
, which cannot be set by the user at all, but is fixed according to the graphics device).mar
is of the non-inline type. If you read the documentation for ?par
, then the Details section lists all of the other graphical parameters that cannot be set in a call to high-level plotting functions, i.e. all of the non-inline parameters :
Several parameters can only be set by a call to par():
- "ask",
- "fig", "fin",
- "lheight",
- "mai", "mar", "mex", "mfcol", "mfrow", "mfg",
- "new",
- "oma", "omd", "omi",
- "pin", "plt", "ps", "pty",
- "usr",
- "xlog", "ylog",
- "ylbias"
The remaining parameters can also be set as arguments (often via
...
) to high-level plot functions
These parameters are all handled by the C function Specify, whereas the ptype 0
parameters, which can be used in high level plotting functions, are handled by the C function Specify2.
It is clear that these assignations are not random, but a conscious design choice. I could easily tell a just-so story about why they were designed this way; for example, if I make a scatter plot then want to add lines, annotations, titles or legends to it, then I would have to use the same mar
argument in my calls to lines
, axis
, legend
etc to ensure the plot areas were matching, and this would feel like worse design than having to set mar
in advance. "Why can't I just set this once before I plot and be done with it!?"
But the actual reasons behind this design would need to come from the developers themselves (or perhaps even from the developers of the GRZ library, on which the par
system was based ).
As for how to get round this, there is little that can be done other than defining a new high-level plotting function that sets the mar
, plots, then resets the old mar
:
plot_with_mar <- function(mar = par('mar'), ...) {
parmar <- par('mar')
par(mar = mar)
plot(...)
par(mar = parmar, new = FALSE)
invisible(NULL)
}
This could be used as a drop in for plot
:
set.seed(1)
x <- 1:10
y <- rnorm(10)
plot_with_mar(x, y, mar = c(2, 2, 2, 2))
plot_with_mar(x, y, mar = c(8, 8, 8, 8))
If this feels a bit clunky, then consider that this is in effect what all the built-in high-level plotting functions in R do. Also, if you try to add lines or annotations after plotting with plot_with_mar
, you will quickly find out how tiresome it would be to try to do things this way round.
Created on 2022-11-17 with reprex v2.0.2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With