Related
I am trying to plot 18 individual plots on a 3x6 multiplot in R. To be more efficient I have created these plots as a loop, however I would like the plots in each column to have their own color (i.e. the all the plots in column 1 would be red, all the plots in column 2 would be blue etc.). Is there a way I can do this while still retaining loop format?
par(mfcol = c(3,6))
for(i in 1:6)
{
plot(sigma_trace[,i], type ='l', main = paste("Sigma Traceplot Chain", i))
plot(theta_1_trace[,i], type = 'l', main = paste("Theta[1] Traceplot Chain", i))
plot(theta_2_trace[,i], type = 'l', main = paste("Theta[2] Traceplot Chain", i))
}
So basically, I think I want each loop statement to follow the same pattern of colours. Is this possible?
Thanks.
You can make a colour palette using RColorBrewer and then call each colour in your loop. For example.
library(RColorBrewer)
# set the colour palette
cols <- brewer.pal(4,'Set2')
# variables to plot
x = (1:250)/10
y = cos(x)
# plot in the loop
op <- par(mfrow = c(2, 2))
for (i in 1:4){
plot(x, y, col=cols[i], type='l', lwd=3)
}
par(op)
Here's an overview of the package.
In Base R you can use colorRampPalette() to create gradient, or you can even just make an object with the colours that you wan to reference:
plotcolors <- colorRampPalette(c("gold","blue"))(6)
par(mfrow = c(2, 3))
for(i in 1:6){
plot(1:10,1:10,type='l',col=plotcolors[i])
}
If you want to specify all 6 of your colours its as easy as modifying the above code
plotcolors <- c("red","blue","green","black","yellow","purple")
When using matplot to plot a matrix using:
matplot(t, X[,1:4], col=1:4, lty = 1, xlab="Time", ylab="Stock Value")
my graph comes out as:
How do I reduce the line thickness? I previously used a different method and my graph was fine:
I have tried manupilating lwd but to no avail.
Even tried plot(t, X[1:4097,1]), yet the line being printed is very thick. Something wrong with my R?
EDIT: Here is the code I used to produce the matrix X:
####Inputs mean return, volatility, time period and time step
mu=0.25; sigma=2; T=1; n=2^(12); X0=5;
#############Generating trajectories for stocks
##NOTE: Seed is fixed. Changing seed will produce
##different trajectories
dt=T/n
t=seq(0,T,by=dt)
set.seed(201)
X <- matrix(nrow = n+1, ncol = 4)
for(i in 1:4){
X[,i] <- c(X0,mu*dt+sigma*sqrt(dt)*rnorm(n,mean=0,sd=1))
X[,i] <- cumsum(X[,i])
}
colnames(X) <- paste0("Stock", seq_len(ncol(X)))
Just needed to add type = "l" to matplot(....). Plots fine now.
matplot(t, X[,1:4], col=1:4, type = "l", xlab="Time", ylab="Stock Value")
If I plot a data and use lines to superimpose the same data points on the graph, I get the same data points. Lets say
x<-rnorm(100)
plot(x, type="p")
lines(x, type="p",pch=2)
However, I have realized that there is a distortion in R plots when the same is done in a multipanel graph. It seems R is unable to recall the exact values on the y-axis when you plot the same data again. A simple code below shows the outputs from "plot" and "lines" are not the same.
set.seed(1000)
Range<-rbind(rep(0,4),c(100,100,1,100));thres<-70
Ylab<-c("MAD","Bias","CP","CIL")
X<-list(EVI=cbind(runif(10,0,100),runif(10,0,100),
runif(10,0,1),runif(10,0,100)),
Qp=cbind(runif(10,0,100),runif(10,0,100),runif(10,0,1),runif(10,0,100)))
Plot<-function(x,Pch=1,thres)
{
par(mfrow=c(1,4),las=2)
for(j in 1:4)
{
plot(x[,j],xaxt = "n",xlab="Estimator",
ylab=Ylab[j],type = "p", pch = Pch, ylim=Range[,j])
par(mfg=c(1,j))
axis(1, at=1:nrow(x), labels=LETTERS[1:nrow(x)])
if(j!=3){
par(mfg=c(1,j))
abline(h=thres,col=2)
}else{
par(mfg=c(1,j))
abline(h=c(0.90,0.95,0.99),lty=c(2,1,2),col=rep(2,3))
}
}
}
Line<-function(x,Pch)
{
for(j in 1:ncol(x)) {
par(mfg=c(1,j))
lines(x[,j], type = "p", pch = Pch,col=2)
}
}
lapply(X,function(dat)Plot(dat,thres=thres))
## First panel
Line(X$EVI,Pch=2)
## Move to second panel
Line(X$Qp,Pch=2)
What explains the distortions in the positioning of the points in the 3rd column? Note that, I have included the range of each data courtesy #WhiteViking in the "Plot" function. However, the distortion keeps showing. Thank you
The problem is in the ordering of 'plot' and 'lines'.
Code like this, with all 3 'plot' commands upfront:
set.seed(1)
X <- cbind(rnorm(20), 2 * rnorm(20), 3 * rnorm(20))
par(mfrow = c(1,3))
for (i in 1:3) {
plot(X[,i])
}
for (i in 1:3) {
par(mfg = c(1,i))
lines(X[,i], type = "p", col = 2, pch = 3)
}
yields misaligment:
In the example above the first 'lines' command that get executed bases its scaling on the last 'plot' that happened. Since that had a larger vertical range than the first, the scaling of the 'lines' is incorrect.
Whereas structured like so:
set.seed(1)
X <- cbind(rnorm(20), 2 * rnorm(20), 3 * rnorm(20))
par(mfrow = c(1,3))
for (i in 1:3) {
par(mfg = c(1,i))
plot(X[,i])
lines(X[,i], type = "p", col = 2, pch = 3)
}
it gives correct alignment of 'plot' and 'lines':
You'll probably have to rework your code to group 'plot' and 'lines' together for each sub-plot.
When the third column is converted to percentages, the ylim becomes uniform and hence there isn't such distortion. However, it will be good to get a way around it instead of such adhoc transformation
plot() sets up a coordinate system via plot.window based on the range of the data. This information is apparently stored in par(usr) for the latest plot, which means that if you want to revisit older plots, you should store those usr values and reset them accordingly,
set.seed(123)
d1 <- data.frame(x=1:10, y=rnorm(10))
d2 <- data.frame(x=1:10, y=10*rnorm(10))
par(mfrow=c(1,2),mar=c(2.5,2.5,0,0))
plot(d1, type="p")
usr1 <- par("usr")
plot(d2, type="p")
usr2 <- par("usr")
par(mfg=c(1,1), usr=usr1)
points(d1, col="red", pch=3)
par(mfg=c(1,2), usr=usr2)
points(d2, col="red", pch=3)
I need to use black and white color for my boxplots in R. I would like to colorfill the boxplot with lines and dots. For an example:
I imagine ggplot2 could do that but I can't find any way to do it.
Thank you in advance for your help!
I thought this was a great question and pondered if it was possible to do this in base R and to obtain the checkered look. So I put together some code that relies on boxplot.stats and polygon (which can draw angled lines). Here's the solution, which is really not ready for primetime, but is a solution that could be tinkered with to make more general.
boxpattern <-
function(y, xcenter, boxwidth, angle=NULL, angle.density=10, ...) {
# draw an individual box
bstats <- boxplot.stats(y)
bxmin <- bstats$stats[1]
bxq2 <- bstats$stats[2]
bxmedian <- bstats$stats[3]
bxq4 <- bstats$stats[4]
bxmax <- bstats$stats[5]
bleft <- xcenter-(boxwidth/2)
bright <- xcenter+(boxwidth/2)
# boxplot
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[1], density=angle.density)
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[2], density=angle.density)
# lines
segments(bleft,bxmedian,bright,bxmedian,lwd=3) # median
segments(bleft,bxmin,bright,bxmin,lwd=1) # min
segments(xcenter,bxmin,xcenter,bxq2,lwd=1)
segments(bleft,bxmax,bright,bxmax,lwd=1) # max
segments(xcenter,bxq4,xcenter,bxmax,lwd=1)
# outliers
if(length(bstats$out)>0){
for(i in 1:length(bstats$out))
points(xcenter,bstats$out[i])
}
}
drawboxplots <- function(y, x, boxwidth=1, angle=NULL, ...){
# figure out all the boxes and start the plot
groups <- split(y,as.factor(x))
len <- length(groups)
bxylim <- c((min(y)-0.04*abs(min(y))),(max(y)+0.04*max(y)))
xcenters <- seq(1,max(2,(len*(1.4))),length.out=len)
if(is.null(angle)){
angle <- seq(-90,75,length.out=len)
angle <- lapply(angle,function(x) c(x,x))
}
else if(!length(angle)==len)
stop("angle must be a vector or list of two-element vectors")
else if(!is.list(angle))
angle <- lapply(angle,function(x) c(x,x))
# draw plot area
plot(0, xlim=c(.97*(min(xcenters)-1), 1.04*(max(xcenters)+1)),
ylim=bxylim,
xlab="", xaxt="n",
ylab=names(y),
col="white", las=1)
axis(1, at=xcenters, labels=names(groups))
# draw boxplots
plots <- mapply(boxpattern, y=groups, xcenter=xcenters,
boxwidth=boxwidth, angle=angle, ...)
}
Some examples in action:
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2)),
x=sort(rep(1:2,200)))
drawboxplots(mydat$y, mydat$x)
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2),
rnorm(200,3,3),rnorm(400,-2,8)),
x=sort(rep(1:5,200)))
drawboxplots(mydat$y, mydat$x)
drawboxplots(mydat$y, mydat$x, boxwidth=.5, angle.density=30)
drawboxplots(mydat$y, mydat$x, # specify list of two-element angle parameters
angle=list(c(0,0),c(90,90),c(45,45),c(45,-45),c(0,90)))
EDIT: I wanted to add that one could also obtain dots as a fill by basically drawing a pattern of dots, then covering them a "donut"-shaped polygon, like so:
x <- rep(1:10,10)
y <- sort(x)
plot(y~x, xlim=c(0,11), ylim=c(0,11), pch=20)
outerbox.x <- c(2.5,0.5,10.5,10.5,0.5,0.5,2.5,7.5,7.5,2.5)
outerbox.y <- c(2.5,0.5,0.5,10.5,10.5,0.5,2.5,2.5,7.5,7.5)
polygon(outerbox.x,outerbox.y, col="white", border="white") # donut
polygon(c(2.5,2.5,7.5,7.5,2.5),c(2.5,2.5,2.5,7.5,7.5)) # inner box
But mixing that with angled lines in a single plotting function would be a bit difficult, and is generally a bit more challenging, but it starts to get you there.
I think it is hard to do this with ggplot2 since it dont use shading polygon(gris limitatipn). But you can use shading line feature in base plot, paramtered by density and angle arguments in some plot functions ( ploygon, barplot,..).
The problem that boxplot don't use this feature. So I hack it , or rather I hack bxp internally used by boxplot. The hack consist in adding 2 arguments (angle and density) to bxp function and add them internally in the call of xypolygon function ( This occurs in 2 lines).
my.bxp <- function (all.bxp.argument,angle,density, ...) {
.....#### bxp code
xypolygon(xx, yy, lty = boxlty[i], lwd = boxlwd[i],
border = boxcol[i],angle[i],density[i])
.......## bxp code after
xypolygon(xx, yy, lty = "blank", col = boxfill[i],angle[i],density[i])
......
}
Here an example. It should be noted that it is entirely the responsibility of the user to ensure
that the legend corresponds to the plot. So I add some code to rearrange the legend an the boxplot code.
require(stats)
set.seed(753)
(bx.p <- boxplot(split(rt(100, 4), gl(5, 20))))
layout(matrix(c(1,2),nrow=1),
width=c(4,1))
angles=c(60,30,40,50,60)
densities=c(50,30,40,50,30)
par(mar=c(5,4,4,0)) #Get rid of the margin on the right side
my.bxp(bx.p,angle=angles,density=densities)
par(mar=c(5,0,4,2)) #No margin on the left side
plot(c(0,1),type="n", axes=F, xlab="", ylab="")
legend("top", paste("region", 1:5),
angle=angles,density=densities)
I am looking to label data points with indices -- to identify the index number easily by visual examination.
So for instance,
x<-ts.plot(rnorm(10,0,1)) # would like to visually identify the data point indices easily through arrow labelling
Of course, if there's a better way of achieving this, please suggest
You can use arrows function:
set.seed(1); ts.plot(x <-rnorm(10,0,1), ylim=c(-1.6,1.6)) # some random data
arrows(x0=1:length(x), y0=0, y1=x, code=2, col=2, length=.1) # adding arrows
text(x=1:10, y=x+.1, 0, labels=round(x,2), cex=0.65) # adding text
abline(h=0) # adding a horizontal line at y=0
Use my.symbols from package TeachingDemos to get arrows pointing to the locations you want:
require(TeachingDemos)
d <- rnorm(10,0,1)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
my.symbols(x=1:10, y=d, ms.arrows, angle=pi/2, add=T, symb.plots=TRUE, adj=1.5)
You can use text() for this
n <- 10
d <- rnorm(n)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
text(1:n, d+par("cxy")[2]/2,col=2) # Upside
text(1:n, d-par("cxy")[2]/2,col=3) # Downside
Here a lattice version, to see the analogous of some base function.
set.seed(1234)
dat = data.frame(x=1:10, y = rnorm(10,0,1))
xyplot(y~x,data=dat, type =c('l','p'),
panel = function(x,y,...){
panel.fill(col=rgb(1,1,0,0.5))
panel.xyplot(x,y,...)
panel.arrows(x, y0=0,x1=x, y1=y, code=2, col=2, length=.1)
panel.text(x,y,label=round(y,2),adj=1.2,cex=1.5)
panel.abline(a=0)
})