I want to make a plot of 4 sets of data points using dual y-axis. The first two are on the left y-axis and last two are on the right y-axis. The first two belong to a set of numbers ranging from 5000 to 50,000. Second two sets of data belong range from 1-100. I want to plot it so that it is easily discernable that the two axis are not only on different scales but also the height between points from the two different sets with distinct ranges is obviously big. I don't want to be able to draw a horizontal line that would suggest some number from the left-y-axis can be mapped bijectively to some number on the right y-axis. I want to make it such that a horizontal line through any points from the left y-axis and right y-axis would belong to only one set related to either left or right axis.
How can I plot with 2 different y-axes?. There's
I'd use twoord.plot
From plotrix v3.7-5
by Jim Lemon but that has the disadvantage than base R beacause I can't add 4 sets of data into one plot. I can only use 2 sets of (x,y) pairs with 2--ord plot. I can theoretically plot n sets of (x,y) pairs using base R.
None
Here's what doesn't work:
time <- seq(0,72,12)
betagal.abs <- c(0.05,0.18,0.25,0.31,0.32,0.34,0.35)
cell.density <- c(0,1000,2000,3000,4000,5000,6000)
## add extra space to right margin of plot within frame
par(mar=c(5, 4, 4, 6) + 0.1)
## Plot first set of data and draw its axis
plot(time, betagal.abs, pch=16, axes=FALSE, ylim=c(0,1), xlab="", ylab="",
type="b",col="black", main="Mike's test data")
axis(2, ylim=c(0,1),col="black",las=1) ## las=1 makes horizontal labels
mtext("Beta Gal Absorbance",side=2,line=2.5)
box()
## Allow a second plot on the same graph
par(new=TRUE)
## Plot the second plot and put axis scale on right
plot(time, cell.density, pch=15, xlab="", ylab="", ylim=c(0,7000),
axes=FALSE, type="b", col="red")
## a little farther out (line=4) to make room for labels
mtext("Cell Density",side=4,col="red",line=4)
axis(4, ylim=c(0,7000), col="red",col.axis="red",las=1)
## Draw the time axis
axis(1,pretty(range(time),10))
mtext("Time (Hours)",side=1,col="black",line=2.5)
## Add Legend
legend("topleft",legend=c("Beta Gal","Cell Density"),
text.col=c("black","red"),pch=c(16,15),col=c("black","red"))
Not quite sure what you're after but you can add an extra 'line per plot' by using lines.
I've edited your code
## Plot first set of data and draw its axis
plot(time, betagal.abs, pch=16, axes=FALSE, ylim=c(0,1), xlab="", ylab="",
type="b",col="black", main="Mike's test data")
lines(seq(0, 1, 0.02), type = 'o')
axis(2, ylim=c(0,1),col="black",las=1) ## las=1 makes horizontal labels
mtext("Beta Gal Absorbance",side=2,line=2.5)
box()
## Allow a second plot on the same graph
par(new=TRUE)
## Plot the second plot and put axis scale on right
plot(time, cell.density, pch=15, xlab="", ylab="", ylim=c(0,7000),
axes=FALSE, type="b", col="red")
lines(seq(0, 5000, 10), type = 'o', col = 'red')
## a little farther out (line=4) to make room for labels
mtext("Cell Density",side=4,col="red",line=4)
axis(4, ylim=c(0,7000), col="red",col.axis="red",las=1)
which produces this:
Please let me know if this wasn't what you were after.
Related
How can I move the y axis label from the left to the right of the plot area and the x-axis label from below to above the plot area in the following graph? Thanks
xleft<-c(1,2,2.5)
xright<-c(2,2.5,2.75)
ybottom<-c(1,2,2.5)
ytop<-c(2,2.5,2.75)
par(mar = c(15,15,2.75,2.75) + 0.1)
plot(c(1,3),c(1,3),type="n",main="title",xlab="xlab-move me above plot",ylab="ylab-move me right of plot",axes=F,asp=1)
axis(1,pos=1)
axis(2,pos=1)
rect(xleft,ybottom,xright,ytop,col=c("blue","red","green"))
#Label position along axes
x.label.position<-(xleft+xright)/2
y.label.position<-(ybottom+ytop)/2
#Labels
x.label<-c("Long species Name1","Long species Name2","Long species Name3")
y.label<-c("Long species Name4","Long species Name5","Long species Name5")
text(par()$usr[1]-0.5,y.label.position,y.label,xpd=TRUE,adj=1)
text(y=par()$usr[3]-0.5,x=x.label.position,x.label,xpd=TRUE,adj=1,srt=90)
par(xpd=TRUE)
legend(-0.1,0,legend=c("Species A","Species B","Species C"),fill=c("blue", "red", "green"))
Ploting axes on the right and top sides of a plot
By default R will plot the x-axis below the plot area and the y-axis to the left of it. You can change this behaviour in this way:
plot(1:100, cumsum(rnorm(100)), type="l", axes=FALSE) # Do not plot any axes
axis(3) # Draw the x-axis above the plot area
axis(4) # Draw the y-axis to the right of the plot area
box()
To also move the labels you set ann=FALSE or xlab="", ylab="" and add them afterwards with mtext, where side=1 is bottom, 2 is left, 3 is top, 4 is right. line controls the distance from the plot area.
plot(1:100, cumsum(rnorm(100)), type="l", axes=FALSE, ann=FALSE)
axis(3)
box()
mtext("Top axis", side=3, line=3)
Changing distance between labels, ticks and plot area.
Use the mgp parameter to control these details, either before the call to plot, like this
par(mgp=c(axis.title.position, axis.label.position, axis.line.position))
or in the plot command itself, like this
plot(1:100, cumsum(rnorm(100)), type="l", mgp=c(2,1,.5), las=1)
Also note the las parameter that turns all tick labels horisontal, which makes them easier to read.
I am having some serious problems with layout() and it is driving me crazy when adding one figure with multiple layers.
I only seem to have a problem when adding a layer on one of the figures within the jpeg I am trying to create.
The layout is to have a 1) simple line plot on top for a time series of fish catch data and on the bottom 2) have a larger image of a map of oceanographic data layers.
I am making a series of maps using image.plot() plus contour(... add=T) and arrows my.symbols(... add=T) from library TeachingDemos.
Data is sliced from large netCDF files.
The image and contours are dissolved oxygen depth and the red arrows are surface current.
Below is my R code, data is looped through variable 'n':
jpeg(paste(interpdate[n],"DailyDOLayerCenAm.jpg", sep=""), width=1150, height=1000, res=100)
layout(matrix(c(1,2),nrow=2), heights=c(1,3))
#first plot on the top, fish catch data by time, moving each day
par(mar=c(1,4,.3,.5))
plot(Date[15:n],sail$X7.day.Average[15:n], xlim=c(Date[15],Date[350]),
xlab='',ylab='Raises/Trip',ylim=c(0,50), type='l', xaxt='n', lwd=2.5)
axis(1, Date, format(Date, "%b %d"), cex.axis = 1)
abline(18.4,0, lty=2)
points(Date[n],sail$X7.day.Average[n], pch=21, col='black', bg='red',
cex=3)
#second plot, Ocean data
par(mar=c(3,3.7,.5,1))
# layer 1 plot the main layer, interpolated grid O2 minimum depth
image.plot( as.surface( expandgrid, ww),xlim=c(xmin,xmax),
ylim=c(ymin,ymax), ylab="Latitude", xlab="Longitude",main="",
col=pal(256), legend.lab="Depth of O2 Minimum Layer (m)",
zlim=c(20,zlimit), cex=1.5)
#layer 2 add the contours
contour(as.surface( expandgrid, ww),xlim=c(xmin,xmax), ylim=c(ymin,ymax),
col='white', lwd=2, nlevels=10, labcex=1, add=T)
#layer 3 add current arrows
my.symbols(lonx,laty,ms.arrows, angle=theta, r=intensity, length=.06,
add=T,xlim=c(xmin,xmax), ylim=c(ymin,ymax), lwd=2, col="red",
fg="black")
#layer 4add the map of land/countries
plot(newmap, col="GREY", add=T)
#add a point of home port in Guatemala
points(-90.81, 13.93, pch=21, col='black', bg='yellow', cex=3.5)
dev.off()
When I plot just my ocean data, it plots fine:
https://fbcdn-sphotos-f-a.akamaihd.net/hphotos-ak-xap1/t31.0-8/10861076_10101738319375937_3436888929444896713_o.jpg
plot it within layout() I get this mess, The contour lines are fine within the x-space, but squished in y space, as are the arrows and map overlay:
https://scontent-b.xx.fbcdn.net/hphotos-xfp1/t31.0-8/10923795_10101738319625437_7583695382864373718_o.jpg
I fixed this by not using imageplot() but by using image() and adding the color legend described by Aurélien Madouasse:
https://aurelienmadouasse.wordpress.com/author/aurelienmadouasse/
I looped the images from a large arrays of oceanographic data and created a series of plots in a function.
My code (sans data) is here, from within a loop:
jpeg(paste(interpdate[n],"DailyDOLayerCenAm.jpg", sep=""), width=1150,
height=1000, res=100)
layout(matrix(c(1,2),nrow=2), heights=c(1,3))
#first plot on the top, fish catch data by time, moving each day
par(mar=c(1,4,.3,.5))
plot(Date[15:n],sail$X7.day.Average[15:n], xlim=c(Date[15],Date[350]),
xlab='',ylab='Raises/Trip',ylim=c(0,50), type='l', xaxt='n', lwd=2.5)
axis(1, Date, format(Date, "%b %d"), cex.axis = 1)
abline(18.4,0, lty=2)
points(Date[n],sail$X7.day.Average[n], pch=21, col='black', bg='red', cex=3)
#second plot, Ocean data
#plot the main layer, dissolved oxygen minimum depth
#plot the main layer, dissolved oxygen minimum depth
image( as.surface( expandgrid, ww),xlim=c(xmin,xmax),
ylim=c(ymin,ymax),ylab="Latitude", xlab="Longitude",main="", col=pal(256),
zlim=c(20,zlimit), cex=1.5)
#add the contours
contour(as.surface( expandgrid, ww),xlim=c(xmin,xmax), ylim=c(ymin,ymax),
col='white', lwd=2,levels=seq(0,zlimit,10), labcex=1, add=T)
#add sea surface current arrows
my.symbols(lonx,laty,ms.arrows, angle=theta, r=intensity, length=.06,
add=T, xlim=c(xmin,xmax), ylim=c(ymin,ymax), lwd=2, col="red", fg="white")
#add the map of land/countries
plot(newmap, col="GREY", add=T)
#add a point of home port in Guatemala
points(-90.81, 13.93, pch=21, col='black', bg='yellow', cex=3.5)
colr <- pal(256) # colors from 'blues'
legend.col(col = colr, lev = ww) # legend from Aurélien Madouasse:
mtext("Depth of O2 Minimum Layer (m)", 4, line=2.5, font=2)
dev.off()
To see a plot of my new images which I am making into a movie, see this link:
https://fbcdn-sphotos-a-a.akamaihd.net/hphotos-ak-xpa1/t31.0-8/10856647_10101738461765587_2760202217270038911_o.jpg
I would like to add a curved line to fit the dark bars of this supply cost curve (like the red line that appears in image). The height of the dark bars represent the range in uncertainty in their costs (costrange). I am using fully transparent values (costtrans) to stack the bars above a certain level
This is my code:
costtrans<-c(10,10,20,28,30,37,50,50,55,66,67,70)
costrange<-c(15,30,50,21,50,20,30,40,45,29,30,20)
cost3<-table(costtrans,costrange)
cost3<-c(10,15,10,30,20,50,28,21,30,50,37,20,50,30,50,40,55,45,66,29,67,30,70,20)
costmat <- matrix(data=cost3,ncol=12,byrow=FALSE)
Dark <- rgb(99/255,99/255,99/250,1)
Transparent<-rgb(99/255,99/255,99/250,0)
production<-c(31.6,40.9,3.7,3.7,1,0.3,1.105,0.5,2.3,0.7,0.926,0.9)
par(xaxs='i',yaxs='i')
par(mar=c(4, 6, 4, 4))
barplot(costmat,production, space=0, main="Supply Curve", col=c(Transparent, Dark), border=NA, xlab="Quantity", xlim=c(0,100),ylim=c(0, 110), ylab="Supply Cost", las=1, bty="l", cex.lab=1.25,axes=FALSE)
axis(1, at=seq(0,100, by=5), las=1, cex.axis=1.25)
axis(2, at=seq(0,110, by=10), las=1, cex.axis=1.25)
Image to describe what I am looking for:
I guess it really depends how you want to calculate the line...
One first option would be:
# Save the barplot coordinates into a variable
bp <- barplot(costmat,production, space=0, main="Supply Curve",
col=c(Transparent, Dark), border=NA, xlab="Quantity",
xlim=c(0,100), ylim=c(0, 110), ylab="Supply Cost", las=1,
bty="l", cex.lab=1.25,axes=FALSE)
axis(1, at=seq(0,100, by=5), las=1, cex.axis=1.25)
axis(2, at=seq(0,110, by=10), las=1, cex.axis=1.25)
# Find the mean y value for each box
mean.cost <- (costmat[1,]+colSums(costmat))/2
# Add a line through the points
lines(bp, mean.cost, col="red", lwd=2)
Which gives
Now, you could do some smoother line, using some sort of regression
For instance, using a LOESS regression.
# Perform a LOESS regression
# To allow for extrapolation, you may want to add
# control = loess.control(surface = "direct")
model <- loess(mean.cost~bp, span=1)
# Predict values in the 0:100 range.
# Note that, unless you allow extrapolation (see above)
# by default only values in the range of the original data
# will be predicted.
pr <- predict(model, newdata=data.frame(bp=0:100))
lines(0:100, pr, col="red", lwd=2)
I have two univariate time series that would like to plot in the same screen chart. The problem is that they have very different scales and therefore the chart becomes very difficult to interpret. How can I plot each series superimposed but each of them using a different vertical axis?
library(xts)
mytime <- as.POSIXlt(seq(Sys.time()-100*60+1,Sys.time(),by=60), origin= '1970-01-01')
x <- xts(rnorm(1:100),mytime)
y <- xts(rnorm(1:100,100,10),mytime)
plot(as.zoo( merge(x,y)), screens=1)
I'm not so sure this is what you want, but here's an idea:
plot(as.zoo(x), las=1)
par(new=TRUE)
plot(as.zoo(y),
col=2,
bty='n',
xaxt="n",
yaxt="n",
xlab="", ylab="")
axis(4, las=1)
legend("topleft",
legend=c("x","y"),
col=1:2,
lty=1,
cex=0.85)
I would like to make a plot with 4 axes in R so that it looks similar to this plot:
I've looked at the Quick-R website for advice and modified one of their examples (called A Silly Axis Example):
# specify the data
x <- c(1:5); y <- x/2;
w <- c(2:4)
z <- c(1:5)
# create extra margin room on the right for an axis
par(mar=c(5, 4, 4, 8) + 0.1)
# plot x vs. y
plot(x, y,type="b", pch=21, col="red",
yaxt="n", lty=3, xlab="", ylab="")
# add x vs. 1/x
lines(x, z, type="b", pch=22, col="blue", lty=2)
# draw an axis on the left
axis(2, at=x,labels=x, col.axis="red", las=2)
# draw an axis on the right, with smaller text and ticks
axis(4, at=w,labels=round(w,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
# draw an axis on the top
axis(3, at=z,labels=round(z,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
# add a title for the right axis
mtext("L", side=3, line=3, cex.lab=1,las=2, col="blue")
# add a title for the right axis
mtext("OSR", side=4, line=3, cex.lab=1,las=2, col="red")
# add a main title and bottom and left axis labels
title("", xlab="GSI", ylab="FSI")
This code produces the following plot:
I'm having difficulty figuring out how different axes can have different scales. For example, I want the top axis L, to go from 5 - 13, but if I set z <-c(5:13) it will not set the axis to these values. However, I can overwrite what the labels are:
axis(3, at=z,labels=round(c(9:13),digits=2), col.axis="blue",
las=2, cex.axis=0.7, tck=-.01)
but then if I want to plot a point with these four parameters, the point will not show up in the correct place. How should I do this?
One (perhaps cumbersome) option would be to write conversion functions that transform values between your two scales. Assuming you know the data ranges for both the top and bottom axes ahead of time, you could write a function like this:
convertScaleToBottom <- function(x,botRange,topRange){
temp <- (x - topRange[1]) / (topRange[2] - topRange[1])
return(botRange[1] + (temp * (botRange[2] - botRange[1])))
}
that takes a set of values, x, in the top axis scale and converts them to the bottom axis scale. Then you can plot the converted values and retain the originals as the labels:
z1 <- 5:13
z1Adj <- convertScaleToBottom(z1,range(x),range(z1))
# draw an axis on the top
axis(3, at=z1Adj,labels=round(z1,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
This method is easily modified to reverse the order of the top axis:
convertScaleToBottomRev <- function(x,botRange,topRange){
temp <- (x - topRange[1]) / (topRange[2] - topRange[1])
return(botRange[2] - (temp * (botRange[2] - botRange[1])))
}