Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing 'mar' graphical parameter to plot function

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?

like image 642
dertomtom Avatar asked Oct 20 '25 02:10

dertomtom


1 Answers

There are three different types of graphical parameter used in R:

  • Normal (ptype 0 in the underlying C code).
  • Non-inline, (ptype 1, meaning it can be set by the user via par, but not inline via high-level functions).
  • Read-only. (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))

enter image description here

plot_with_mar(x, y, mar = c(8, 8, 8, 8))

enter image description here

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

like image 175
Allan Cameron Avatar answered Oct 22 '25 17:10

Allan Cameron