Constant absolute line spacing in R-plots

Q R

, how do I tune the value height

of a plotter (like windows()

or png()

) so that the absolute distance between the points remains constant? Here's an example to illustrate what I mean:

n <- 10
windows(width=4, height=4)
par(xpd=NA)
plot(1:n)
abline(h=1:10)

n <- 20
windows(width=4, height=6)
par(xpd=NA)
plot(1:n)
abline(h=1:20)

      

I want the distance between the points / lines to be the same for each graph (I used par(xpd=NA)

, so it is easy to place the graphs next to each other and better see if the lines line up). It is clear what height

should be a function n

. Of course, one can try to find some function n

through trial and error that achieves this. But perhaps there is a clever way to achieve this programmatically.

My question is similar to this one: set ggplot plots to have the same x-axis width and the same space between points. But this question is using ggplot2

while my question is tied to the base graph.

+3


source to share


1 answer


there seem to be two things here.

  • how does R deal with axis segmentation?
  • which affect the margin on these rules?

First of all, we need to figure out what's going on with borderless:

adjLineSpace <- function(nLine, baseHeight=32){
  # get reproducible device
  png(paste("nLine=", nLine, ".png", sep=""), width=baseHeight*nLine, heigh=baseHeight*nLine, res=100)
  # control margins
  par(mar=c(0,0,0,0))
  # open plot with axis that do not interact with data to find "optimal range of axes"
  plot(0:nLine, pch=NA_integer_, xlim=c(0, nLine), ylim=c(0, nLine), yaxs="i", xaxs="i")
  abline(h=1:nLine)
  # include text for visualisation
  text(0:nLine, 0:nLine, labels=0:nLine)
  text(c(1:4), 0.5, par("usr")[1:4], pos=4)
  text(c(5), c(0.5), paste("pin: ", par("pin")[1], ", ", par("pin")[2], sep=""), pos=4)
  text(c(5), c(1.5), paste("fin: ", par("fin")[1], ", ", par("fin")[2], sep=""), pos=4)
  text(c(5), c(2.5), paste("din: ", par("din")[1], ", ", par("din")[2], sep=""), pos=4)
  dev.off()
}
# examples
adjLineSpace(10)
adjLineSpace(20)

      

After I found the problem, I felt like a bloody newbie, as I haven't seen it for so long: you have to start counting from 0, otherwise there won't be 10 or 20 or n "lines", but 9, 19 or n-1 ...




But since fields are required in almost any scenario, we also need to figure out how the fields interact with it:

# introduce new attributes to adjust margins, make it possible to deal with width and height seperately and be able to controll resolution
adjLineSpace <- function(nLine, basePHeight=32, basePWidth=32, marHeight=c(0, 0), marWidth=c(0, 0), res=100){
  pMai <- round(par("mai"),2); pMar <- round(par("mar"),2)
  pixelLeft <- marWidth[1]*pMai[2]/pMar[2]*res       # adjust left
  pixelRight <- marWidth[2]*pMai[4]/pMar[4]*res      # adjust right
  pixelBottom <- marHeight[1]*pMai[1]/pMar[1]*res    # adjust bottom
  pixelTop <- marHeight[2]*pMai[3]/pMar[3]*res       # adjust top
  # reproducible device: we first need to find out how many inches one row covers, multiply that by our resolution and the number of 'lines' we want to use in our margin
  png(
    paste("nLine=", nLine, ".png", sep=""), 
    width=basePWidth*nLine + pixelLeft + pixelRight, 
    heigh=basePHeight*nLine + pixelBottom + pixelTop, 
    res=res
  )
  # set margins
  par(mar=c(marHeight[1], marWidth[1], marHeight[2], marWidth[2]))
  # this is the key to it all! Set range of plot area (in pixels) as above, only transformed to inches (divided by resolution in ppi=points/pixels per inch)
  par(pin=c(basePWidth*nLine/res, basePHeight*nLine/res))
  plot(0:nLine, pch=NA_integer_, xlim=c(0, nLine), ylim=c(0, nLine), yaxs="i", xaxs="i", bg="white")
  abline(h=1:nLine)
  # include text for visualisation of values
  text(0:nLine, 0:nLine, labels=0:nLine)
  text(c(1:4), 0.5, par("usr")[1:4], pos=4)
  text(c(5), c(0.5), paste("din: ", par("din")[1], ", ", par("din")[2], sep=""), pos=4)
  text(c(5), c(1.5), paste("fin: ", par("fin")[1], ", ", par("fin")[2], sep=""), pos=4)
  text(c(5), c(2.5), paste("pin: ", par("pin")[1], ", ", par("pin")[2], sep=""), pos=4)
  text(c(.5), c(9.5), paste("mar: ", pMar[1], ", ", pMar[2], ", ", pMar[3], ", ", pMar[4], sep=""), pos=4)
  text(c(.5), c(8.5), paste("mai: ", pMai[1], ", ", pMai[2], ", ", pMai[3], ", ", pMai[4], sep=""), pos=4)
  text(c(.5), c(7.5), paste("mai(nch)/mar(ow): ", round(pMai[1]/pMar[1],2), ", ", round(pMai[2]/pMar[2],2), ", ", round(pMai[3]/pMar[3],2), ", ", round(pMai[4]/pMar[4],2), sep=""), pos=4)
  dev.off()
}
# examples without margins still work
adjLineSpace(10, basePHeight=32, basePWidth=32, marHeight=c(0, 0), marWidth=c(0, 0))
adjLineSpace(20, 32, 32, c(0, 0), c(0, 0))
# with margins as well!
adjLineSpace(10, 32, 32, c(4, 0), c(4, 0))
adjLineSpace(20, 32, 32, c(4, 0), c(4, 0))

      

See https://www.stat.auckland.ac.nz/~paul/RGraphics/rgraphics.html (Paul Murrell) for an explanation of device regions. Unfortunately I am unable to post the images, otherwise I will also provide some examples.

Actually I came from this post too: Align text to plot with variable size in R and I would be interested to see this in the forest () - function metaphor. I will send you the Prime Minister, Wolfgang.

+2


source







All Articles