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.
source to share
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.
source to share