I'm trying to tile plots, specifically in a 2x3 matrix, for example:
x <- seq(1, 10, by=.01)
y <- 1/x
prms<-
list(xlab=rep(c("", "x"), each=3),
ylab=rep(c("y", "", ""), 2),
xaxt=rep(c("n", "s"), each=3),
yaxt=rep(c("s", "n", "n"), 2),
mar=list(c(0 , 4.1, 4.1, 0),
c(0 , 0, 4.1, 0),
c(0 , 0, 4.1, 1.1),
c(5.1, 4.1, 0, 0),
c(5.1, 0, 0, 0),
c(5.1, 0, 0, 1.1)))
par(mfrow=c(2, 3))
for (ii in 1:6){
par(mar=prms$mar[[ii]])
plot(x, y, type="l", lwd=2,
xlab=prms$xlab[ii], ylab=prms$lab[ii],
xaxt=prms$xaxt[ii], yaxt=prms$yaxt[ii])
}
Which produces:
I've suppressed the "inner" margins and axes because all x (resp., y) units are the same, so such axes would be redundant. However, as you can see, by squeezing the margins on the left- and rightmost plots, I've accidentally given the middle plots too much space (it's not as apparent, but the same trouble plagues the top vis-a-vis the bottom row).
Therein lies the conundrum. mfrow assigns equal space to each subplot (i.e., axes, margins, etc), not to each plot area. How can I change my approach so that each plotting area is of equal size, ceteris paribus (i.e., axes, etc. unchanged)? I thought of using layout, but couldn't think of a nice programmatic way to go about making sure everything has equal representation.
You may set mar to 0, and use oma to give the size of the outer margins. Axes are added to relevant plots in a loop. Common x and y axis labels are added with mtext and outer = TRUE
par(mfrow = c(2, 3),
mar = c(0, 0, 0, 0),
oma = c(4, 4, 0.5, 0.5))
for (i in 1:6) {
plot(x, y, type = "l", axes = FALSE)
if (i %in% c(4, 5, 6))
axis(side = 1)
if (i %in% c(1, 4))
axis(side = 2)
box()
}
mtext("x values", side = 1, outer = TRUE, line = 2.5)
mtext("y values", side = 2, outer = TRUE, line = 2.5)
See also How to create multiple plots, each with same plot area size, when only one plot has axis labels?
Related
I have a curious question, I am using par() to make a muli-panel barplot, I am noticing after using xlim and width to make bars across all barplot() the same, I am notice the main ="" not centering on the bars themselves, but the plot. I am also seeing that for some plots, the bars them selves are being cut off.
I was wondering 1) if it is possible to make main"" command in barplot() center over just the bars and 2) How to keep bars from being cut off, but be able to plot each of the plots using the par(mfow="") Any comments would be helpful.
I have this generic example:
# create data
a<-c(1:100)
b<-c(1:200)
c<-c(1:300)
d<-c(1:400)
e<-c(1:500)
f<- c(1:600)
#make dataframes for barplots
test<-as.data.frame(cbind(a,b))
test1<-as.data.frame(cbind(a,b,c))
test2<-as.data.frame(cbind(a,b,c,d))
test3<-as.data.frame(cbind(a,b,c,d,e))
test4<-as.data.frame(cbind(a,b,c,d,e,f))
#gets means for each column
a1<-colMeans(test)
a2<-colMeans(test1)
a3<-colMeans(test2)
a4<-colMeans(test3)
a5<-colMeans(test4)
#lets plot
pdf(file= "/Users/Highf_000/Desktop/prac1.pdf");
par(mfrow = c(2, 3), # 2 rows x 4 columns layout
oma = c(2, 2, 0, 0), # two rows of text at the outer left and bottom margin
mar = c(5, 5, 2, 1)+0.1, # space for one row of text at ticks and to separate plots
mgp = c(2, 1, 0), # axis label at 2 rows distance, tick labels at 1 row
xpd = NA)
barplot(mean(a), xlim = c(0, 1), width = 0.2, main = "Learning")
barplot(a1, xlim = c(0, 1), width = 0.2, main = "Learning")
barplot(a2, xlim = c(0, 1), width = 0.2, main = "Learning")
barplot(a3, xlim = c(0, 1), width = 0.2, main = "Learning")
barplot(a4, xlim = c(0, 1), width = 0.2, main = "Learning")
barplot(a5, xlim = c(0, 1), width = 0.2, main = "Learning")
dev.off()
This is my output:
We see the title isn't centered in the last right plot for sure, and the bottom right two plots are being cut off. How to plots to not cut the bars off and maintain the bar widths across all plots? Take into account my real data will have anywhere from 3 bar in a plot to 20 bars in a plot.
I am trying to make a single plot using dataset with same X axis but different Y axis. As an example, I have this dataset:
A1 <- rnorm(100)
B1 <- rnorm(100)
B2 <- rnorm(100)
B3 <- rnorm(100)
grid <- matrix(c(1:3),nrow=3,ncol=1,byrow=TRUE)
layout(grid)
plot(A1,B1)
plot(A1,B2)
plot(A1,B3)
This is what I get and comes with multiple X axis:
I know how to do it using ggplot2 but I am looking for another way like using layout. Any help would be much appreciated.
You can
Use the mfcol argument in par to set the number of plots, use mar to leave out the margins, oma to add space to axis which you will make with axis, and mgp to set the space for the axis label you will make.
Make the plots without axis with axes = FALSE.
Use box to add the boxes around the plots.
Add the axis labels in the end with mtext.
Here is an example
set.seed(32273438)
A1 <- rnorm(100)
B1 <- rnorm(100)
B2 <- rnorm(100)
B3 <- rnorm(100)
par(mfcol = c(3, 1), mar = numeric(4), oma = c(4, 4, .5, .5),
mgp = c(2, .6, 0))
plot(A1, B1, axes = FALSE)
axis(2L)
box()
plot(A1, B2, axes = FALSE)
axis(2L)
box()
plot(A1, B3, axes = FALSE)
axis(1L)
axis(2L)
box()
mtext("A1", side = 1, outer = TRUE, line = 2.2)
mtext("B", side = 2, outer = TRUE, line = 2.2)
You may have issues with overlapping y-ticks but you solve this with a the yaxp argument of par.
Its too easy by working with par(mar) and layout function.
par(mar=c(6,6,4,4))
layout(matrix(1:3, ncol = 1), widths = 1, heights = c(2.3,2,2.3), respect = FALSE)
par(mar = c(0, 4.1, 4.1, 2.1))
plot(B1,A1,xaxt='n')
par(mar = c(0, 4.1, 0, 2.1))
plot(B2,A1,xaxt='n')
par(mar = c(4.1, 4.1, 0, 2.1))
plot(B3,A1)
enter image description here
I have plotted five graphs and a legend. The graphs work just fine, however the legens disappears without an error.
My preview in RStudio looks like this
When I zoom in, the area where the legend should be is blank.
I use the following code:
opar <- par (no.readonly = TRUE)
par (mfrow = c(3, 2))
library(deSolve)
# Plot A
LotVmod <- function (Time, State, Pars) {
with(as.list(c(State, Pars)), {
dx = (b*x) - (b*x*x/K) - (y*(x^k/(x^k+C^k)*(l*x/(1+l*h*x))))
dy = (y*e*(x^k/(x^k+C^k)*(l*x/(1+l*h*x)))) - (m*y)
return(list(c(dx, dy)))
})
}
Pars <- c(b = 1.080, e = 2.200, K = 130.000, k = 20.000, l = 2.000,
h = 0.030, C = 2.900, m = 0.050)
State <- c(x = 0.25, y = 2.75)
Time <- seq(1, 9, by = 1)
out <- as.data.frame(ode(func = LotVmod, y = State, parms = Pars, times = Time))
matplot(out[,-1], type = "l", xlim = c(1, 9), ylim = c(0, 45),
xlab = "time",
ylab = "population",
main = "Compartment A")
mtext ( "Coefficient of Variance 4.96", cex = 0.8 )
x <- c(# Validation data)
y <- c(# Validation data)
lines (Time, x, type="l", lty=1, lwd=2.5, col="black")
lines (Time, y, type="l", lty=1, lwd=2.5, col="red")
# Legend
plot.new()
legend("center", c(expression (italic ("F. occidentalis")*" observed"),
expression (italic ("M. pygmaeus")*" observed"),
expression (italic ("F. occidentalis")*" simulated"),
expression (italic ("M. pygmaeus")*" simulated")),
lty = c(1, 1, 1, 2),
col = c(1, 2, 1, 2),
lwd = c(2.5, 2.5, 1, 1),
box.lwd = 0, bty = "n")
# Plot C to F = same as A
par(opar)
My output doesn't give an error. I have used the exact same code before without any trouble, thus I restarted R, removed all objects, cleared all plots and restarted both RStudio and my computer.
Try to add xpd=TRUE in your legend statement. I.e.
legend("center", c(expression (italic ("F. occidentalis")*" observed"),
expression (italic ("M. pygmaeus")*" observed"),
expression (italic ("F. occidentalis")*" simulated"),
expression (italic ("M. pygmaeus")*" simulated")),
lty = c(1, 1, 1, 2),
col = c(1, 2, 1, 2),
lwd = c(2.5, 2.5, 1, 1),
box.lwd = 0, bty = "n", xpd=TRUE)
By default, the legend is cut off by the plotting region. This xpd parameter enables plotting outside the plot region. See e.g. ?par for more on xpd.
This is due to how the plot canvas is set up and how rescaling that device works. The way you do it, you add the legend in the plotting region of the top right plot. The plotting region is however not the complete device, but only the part inside the space formed by the axes. If you rescale, that plotting region will be rescaled as well. The margins around the plotting region don't change size though, so zooming in makes your plotting region so small that it doesn't fit the legend any longer. It is hidden by the margins around the plotting region.
For that reason AEBilgrau is very right you need to add xpd = TRUE. This allows the legend to extend outside of the plotting region, so it doesn't disappear behind the margins when resizing the plotting device.
For the two column data available here, I am plotting two logarithmic y-axis histograms in one diagram in RStudio:
data = read.table("C:\\test\\test.csv", header=TRUE, sep=",")
par( mar=c(3.1, 5.1, 0, 0))
hist.x <- hist(data$a, plot = FALSE, breaks=50)
hist.x$counts <- log10(hist.x$counts + 1)
plot(hist.x, col = rgb(0, 0, 1, 0.99), main="", xlab="", ylab="", yaxt="n")
yAxesTitles=c(1, 10, 100, 1000, 10000)
axis(2, at=c(0, 1, 2, 3, 4),labels=yAxesTitles, col.axis="black", las=2)
mtext(side = 3, text = "a vs b", line = 0, cex=1.3)
mtext(side = 1, text = "Number", line = 2)
mtext(side = 2, text = "Frequency", line = 4)
# Adding the second diagram to the first one:
relocatedData=data$b+0.2
hist.y <- hist(relocatedData, plot = FALSE, breaks=50)
hist.y$counts <- log10(hist.y$counts + 1)
plot(hist.y, col = rgb(1, 0, 0, 0.99), main="", xlab="", ylab="", yaxt="n", add=TRUE)
legend(7.5, 4, c("a", "b"), lwd=c(1, 1), col=c(rgb(0, 0, 1, 0.99), rgb(1, 0, 0, 0.99)), pch = c(15, 15), pt.cex=2)
And this is what I have till now:
Now, there are two problems in this plot:
1- I want to add texture to the red bars (or two different patterns
for red and blue). I tried the regular texture solutions but couldn't successfully apply them in my specific diagram.
2- The space between the first bars (at 0) and the next bars (at 1) is less than other spaces (e.g., between 1 and 2, between 2 and 3 and so on). It seems that it automatically centered any two bars at each of the ticks except at 0 (which it puts both to the right side of zero).
Any solutions?
I would like to write a plot function for my specific purposes and put the y labels on the left margin. The length of these labels, however, can differ dramatically and depends on the model terms the user comes up with. For this reason, I would like to measure the width of the longest label and set the left margin width accordingly. I found the strwidth function, but I don't understand how to convert its output unit to the unit of the mar argument. An example:
label <- paste(letters, collapse = " ") # create a long label
par(mar = c(5, 17, 4, 2) + 0.1) # 17 is the left margin width
plot(1:2, axes = FALSE, type = "n") # stupid plot example
# if we now draw the axis label, 17 seems to be a good value:
axis(side = 2, at = 1, labels = label, las = 2, tck = 0, lty = 0)
# however, strwidth returns 0.59, which is much less...
lab.width <- strwidth(label) # so how can I convert the units?
You can use mai instead of mar to specify a distance in inches
(instead of "lines").
par(mai = c(1, strwidth(label, units="inches")+.25, .8, .2))
plot(1:2, axes=FALSE)
axis(side = 2, at = 1, labels = label, las = 2, tck = 0, lty = 0)
You can compute the conversion factor between lines and inches
by dividing mar by mai.
inches_to_lines <- ( par("mar") / par("mai") )[1] # 5
lab.width <- strwidth(label, units="inches") * inches_to_lines
par(mar = c(5, 1 + lab.width, 4, 2) + 0.1)
plot(1:2, axes=FALSE)
axis(side = 2, at = 1, labels = label, las = 2, tck = 0, lty = 0)