When using matplot() there always is a lot of empty space on the panel.
Normaly I cut it out with an external program, but it becomes a problem when arranging multiple graphs in one panel.
The option plot(x,y,...,,xaxs="i",yaxs="i") removes space around the x-axis (plot becomes wider), but not for the y-axis. I have also tried to readjust the layout matrix, giving more or less rows in the matrix to plots.
I first arrange my plots with layout() (works the same for par(mfrow=c(#,#))). Then I add the plots.
layout(rbind(
c(1,1,2,2),
c(1,1,2,2),
c(3,3,4,4),
c(3,3,4,4),
c(5,6,7,8),
c(9,10,11,12)
))
matplot(matrix(runif(100),nrow=10),type='l',xaxs="i",yaxs="i") # plot 1 upper left
matplot(matrix(runif(100),nrow=10),type='l',xaxs="i",yaxs="i") # plot 2 upper right
# fill the other panels
for(i in 3:12){
plot(1:10,runif(10),type='p',xaxs="i",yaxs="i")
}
When plotting this, a lot of the panel is wasted with empty space, while the plots are cropped. I want panel space to be filled with (nice) plots, making them as large as possible.
?par, entry mar is your friend.
with par(mar=c(bottom,left,top,right)) you can set the margins around your plot to move them closer to each other
Related
I am interested in labeling my boxplot with the letter A in the top left corner, but because I have a categorical X axis comparing seasons (summer vs winter), I am unable to give coordinates for my added text. How do you add text to a boxplot with a categorical axis?
This is what I've tried, which doesn't work:
`boxplot(LogTHg~Season, data = HgSIS, xlab= "Season", ylab= "LogTHg", text ("topleft", "A"))'
Three points:
As Ben writes, you can add a legend with x="topleft". The inset parameter allows you to separate it from the top and left boundaries.
If you call boxplot() with a formula object that has a factor-like right hand side, then R will put the first boxplot over the horizontal coordinate 1, the second over 2 and so forth. Which still doesn't tell you what the exact coordinates of the top left corner of the plotting region are, but you can at least do a couple of things. Like putting labels above each separate boxplot.
Relatedly, you can use the xlim parameter for boxplot() to control the horizontal spacing. For instance, if you use xlim=c(0,3), then you know that you can put something at horizontal coordinate 0. And the same with ylim.
I am trying to arrange 3 plots together. All 3 plots have the same y axis scale, but the third plot has a longer x axis than the other two. I would like to arrange the first two plots side by side in the first row and then place the third plot on the second row aligned to the right. Ideally I would like the third plot's x values to align with plot 2 for the full extent of plot 2 and then continue on below plot one. I have seen some other postings about using the layout function to reach this general configuration (Arrange plots in a layout which cannot be achieved by 'par(mfrow ='), but I haven't found anything on fine tuning the plots so that the scales match. Below is a crappy picture that should be able to get the general idea across.
I thought you could do this by using par("plt"), which returns the coordinates of the plot region as a fraction of the total figure region, to programmatically calculate how much horizontal space to allocate to the bottom plot. But even when using this method, manual adjustments are necessary. Here's what I've got for now.
First, set the plot margins to be a bit thinner than the default. Also, las=1 rotates the y-axis labels to be horizontal, and xaxs="i" (default is "r") sets automatic x-axis padding to zero. Instead, we'll set the amount of padding we want when we create the plots.
par(mar=c(3,3,0.5,0.5), las=1, xaxs="i")
Some fake data:
dat1=data.frame(x=seq(-5000,-2500,length=100), y=seq(-0.2,0.6,length=100))
dat2=data.frame(x=seq(-6000,-2500,length=100), y=seq(-0.2,0.6,length=100))
Create a layout matrix:
# Coordinates of plot region as a fraction of the total figure region
# Order c(x1, x2, y1, y2)
pdim = par("plt")
# Constant padding value for left and right ends of x-axis
pad = 0.04*diff(range(dat1$x))
# If total width of the two top plots is 2 units, then the width of the
# bottom right plot is:
p3w = diff(pdim[1:2]) * (diff(range(dat2$x)) + 2*pad)/(diff(range(dat1$x)) + 2*pad) +
2*(1-pdim[2]) + pdim[1]
# Create a layout matrix with 200 "slots"
n=200
# Adjustable parameter for fine tuning to get top and bottom plot lined up
nudge=2
# Number of slots needed for the bottom right plot
l = round(p3w/2 * n) - nudge
# Create layout matrix
layout(matrix(c(rep(1:2, each=0.5*n), rep(4:3,c(n - l, l))), nrow=2, byrow=TRUE))
Now create the graphs: The two calls to abline are just to show us whether the graphs' x-axes line up. If not, we'll change the nudge parameter and run the code again. Once we've got the layout we want, we can run all the code one final time without the calls to abline.
# Plot first two graphs
with(dat1, plot(x,y, xlim=range(dat1$x) + c(-pad,pad)))
with(dat1, plot(x,y, xlim=range(dat1$x) + c(-pad,pad)))
abline(v=-5000, xpd=TRUE, col="red")
# Lower right plot
plot(dat2, xaxt="n", xlim=range(dat2$x) + c(-pad,pad))
abline(v=-5000, xpd=TRUE, col="blue")
axis(1, at=seq(-6000,-2500,500))
Here's what we get with nudge=2. Note the plots are lined up, but this is also affected by the pixel size of the saved plot (for png files), and I adjusted the size to get the upper and lower plots exactly lined up.
I would have thought that casting all the quantities in ratios that are relative to the plot area (by using par("plt")) would have both ensured that the upper and lower plots lined up and that they would stay lined up regardless of the number of pixels in the final image. But I must be missing something about how base graphics work or perhaps I've messed up a calculation (or both). In any case, I hope this helps you get the plot layout you wanted.
I'm trying to plot a legend at the bottom of a main plot area comprised of multiple small plots (4 rows x 5 columns). I figured out how to center the legend at the bottom with the aid of this answer. I'm interested in having a horizontal legend, so I tried to use legend(x="center",lbls, horiz=TRUE), which would have served my purposes - except that my labels are too long and it goes off the plot area. I decided that a 2 row x 4 column layout will work best (I have 8 labels).
However, legend(x="center",lbls, ncol = 4) fills in the columns first (i.e. top to bottom), and I'd like to fill in by row first (i.e. left to right). Is there a workaround for this that doesn't involve using multiple legends?
I am very close to the heatmap I want, but I have been struggling for several days to figure out the headings problem. I want angled headings (such as 45 or 50 degrees) at the top of each column. I have already suppressed the dendrograms (for both columns and rows), and used the first column of my matrix (depth) as labels for the rows.
From my many, many searches on this issue, I see that mtext won't help me because the text cannot be rotated in mtext. Then I thought I could do it with text, but the column labels get overwritten onto the heatmap itself; they "disappear" (get covered) when the words reach the edge of the heatmap layout space. So I examined the layout used by heatmap (thanks very much to #Ian Sudbery), and it occurred to me that what I really need is a dedicated space in the layout for my column headings. I can allocate that space using the layout function, and I have done so in the code below. But I think the next step may involve getting inside the heatmap.2 code. Heatmap.2 calls four plots (two of which I have suppressed, the dendrograms). How do I make it call a fifth plot? And if that is possible, how do I tell it that I want to use text as my fifth "plot", where the text is my column headings, rotated 50deg?
Thanks very much for any ideas. Please forgive the clumsy way I've provided sample data in the code below; I am new to R and generally do not know the most elegant way to do most things.
par(oma=c(0.5,0.5,.5,0.5)) # outer margins of whole figure
lmat = rbind(c(3,5,5),c(2,1,1),c(0,4,0))
lwid = c(.08,.9, .1)
lhei = c(1,4,1.5)
Depth<-c("0m","20m","40m","60m","80m","100m")
Sept2008<-c(3,6,8,10,15,16)
March2010<-c(10,12,11,13,12,11)
Sept2010<-c(5,6,NA,8,11,13)
March2011<-c(4,6,10,NA,14,14)
Sept2011<-c(2,5,3,9,16,12)
heatmap_frame=data.frame(Depth=Depth,Sept2008=Sept2008,March2010=March2010,Sept2010=Sept2010, March2011=March2011, Sept2011=Sept2011)
row.names(heatmap_frame)<-heatmap_frame$Depth
heatmap_frame<-heatmap_frame[,-1]
heatmap_matrix <- as.matrix(heatmap_frame)
labCol=c("Sept 2008","March 2010","Sept 2010","March 2011","Sept 2011")
cexCol=1.1
heatmap <- heatmap.2(heatmap_matrix, dendrogram="none", trace="none",Rowv=NA, Colv=NA,
col = brewer.pal(9,"Blues"), scale="none", margins=c(2,5),labCol="",
lmat=lmat, lwid=lwid,lhei=lhei, density.info="none",key=TRUE)
# want to plot a fifth area, to use for col labels
# don't know how to pass a text line to the heatmap.2/layout/matrix to print as my fifth plot
mtext("Use for main title", side=3,outer=F,line=2.75, font=2, cex=1.25)
# testing the text function; did not work as desired
#text(x=1, y=1, labels="Label_1",xpd=T)
text(x=c(0,.2,.4,.6,.8), y=0.95, pos=3, srt=50, labels=labCol,xpd=T, cex=1)
Here's a hack that doesn't involve pulling apart the convoluted code of heatmap.2:
pos2 <- locator() #will return plotting coordinates after doing this:
# Shift focus to the graphics window by clicking on an edge
# Left-Click once where you want the first label to be centered
# Left-click again on the point where you want the last label centered
# Right-Click, then return focus to the console session window
pos2 <- structure(list(x = c(0.27149971320082, 0.858971646016485),
y = c(0.861365598392473, 0.857450478257082)),
.Names = c("x", "y"))
text(x=seq(pos2$x[1], pos2$x[2], len=5), y=rep(pos2$y[1],5) ,
srt=50, xpd=TRUE, adj = 0,
labels=c("Sept 2008","March 2010","Sept 2010",
"March 2011","Sept 2011") )
I don't know if you actually need the xpd in there, since it appears that after heatmap.2 is finished it returns the window to its native coordinates: [0,1]x[0,1]
I am trying to construct 3 scatter plots together using:
par(mfrow=c(3,1)),pty='s')
but they appear rather small on an A4 size paper. When I used:
par(mfrow=c(3,1)) only, the scatter plots were too stretched out (length wise). Is there anything that can be used to allow me to adjust the size so that they are large enough (3 plots) on an A4 size paper?
This is what I am currently using to plot the fist one:
(apirt$irt12[apirt$surv==1],
apirt$prtemp[apirt$surv==1],
xlab="ave.base of ears (°C)",
ylab="rectal (°C)",
xlim=c(26,42),
ylim=c(30,42),
col='blue')
Thanks,
Baz
I would suggest using par(mfrow=c(2,2), pty='s') instead. Then you will have a 2 by 2 layout of the graphs instead (with one empty quadrent), which will give more room for the square plotting regions. You should also make sure that your plotting device is set up to have the same dimensions as your paper. Reducing margins will also give you bigger plotting areas (just be sure to leave enough room for the annotations).
You should specify the size of the page, somewhere...
pdf(file="tmp.pdf",width=21/2,height=29.7/2)
op <- par(mfrow=c(3,1), pty="s") # or c(2,2)
plot(1:100,cumsum(rnorm(100)))
plot(1:100,cumsum(rnorm(100)))
plot(1:100,cumsum(rnorm(100)))
par(op)
dev.off()