R universal distance between plot limits and axis labels/title - r

In the R plotting environment, is there a way to specify a universal distance between the placement of the axis label and axis name relative the the boundary of the plot? For example, consider the Y-axis of the following test plot:
pdf('test1.pdf',height=3.5,width=5.0)
plot(1,ylab=NA,yaxt='n',ann=F)
mtext('Y-Axis Label',side=2,line=1.5)
axis(2, pos=0.595,tick=F)
dev.off()
I have optimized the placement of the axis label and axis name using the line=1.5 and pos=0.595 parameters within the mtext and axis functions, respectively. However, once I generate a separate plot with a different width (i.e. width=8.0), the placement no longer resembles the physical distance that I specified in the original graph using a width of 5.0. So the questions is - how do I specify these parameters so that the label and name will be exactly the same distance from the graph boundary regardless of the plot width. Could this be specified using a universal unit (i.e. pixels)?
Thanks in advance!

try grconvertX but you have to keep track of how you need the output. mtext(..., line = ) is in lines whereas axis(..., pos = ) is expecting user coordinates. Also you would need to use grconvertY where appropriate (ie, on sides 1 and 3)
pdf('test1.pdf',height=3.5,width=5.0)
plot(1,ylab=NA,yaxt='n',ann=F)
mtext('Y-Axis Label',side=2,line=1.5)
axis(2, pos=0.595,tick=F)
line <- grconvertX(1.5, 'lines', 'device')
pos <- grconvertX(0.595, 'user', 'device')
dev.off()
pdf('test1-again.pdf',height=3.5,width=5)
plot(1,ylab=NA,yaxt='n',ann=F)
mtext('Y-Axis Label',side=2, line = grconvertX(line, 'device', 'lines'))
axis(2, pos = grconvertX(pos, 'device', 'user'), tick = FALSE)
dev.off()
pdf('test1-wide.pdf',height=3.5,width=8)
plot(1,ylab=NA,yaxt='n',ann=F)
mtext('Y-Axis Label',side=2, line = grconvertX(line, 'device', 'lines'))
axis(2, pos = grconvertX(pos, 'device', 'user'), tick = FALSE)
dev.off()

Related

How do I add custom ranges and points in the axes of a Base R plot?

Let's consider a vector and plot it.
s1 <- sample(100:1000,32,replace = T)
plot(s1)
The plot I get has a Y-Axis that ranges from 0-1000 with points in the intervals of 200 (0,200,400,600,800,1000) and this is happening implicitly.
If I use ylim argument, apparently or to be honest, evidently, I can now have a custom range,
plot(s1,ylim = c(0,1500))
The points on Y Axis now are 0-1500 as indicated but with the points in the intervals of 500 (0,500,1000,1500), this is happening without my control.
My question, how can I have custom points with custom intervals on the X or Y axis?
use axis() to set your limits : on either x, y, or both
s1 <- sample(100:1000,32,replace = T)
plot(s1, yaxt = "n") # `yaxt` prevents y-axis labels to be printed
axis(2, yaxp=c(10, 1000, 10), las=2) # 'las' helps to align the tick mark labels along the axis or perpendicular
# 'yaxp' helps to set the break points you desire. Learn more from ?par

Set the scale the figure in R

I want to plot a figure by the ratio of xaxis to yaxis as 2 (x/y=2). Here, the ratio refers to the physical lenght of xaxis and yaxis. So the yaxis diplays as half of the xaxis.
As I know, the figure can be plotted as a square (i.e, x/y=1) by seting the parameter of "pty" as "s". But it is impossible to set the "pty" as a number.
Thanks for your attention!
You can use asp in the arguments to the plot function or you can save your generic plot using the png function, with a specific x and y as defined by the arguments width and height.
Following you comment, you can work with the xlim and ylim to ensure the physical dimensions of your plot along the axes. For example, to ensure that the ylim is twice the xlim you could do the following.
xlim=range(mpg$hwy)
ylim = range(mpg$cty) # y is 16
plot(data = mpg, cty~hwy, ylim = c(ylim[1]-16, ylim[2]+16))

Why is one number missing with barplot? [duplicate]

When I manually add the following labels with (axis(1, at=1:27, labels=labs[0:27])):
> labs[0:27]
[1] "0\n9.3%" "1\n7.6%" "2\n5.6%" "3\n5.1%" "4\n5.7%" "5\n6.5%" "6\n7.3%" "7\n7.6%" "8\n7.5%" "9\n7%" "10\n6.2%" "11\n5.2%"
[13] "12\n4.2%" ........
I get the following:
How do I force all labels to be drawn so 1,3,5,6, and 11 are not skipped? (also, for extra credit, how do I shift the whole thing down a few pixels?)
If you want to force all labels to display, even when they are very close or overlapping, you can "trick" R into displaying them by adding odd and even axis labels with separate calls to the axis function, as follows:
labs <-c("0\n9.3%","1\n7.6%","2\n5.6%","3\n5.1%","4\n5.7%","5\n6.5%","6\n7.3%",
"7\n7.6%","8\n7.5%","9\n7%", "10\n6.2%","11\n5.2%","12\n4.2%",13:27)
n=length(labs)
plot(1:28, xaxt = "n")
axis(side=1, at=seq(1,n,2), labels=labs[seq(1,n,2)], cex.axis=0.6)
axis(side=1, at=seq(2,n,2), labels=labs[seq(2,n,2)], cex.axis=0.6)
You can play with cex.axis to get the text size that you want. Note, also, that you may have to adjust the number of values in at= and/or labels= so that they are equal.
I agree with #PLapointe and #joran that it's generally better not to tamper with R's default behavior regarding overlap. However, I've had a few cases where axis labels looked fine even when they weren't quite a full "m-width" apart, and I hit on the trick of alternating odd and even labels as a way to get the behavior I wanted.
?axis tells you that:
The code tries hard not to draw overlapping tick labels, and so will omit labels where they would abut or overlap previously drawn labels. This can result in, for example, every other tick being labelled. (The ticks are drawn left to right or bottom to top, and space at least the size of an ‘m’ is left between labels.)
Play with cex.axis so that labels are small enough to fit without overlapping
labs <-c("0\n9.3%","1\n7.6%","2\n5.6%","3\n5.1%","4\n5.7%","5\n6.5%","6\n7.3%",
"7\n7.6%","8\n7.5%","9\n7%", "10\n6.2%","11\n5.2%","12\n4.2%",12:27)
plot(1:27,xaxt = "n")
axis(side=1, at=1:27, labels=labs[0:27],cex.axis=0.35)
If you widen you graph (manually by dragging or programmatically), you can increase the size of your labels.
Although there are some good answers here, the OP didn't want to resize the labels or change anything about the plot besides fitting all of the axis labels. It's annoying, since often there appears to be plenty of room to fit all of the axis labels.
Here's another solution. Draw the plot without the axis, then add ticks with empty labels. Store the positions of the ticks in an object, so then you can go through each one and place it in the correct position on the axis.
plot(1:10, 1:10, yaxt = "n")
axis_ticks = axis(2, axTicks(2), labels = rep("", length(axTicks(2))))
for(i in axis_ticks) axis(2, i)
#PLapointe just posted what I was going to say, but omitted the bonus answer.
Set padj = 0.5 in axis to move the labels down slightly.
Perhaps draw and label one tick at a time, by calling axis repeatedly using mapply...
For example, consider the following data:
x = runif(100)*20
y = 10^(runif(100)*3)
The formula for y might look a bit odd; it gives random numbers distributed across three orders of magnitude such that the data will be evenly distributed on a plot where the y axis is on a log scale. This will help demonstrate the utility of axTicks() by calculating nice tick locations for us on a logged axis.
By default:
plot(x, y, log = "y")
returns:
Notice that 100 and 1000 labels are missing.
We can instead use:
plot(x, y, log = "y", yaxt = "n")
mapply(axis, side = 2, at = axTicks(2), labels = axTicks(2))
which calls axis() once for each tick location returned by axTicks(), thus plotting one tick at a time. The result:
What I like about this solution is that is uses only one line of code for drawing the axis, it prints exactly the default axis R would have made, except all ticks are labeled, and the labels don't go anywhere when the plot is resized:
I can't say the axis is useful in the resized example, but it makes the point about axis labels being permanent!
For the first (default) plot, note that R will recalculate tick locations when resizing.
For the second (always labeled) plot, the number and location of tick marks are not recalculated when the image is resized. The axis ticks calculated by axTicks depend upon the size of the display window when the plot is first drawn.
If you want want to force specific tick locations, try something like:
plot(x, y, log = "y", yaxt = "n")
mapply(axis, side = 2, at = c(1,10,100, 1000), labels = c("one", "ten", "hundred", "thousand"))
which yields:
axis() includes a gap.axis parameter that controls when labels are omitted. Setting this to a very negative number will force all labels to display, even if they overlap.
The padj parameter of axis() controls the y offset whilst plotting an individual axis.
par(mgp = c(3, 2, 0) will adjust the position of all axis labels for the duration of a plotting session: the second value (here 2, default 1) controls the position of the labels.
# Set axis text position, including for Y axis
par(mgp = c(3, 2, 0))
# Plot
plot(1:12, 1:12, log = 'x', ann = FALSE, axes = FALSE)
# Some numbers not plotted:
axis(1, 1:12)
# All numbers plotted, with manual offset
axis(1, 1:12, gap.axis = -100, padj = 0.5)
I had a similar problem where I wanted to stagger the labels and get them to print without losing some. I created two sets of ticks showing second set below the other to make it look as if it staggers.
xaxis_stagger = function(positions,labels) {
odd=labels[seq(1,length(labels),2)]
odd_pos=positions[seq(1,length(positions),2)]
even=labels[seq(2,length(labels),2)]
even_pos=positions[seq(2,length(positions),2)]
axis(side=1,at=odd_pos,labels=odd)
axis(side=1,at=even_pos,labels=even,padj=1.5)
}
So you give the positions where you want the ticks to be and the labels for those ticks and this would then re-organise it into two sets of axis and plot them on the original plot. Original plot would be done with xaxt="n".

axis length (not scaling!) in a scatter plot

How to change the axis length? for ex:
s <- data.table(school=rep(1:3,5), wave=c(rep(1,7), rep(2,8)), v1=rpois(15,10))
plot(s$wave,s$v2)
I get a scatter plot where the data is at the edges of the plot (a lot of white space in the graph). changing the xaxp values doesn't help (tried xaxp=c(-1, +2,4)) but nothing happened) and when I try to define it a factor I get a box plot. I know I can "squeeze" it when i save to .png but is there any other way?
I tried to upload pictures to convey the problem but I don't have enough reputation.
edit-thanks for whoever uploaded it (although the axis are reversed - wave is the x and V2 is the y). the thing is that there is a lot of "free space" between the 1st and the 2nd wave. the position is perfect when i define the wave a factor (it's centered and each factor is half the axis length) but it keeps giving me a box plot!
You can add a lot of values to your plot function, like colour, title, and also the limits of the axsis
Your code:
s <- data.frame(school=rep(1:3,5), wave=c(rep(1,7), rep(2,8)), v1=rpois(15,10))
plot(s$wave,s$v2)
And now just add some more:
plot(
x = s$wave,
y = s$v2,
col = "red",
main = "This is my title",
xlab = "the label of the x-axis",
ylab = "the label of the y-axis",
xlim = c(-5, 5), # the limits of the x-axis,
ylim = c(-4, 10) # the limits of the y-axis
)
You can add much more like size and type of the points ...
just as jlhoward mentioned
i found a function in the "lattice" package that does exactly what i want - a boxplot without the box.
the function is called stripplot.
http://www.math.ucla.edu/~anderson/rw1001/library/base/html/stripplot.html
thank you all for the help

Controlling axis ticks and axis lines separately on R lattice xyplot

How can I go about removing the box around an xyplot, while keeping the axis scale tick marks? In the spirit of Edward Tufte's minimalist data graphic aesthetic, these axis lines are "non-data ink," and can (should?) be "erased."
library(lattice)
my.df <- data.frame(x=-10:10)
my.df$y <- my.df$x^2
xyplot(y~x,data=my.df)
It seems that the trellis display parameters (e.g. axis.line$col) control both the axis lines and axis ticks together:
xyplot(y~x,data=my.df,
par.settings=list(axis.line=list(col="transparent")))
...which is not the desired result, so it doesn't look like there's a simple way to turn off the lines while leaving the box.
The best I've been able to come up with is a brute-force hack, where I build the tick marks by hand using panel.segments:
at.x=pretty(c(-10,10))
at.y=pretty(c(0,100))
xyplot(y~x,data=my.df,
par.settings=list(axis.line=list(col="transparent")),
scales=list(x=list(at=at.x,labels=at.x),
y=list(at=at.y,labels=at.y)),
panel=function(...){
panel.xyplot(...)
panel.segments(x0=at.x,x1=at.x,y0=-4,y1=-2)
panel.segments(x0=-11.5,x1=-11,y0=at.y,y1=at.y)
}
)
This is close to the desired result, but there's quite a bit of fiddling required to get the tick marks to be a reasonable length and offset a "nice" distance from the data points. These values won't translate from one graphic to the next. Plus, note that the axis labels are now padded too far from the tick marks. I'm sure there's a way to reduce that padding, but that would only make the code even uglier and less portable.
So how can one go about suppressing just the lines that make up the "box" around the plot area, while leaving the tick marks and axis labels intact? Bonus points if this approach could also be used to suppress some, but not all of the lines (e.g. leave the left and lower lines, but suppress the top and right lines).
This is still a bit hacky, but at least you don't have to do any figuring by hand. It uses a combination of par.settings and a custom axis function that takes an argument line.col and temporarily changes the axis line color by a call to trellis.par.set:
EDIT (removed unnecessary changing of trellis settings)
xyplot(y~x,data=my.df, par.settings = list(axis.line = list(col = "transparent")),
# Pass custom axis function to argument 'axis'
axis = function(side, line.col = "black", ...) {
# Only draw axes on the left and bottom
if(side %in% c("left","bottom")) {
# Call default axis drawing function
axis.default(side = side, line.col = "black", ...)
}
}
)
At the moment, I chalk up why line.col = "black" is required in the arguments of the custom axis function to magic. My guess is that it has to do with argument matching with the ellipses (...). Perhaps I'll be wiser tomorrow and find the true reason.
This results in:
The easiest thing to do is to use the custom axis function (axis). Just set lwd (line width) to zero and tick marks (lwd.ticks) to something else. It worked like a charm!
plot(NA,NA,type="n",xaxt="n", lwd=linewidth, xlim=c(1,24), xlab="", ylab="",ylim=c(-300,500))
axis(side = 4, tck = .05, **lwd=0, lwd.ticks=1**, line = 0, labels = NA, col= cols_border[1], col.axis = cols_black)
axis(side = 4, lwd = 0, line = -4.5, las = 1, cex.axis=axis_fontsize, col= cols_border[1], col.axis = cols_black)
mtext("Light deviations (lum/sec)",side=4, padj=-2.5, cex=title_fontsize, col="black")

Resources