I want to create multiple plots in a single plotting window in which the width of the panels of each plot are proportional to xlim of each plot.
At the moment I use:
layout(matrix(c(1:8,10,9), 5, 2, byrow = FALSE), widths=2)
layout.show(10)
Basically, I would like width to be applied individually to each plot rather than to all the plots in a column. What is the best way of doing this?
layout only works if the plots can be arranged over a regular grid,
but they need not have the same widths.
layout(
matrix(
c(1,1,2,3,3,2,4,5,5,6,6,6),
nc=3, byrow = TRUE
)
)
layout.show(6)
If you want something really irregular, you can use par(fig=...,new=TRUE).
plot.new()
par(mar=c(2,2,1,1))
k <- 4
f <- function()
plot(rnorm(20),rnorm(20), xlab="", ylab="", main="", las=1)
for(i in 1:k) {
par(fig=c(0,i/(k+1), (i-1)/k, i/k), new=TRUE)
f()
par(fig=c(i/(k+1),1, (i-1)/k, i/k), new=TRUE)
f()
}
Related
The problem is that I managed to graph this:
by writing this:
par(mar=c(0,4,2,1)+.1)
shap <- shapiro.test(as.numeric(residuals.arima))$p.value
qqnorm(residuals.arima, main=c("Normal Q-Q Plot", paste("Shapiro p=", prettyNum(shap, digits = 2))))
qqline(residuals.arima)
op <- par(fig=c(.02,.5,.5,.98), new=TRUE)
hist(residuals.arima, breaks=22, probability=T,
col="grey", xlab="", ylab="", main="", axes=F)
lines(a,dnorm(a,mean(residuals.arima), sd(residuals.arima)), lty=1, col="darkblue", lwd=2)
box()
par(op)
Now, this is exactly the way I'd like the two plots to be visualized together. I do not want to split them up.
However I'd like to put everything in the right panel (2) of a structure like the following, so that I can add another plot on panel (1) without everything messing up:
layout(matrix(c(1,2), nr=1, byrow = TRUE))
How can I do this?
If I understand you correctly you want to simply do this:
par(mfrow=c(1,2))
qqplot(...)
qqline(...)
hist(...)
lines(...)
Alternative interpretation of what you are after is to put everything you have on that second side and leave the first side blank (to be used for something else). If that is the case you can use screen:
figs <- rbind(c(0, 0.5, 0, 1), # Screen1
c(0.5, 1, 0, 1), # Screen2
)
colnames(figs) <- c("W", "E", "S", "N")
rownames(figs) <- c("Screen1", "Screen2")
screenIDs <- split.screen(figs)
names(screenIDs) <- rownames(figs)
screen(screenIDs["Screen1"])
# Everything that should go on the left side goes here
screen(screenIDs["Screen2"])
# The current plots you have go here
I am trying to do a density plot of a dataset that has a wide range.
data=c(-10,-20,-20,-18,-17,1000,10000, 500, 500, 500, 500000)
plot(density(data))
As you can see in the figure, we can not see much
.
Is there a way to make an axis break (or several ones) on the x axis to visualise better the distribution of the data? Or, is there a way to plot a certain range of the data in several graphs and than paste it together?
Thanks a lot!
There is a function gap.plot() in package plotrix but I think it has some problems (see How to plot “multiple” curves with a break through y-data-range in R?). I recommend you draw two plots.
## use small margins and relatively big outer margins (to write labels).
old.par <- par(mfrow = c(1, 2), mar = rep(0.5, 4), oma = c(4, 4, 1, 1))
plot(density(data), xlim = c(-1000, 29000), main = "", bty="c") # diff 30000
abline(v = par("usr")[2], lty=2) # keep the same diff of xlim to avoid misleading
plot(density(data), xlim = c(471000, 501000), main = "", yaxt ="n", bty="]") # diff 30000
abline(v = par("usr"[1]), lty=2)
par(old.par)
I want to make a plot consisting of multiple plots consisting of multiple plots, say a 5x2 grid with three plots in each cell. To be more precise, what I need is not just one figure but finding a way of using my plotting function multiple times in a single plot.
I have written a function that uses layout to stack plots, with a common axis in outer margin. I actually need it for seqIplot and seqdplot functions from the TraMineR package, but as far as I understand the problem is not related to those, so here is a minimal working example with barplot.
stackedplot <- function(data){
layout(matrix(c(1:3), nrow=3))
par(mar=c(0,0,0,0), oma=c(4,1,1,1), mgp=c(3,0.5,0), cex=1)
barplot(data[[1]], axes=F, xlab="", ylab="", horiz=TRUE)
barplot(data[[2]], axes=F, xlab="", ylab="", horiz=TRUE)
barplot(data[[3]], axes=F, xlab="", ylab="", horiz=TRUE)
axis(1, at=c(0:10)/10, outer=TRUE)
mtext("Label", line=2, side=1)
}
stackedplot(list(1:10, 10:1, rep(1,10)))
What I would like to do is to then use something like layout again and use stackedplot for the grids of the layout, i.e. something like this (which, of course, does not work):
layout(matrix(c(1:2), nrow=1))
stackedplot(list(1:10, 10:1, rep(1,10)))
stackedplot(list(rep(1,10), 1:10, 10:1))
I have tried split.screen, with no success:
split.screen(c(1,2))
screen(1)
stackedplot(list(1:10, 10:1, rep(1,10)))
screen(2)
stackedplot(list(rep(1,10), 1:10, 10:1))
close.screen(all = TRUE)
I also tried grid package, but apparently it is not compatible with base graphics.
grid.newpage()
pushViewport(viewport(x=0, y=0, width=0.5, height=1,
default.units="native"))
print(stackedplot(list(1:10, 10:1, rep(1,10))), newpage=FALSE)
pushViewport(viewport(x=0.5, y=0, width=0.5, height=1,
default.units="native"))
print(stackedplot(list(rep(1,10), 1:10, 10:1)), newpage=FALSE)
After more research and some help I am now answering my own question in case it would be useful to someone else.
Nested layouts can be created with the grid package, which can be used for base graphics using the gridBase package. The function for the stacked plots is written as follows.
library(grid)
library(gridBase)
stackedplot <- function(data, main=""){
top.vp <- viewport(layout=grid.layout(nrow=5, ncol=1,
heights=unit(c(3, 1, 1, 1, 5),
c("lines", "null", "null", "null", "lines"))),
width=unit(0.9, "npc"))
title <- viewport(layout.pos.row=1, layout.pos.col=1, name="title")
p1 <- viewport(layout.pos.row=2, layout.pos.col=1, name="plot1")
p2 <- viewport(layout.pos.row=3, layout.pos.col=1, name="plot2")
p3 <- viewport(layout.pos.row=4, layout.pos.col=1, name="plot3")
xaxis <- viewport(layout.pos.row=5, layout.pos.col=1, name="xaxis")
splot <- vpTree(top.vp, vpList(title, p1, p2, p3, xaxis)) # Defining the hierarchy of the viewports
pushViewport(splot) # Creating viewports for plotting with the definitions of splot
upViewport() # Navigating up in the viewport tree
downViewport("plot1") # Navigating down in the viewport tree, searching for viewport "plot1"
grid.rect() # Plotting a rectangle (borders for the viewport)
par(plt=gridPLT(), new=TRUE) # Taking the dimensions of the viewport for a base graphics plot
# Adding plot to an existing plot
barplot(data[[1]], axes=FALSE, xlab="", ylab="", horiz=TRUE)
upViewport()
downViewport("plot2")
grid.rect()
par(plt=gridPLT(), new=TRUE)
barplot(data[[2]], axes=FALSE, xlab="", ylab="", horiz=TRUE)
upViewport()
downViewport("plot3")
grid.rect()
par(plt=gridPLT(), new=TRUE)
barplot(data[[3]], xlab="", ylab="", horiz=TRUE)
upViewport()
downViewport("xaxis")
grid.text("X label", y = unit(2, "lines"))
upViewport()
downViewport("title")
grid.text(main, y = unit(1, "lines"))
upViewport(2)
}
The function first describes a viewport (of 90% of the window's width) that is divided into a 5x1 grid of viewports with differing heights. Each viewport in the grid is given a name that can be later called. The tree of viewports (splot) is described with vpTree which defines the hierarchical structure of the viewports. After describing the viewports those are actually prepared for plotting with pushViewport.
Now each named viewport is first seeked and then opened for plotting with upViewport (which goes up in the viewport tree) and downViewport (which seeks for the requested viewport down in the viewport tree).
For plotting base graphics, gridPLT is needed here (alternatively gridFIG or gridOMI can be used, see the manual of gridBase for further info). After that any base graphics function can be used to plot into the current viewport.
After the requested plots, upViewport(2) is used to navigate back to the root (2 viewports up in the hierarchy).
Now the stackedplot function can be called multiple times in another grid as follows.
opar <- par(no.readonly=TRUE) # Saving graphical parameters
plot.new() # Needed for par(new=TRUE) in stackedplot()
multitop.vp <- viewport(layout=grid.layout(1,2), width = unit(0.95, "npc"))
pl1 <- viewport(layout.pos.col=1, layout.pos.row=1, name="A")
pl2 <- viewport(layout.pos.col=2, layout.pos.row=1, name="B")
vpall <- vpTree(multitop.vp, vpList(pl1,pl2))
pushViewport(vpall)
upViewport()
downViewport("A")
stackedplot(data=list(1:10,10:1,rep(10,10)),main="A")
upViewport()
downViewport("B")
stackedplot(data=list(10:1,rep(10,10),1:10),main="B")
upViewport(2)
par(opar) # Returning the graphical parameters saved earlier
I'm plotting multiple data series.
colos=c('red','green','purple','pink','brown')
par(new=F)
for (i in 1:5)
{
plot(dat[[i+1]],col=colos[i],cex=marksize,xlab='Reading #',ylab = 'Current')
par(new=T)
}
My plot looks like this:
Is there a way I can overwrite the plot axis with each iteration, but not overwrite the plotted points?
You may want to use the lines or points function(s) instead. Here's an example of how I usually go about this problem. This way you only overlay points on top of the existing plot, instead of plotting one plot on top of another.
Plot the first one with your original plot call, then use lapply to overlay the other columns' points on top of that.
set.seed(1)
dat <- data.frame(replicate(5, sample(10)))
colos <- c('red','green','purple','pink','brown')
plot(dat[[1]], col = colos[1], xlab = 'Reading #',
ylab = 'Current', ylim = range(as.matrix(dat)))
invisible(lapply(2:ncol(dat), function(i) points(dat[[i]], col = colos[i])))
Turn off the axes using xaxt and yaxt
E.g.:
plot(1:10)
par(new=TRUE)
plot(1:10, rnorm(10), xaxt="n", yaxt="n", xlab="", ylab="", type="l")
axis(side=4)
I'm generating numerous plots with xlim and ylim values that I'm calculating on a per-plot basis. I want to put my legend outside the plot area (just above the box around the actual plot), but I can't figure out how to get the maximum y-value of the box around my plot area.
Is there a method for even doing this? I can move the legend where I want it by manually changing the legend() x and y values, but this takes a LONG time for the amount of graphs I'm creating.
Thanks!
-JM
Here's a basic example illustrating what I think you're looking for using one of the code examples from ?legend.
#Construct some data and start the plot
x <- 0:64/64
y <- sin(3*pi*x)
plot(x, y, type="l", col="blue")
points(x, y, pch=21, bg="white")
#Grab the plotting region dimensions
rng <- par("usr")
#Call your legend with plot = FALSE to get its dimensions
lg <- legend(rng[1],rng[2], "sin(c x)", pch=21,
pt.bg="white", lty=1, col = "blue",plot = FALSE)
#Once you have the dimensions in lg, use them to adjust
# the legend position
#Note the use of xpd = NA to allow plotting outside plotting region
legend(rng[1],rng[4] + lg$rect$h, "sin(c x)", pch=21,
pt.bg="white", lty=1, col = "blue",plot = TRUE, xpd = NA)
The command par('usr') will return the coordinates of the bounding box, but you can also use the grconvertX and grconvertY functions. A simple example:
plot(1:10)
par(xpd=NA)
legend(par('usr')[1], par('usr')[4], yjust=0, legend='anything', pch=1)
legend( grconvertX(1, from='npc'), grconvertY(1, from='npc'), yjust=0,
xjust=1, legend='something', lty=1)
The oma, omd, and omi arguments of par() control boundaries and margins of plots - they can be queried using par()$omd (etc). and set (if needed) using par(oma=c()) (where the vector can have up to 4 values - see ?par)