I am trying to generate a figure that consists of a box plot with a set of points overlaid on the boxplot. The key issue is that the y scale of the box plot is different from that of the points. (Yes, this is very poor visualization - but I'm not the lead author of the paper). I have been able to generate a plot with different y scales, but am facing an issue with the x axis.
Using the following code
boxdata <- data.frame(fc=runif(100, min=-4, max=4),
sym=sample(c('A', 'B', 'C', 'D', 'E'), 100, replace=TRUE))
par(mar=c(5, 4, 1, 6) + 0.1)
junk <- boxplot(fc ~ sym, boxdata, las=2, pch=19, ylim=c(-5,5),
varwidth=FALSE, xaxt='n')
mtext("Y-axis",side=2,line=2.5)
axis(1, at=1:5, labels=sort(unique(boxdata$sym)), las=2)
par(new=TRUE)
x <- 1:5
y <- runif(5, min=-1, max=1)
plot(x,y, col='red', type='p', pch=15, axes=FALSE, ylim=c(-1,1), cex=1.5)
axis(4, ylim=c(-1,1), las=1)
I get the following figure. As you can see the points in red do not align with the X-axis labels (or box centers). The box centers are located at 1:5, so I thought that the plot() call with x = 1:5 should line up.
Could anybody point me to a way to line up the second set of points with the box centers?
EDIT: This problem doesn't occur if I plot two sets of points on different y scales
plot(1:10, runif(10) , col='red', pch=19)
par(new=TRUE)
plot(1:10, runif(10, min=5, max=20), col='blue', pch=19, axes=FALSE)
axis(4, las=2)
Don't use par(new=TRUE), but use pointsinstead of the second plotcommand:
boxdata <- data.frame(fc=runif(100, min=-4, max=4),
sym=sample(c('A', 'B', 'C', 'D', 'E'), 100, replace=TRUE))
par(mar=c(5, 4, 1, 6) + 0.1)
junk <- boxplot(fc ~ sym, boxdata, las=2, pch=19, ylim=c(-5,5),
varwidth=FALSE, xaxt='n')
mtext("Y-axis",side=2,line=2.5)
axis(1, at=1:5, labels=sort(unique(boxdata$sym)), las=2)
x <- 1:5
y <- runif(5, min=-1, max=1)
points(x, 4*y, col='red', type='p', pch=15, ylim=c(-1,1), cex=1.5)
axis(4, at=seq(-4, 4, by=2), label=seq(-1, 1, by=.5), las=1)
EDIT: Check the ?bxp help page. You will find a note that xlim defaults to range(at, *) + c(-0.5, 0.5). So, you could specify the same for your second plot:
junk <- boxplot(fc ~ sym, boxdata, las=2, pch=19, ylim=c(-5,5),
varwidth=FALSE, xaxt='n')
mtext("Y-axis",side=2,line=2.5)
axis(1, at=1:5, labels=sort(unique(boxdata$sym)), las=2)
par(new=TRUE)
plot(x,y, col='red', type='p', pch=15, axes=FALSE, ylim=c(-1,1), cex=1.5,
xlim=range(x) + c(-0.5, 0.5))
axis(4, ylim=c(-1,1), las=1)
Related
I have this line-and-dots plot:
#generate fake data
xLab <- seq(0, 50, by=5);
yLab <- c(0, sort(runif(10, 0, 1)));
#this value is fixed
fixedVal <- 27.3
#new window
dev.new();
#generate the plot
paste0(plot(xLab, yLab, col=rgb(50/255, 205/255, 50/255, 1), type="o", lwd=3,
main="a line-and-dots plot", xlab="some values", ylab="a percentage",
pch=20, xlim=c(0, 50), ylim=c(0, 1), xaxt="n", cex.lab=1.5, cex.axis=1.5,
cex.main=1.5, cex.sub=1.5));
#set axis
axis(side = 1, at=c(seq(min(xLab), max(xLab), by=5)))
#plot line
abline(v=fixedVal, col="firebrick", lwd=3, lty=1);
now, I would like to find the y coordinate of the intersection point between the green and the red lines.
Can I achieve the goal without the need of a regression line? Is there a simple way of getting the coordinates of that unknown point?
You can use approxfun to do the interpolation:
> approxfun(xLab,yLab)(fixedVal)
[1] 0.3924427
Alternatively, just use approx:
> approx(xLab,yLab,fixedVal)
$x
[1] 27.3
$y
[1] 0.3924427
Quick fix like #JohnColeman said:
# find the two points flanking your value
idx <- findInterval(fixedVal,xLab)
# calculate the deltas
y_delta <- diff(yLab[idx:(idx+1)])
x_delta <- diff(xLab[idx:(idx+1)])
# interpolate...
ycut = (y_delta/x_delta) * (fixedVal-xLab[idx]) + yLab[idx]
ycut
[1] 0.4046399
So we try it on the plot..
paste0(plot(xLab, yLab, col=rgb(50/255, 205/255, 50/255, 1), type="o", lwd=3,
main="a line-and-dots plot", xlab="some values", ylab="a percentage",
pch=20, xlim=c(0, 50), ylim=c(0, 1), xaxt="n", cex.lab=1.5, cex.axis=1.5,
cex.main=1.5, cex.sub=1.5));
#set axis
axis(side = 1, at=c(seq(min(xLab), max(xLab), by=5)))
#plot line
abline(v=fixedVal, col="firebrick", lwd=3, lty=1);
abline(h=ycut, col="lightblue", lwd=3, lty=1);
I have the following data and code in R:
x <- runif(1000, -9.99, 9.99)
mx <- mean(x)
stdevs_3 <- mx + c(-3, +3) * sd(x/5) # Statndard Deviation 3-sigma
And I plotted as line (alongwith 3 standard deviation and mean lines) in R:
plot(x, t="l", main="Plot of Data", ylab="X", xlab="")
abline(h=mx, col="red", lwd=2)
abline(h=stdevs_3, lwd=2, col="blue")
What I want to do:
Anywhere on the plot, whenever line is crossing 3 sigma thresholds (blue lines), above or below it, line should be in different color than black.
I tried this, but did not work:
plot(x, type="l", col= ifelse(x < stdevs_3[[1]],"red", "black"))
abline(h=mx, col="red", lwd=2)
abline(h=stdevs_3, lwd=2, col="blue")
Is there any other way?
This is what is requested, but it appears meaningless to me because of the arbitrary division of x by 5:
png( )
plot(NA, xlim=c(0,length(x)), ylim=range(x), main="Plot of Data", ylab="X", xlab="", )
stdevs_3 <- mx + c(-3, +3) * sd(x/5)
abline(h=mx, col="red", lwd=2)
abline(h=stdevs_3, lwd=2, col="blue")
segments( 0:999, head(x,-1), 1:1000, tail(x,-1) , col=c("black", "red")[
1+(abs(tail(x,-1)) > mx+3*sd(x/5))] )
dev.off()
I am using the following couple of lines to produce the below plot from rows of two 4X10 Matrix d1, and d2 in one graph:
plot(as.matrix(d1[2,]), as.matrix(d2[2,]), type="o", col="red",
ann=FALSE, pch=17, log = 'y',lty=4, axes=FALSE, las=2) +
lines(as.matrix(d1[1,]),as.matrix(d2[1,]), type="o", col="blue",
ann=FALSE, pch=15, lty=4)
x_axis_labels <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
axis(1,labels = x_axis_labels, at = x_axis_labels)
y_axis_labels <- c(3e+4, 6e+4,2e+5,3e+5, 6e+5, 2e+6,5e+6)
axis(2,labels = y_axis_labels, at = y_axis_labels, las=2)
grid()
Which produces the following:
But what I like to have is to have the grid to start from all the labels on each axis. At the moment it only starts from some of the labels on the x-axis, and is not aligned with any of the y-axis labels.
This will probably be easier to control with abline:
abline(v=x_axis_labels, h=y_axis_labels, lty=2, col='lightgray')
If you want the gridlines behind points etc., then try the panel.first argument to plot:
plot(as.matrix(d1[2,]), as.matrix(d2[2,]), type="o", col="red",
ann=FALSE, pch=17, log = 'y',lty=4, axes=FALSE, las=2,
panel.first=abline(v=x_axis_labels, h=y_axis_labels, lty=2, col='lightgray'))
I want the y axis in regular plot() function to start at the bottom of the plot area similar to hist() function. In other words I would like to have zeros of both axis at the same level. Here is my working example
set.seed(1)
data <- data.frame(
type = as.factor(sample(c('A', 'B', 'C'), size = 100, replace = T)),
value = rexp(100, 1/3)
)
plot(data$type)
par(new=TRUE)
plot(tapply(data$value, data$type, mean),
xaxt="n", yaxt="n", xlab="", ylab="",
xlim=c(0.55,3.45), ylim=c(0, 5), bty='n', pch=24, bg='black')
axis(4)
I tried to use parameter yaxs = 'i' in the plot() function, but it moved the axis too low. Is there any solution to this?
Find one variant
set ylim in first plot
try
plot(data$type, yaxs = 'i',ylim=c(0,max(apply(data, 2, table)[[1]])))
par(new=TRUE)
plot(tapply(data$value, data$type, mean),
xaxt="n", yaxt="n", xlab="", ylab="",
xlim=c(0.55,3.45), ylim=c(0, 5), bty='n', pch=24, bg='black',yaxs = 'i')
axis(4)
axis(1,at=c(0,5)) #only for show that one lvl
Can someone help me. I have a dataset that had NA values that I have interpolated with zoo. I have added a 'colour column' in the hope that I could create a line plot (time series) with the interpolated values plotted in a different colour to the rest of the line. That is, the segment of the line defined by the point immediately before and immediately after the interpolated point should be red, and not black.
I've attached an example of my table here (where the colour is 'red' defines the values that have been interpolated). I've also put an image of the graph so far and the desired output here too:
https://drive.google.com/folderview?id=0B_eJi0urUAzFM0JBS1ZIbUdGck0&usp=drive_web
This is my code thus far. The 'lines' part of the code is where I hoped to define the colour as the column in the data frame:
par(mfrow=c(2,1), mar=c(4,4.5,2,2), mgp=c(2,0.6,0))
x.limit <- round(range(UN.GRACE.Int$DecimDate), 2)
plot(NULL, type="n", xlim=x.limit, ylim=c(-20, 25), xlab="Year", ylab="GRACE-TWS (cm)", axes=F)
box(lwd=1.5)
abline(h=0, col="gray50", lty=1)
axis(1, seq(2003, 2012, 1), cex.axis=0.8)
axis(2, seq(-20, 25, 5), las=1, cex.axis=0.8)
minor.tick(nx=4, ny=0, tick.ratio=0.5)
lines(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1], type="l", lwd=3, col=UN.GRACE.Int[,3])
tws.slope <- round(as.vector(coef(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]))[2]), 2)
tws.sdev <- round(as.vector(coef(summary(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1])))[, "Std. Error"][2]), 2)
abline(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]), lwd=2.5, lty=2, col=2)
mtext(paste("Trend (cm/year): ", tws.slope, "±", tws.sdev, sep=""), cex=0.8, side=1, line=-1.1)
Any help would be appreciated - Thanks
If I understand this correctly, you want the interpolated points to show up with a different color. You can accomplish this using the type="o" option in R, which gives over-plotted lines. Here's some adjusted code that produces the following plot. I took the minor.tick command out because it must have been from a package I don't have, but otherwise it works fine (using R 2.15.3 on my local machine).
You'll notice that I just plot the item directly, rather than calling plot to NULL and then adding in lines. This simplifies the code substantially. You can play with the pch parameter in the plot call to change the symbols used, and also alter the lwd parameters as needed. In fact, you could easily give a different value to pch for the interpolated values, like you did color - it accepts a vector as an argument.
par(mar=c(4,4.5,2,2), mgp=c(2,0.6,0))
x.limit <- round(range(UN.GRACE.Int$DecimDate), 2)
plot(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1],
type="o",
pch=18,
col=UN.GRACE.Int[,3],
xlim=x.limit,
ylim=c(-20, 25),
xlab="Year",
ylab="GRACE-TWS (cm)",
axes=F)
box(lwd=1.5)
abline(h=0, col="gray50", lty=1)
axis(1, seq(2003, 2012, 1), cex.axis=0.8)
axis(2, seq(-20, 25, 5), las=1, cex.axis=0.8)
tws.slope <- round(as.vector(coef(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]))[2]), 2)
tws.sdev <- round(as.vector(coef(summary(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1])))[, "Std. Error"][1]), 2)
abline(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]), lwd=2.5, lty=2, col=2)
mtext(paste("Trend (cm/year): ", tws.slope, "±", tws.sdev, sep=""), cex=0.8, side=1, line=-1.1)
You could also add the points later if you JUST want to see the points where the data was interpolated. This could be done as follows:
par(mar=c(4,4.5,2,2), mgp=c(2,0.6,0))
x.limit <- round(range(UN.GRACE.Int$DecimDate), 2)
plot(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1],
type="l",
pch=18,
col="black",
xlim=x.limit,
ylim=c(-20, 25),
xlab="Year",
ylab="GRACE-TWS (cm)",
axes=F)
box(lwd=1.5)
abline(h=0, col="gray50", lty=1)
axis(1, seq(2003, 2012, 1), cex.axis=0.8)
axis(2, seq(-20, 25, 5), las=1, cex.axis=0.8)
tws.slope <- round(as.vector(coef(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]))[2]), 2)
tws.sdev <- round(as.vector(coef(summary(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1])))[, "Std. Error"][3]), 2)
abline(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]), lwd=2.5, lty=2, col=2)
mtext(paste("Trend (cm/year): ", tws.slope, "±", tws.sdev, sep=""), cex=0.8, side=1, line=-1.1)
points(x=UN.GRACE.Int[UN.GRACE.Int$Col.CSR=="red",1],
y=UN.GRACE.Int[UN.GRACE.Int$Col.CSR=="red",2],
pch=16,
col="red")
EDITED TO ADD: This is a way to color the line segments themselves by overplotting the original plot, assuming the distance to be colored is always of length one. It uses a quick'n'dirty for() loop, but it could be made into a function if you wanted.
par(mar=c(4,4.5,2,2), mgp=c(2,0.6,0))
x.limit <- round(range(UN.GRACE.Int$DecimDate), 2)
plot(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1],
type="l",
pch=18,
col="black",
xlim=x.limit,
ylim=c(-20, 25),
xlab="Year",
ylab="GRACE-TWS (cm)",
axes=F)
box(lwd=1.5)
abline(h=0, col="gray50", lty=1)
axis(1, seq(2003, 2012, 1), cex.axis=0.8)
axis(2, seq(-20, 25, 5), las=1, cex.axis=0.8)
tws.slope <- round(as.vector(coef(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]))[2]), 2)
tws.sdev <- round(as.vector(coef(summary(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1])))[, "Std. Error"][5]), 2)
abline(lm(UN.GRACE.Int[,2] ~ UN.GRACE.Int[,1]), lwd=2.5, lty=2, col=2)
mtext(paste("Trend (cm/year): ", tws.slope, "±", tws.sdev, sep=""), cex=0.8, side=1, line=-1.1)
line_segs <- cbind(lstart=UN.GRACE.Int[which(UN.GRACE.Int$Col.CSR=="red")-1,c("DecimDate","CSR")],
lend=UN.GRACE.Int[which(UN.GRACE.Int$Col.CSR=="red")+1,c("DecimDate","CSR")])
for(x in 1:nrow(line_segs)) {
lines(x=c(line_segs[x,1],line_segs[x,3]),
y=c(line_segs[x,2],line_segs[x,4]),
lwd=3,
col="red")
}