Two x-axis scales on one R plot - r

I am trying to create a plot that shows two different time scales on the x-axis. The problem is that the two time scales have a complicated relationship.
I would like to show weather data by the day of year and by the thermal units. Thermal units are the accumulation of the mean temperatures of each day. Some days we get a lot of thermal units, some days not so many. I fit a spline to the relationship between day of year and thermal units and used that to predict thermal unit values for each day. So I do have a nice dataset with the following headers: day of year (day), thermal units (gdd), temperature (temp), precipitation (precip).
I created the following figure (may have to open in new window):
with this code:
pdf(file="Climate 2010.pdf", family="Times")
par(mar = c(5,4,4,4) + 0.3)
plot(cobs10$day, cobs10$precip, col="white", type="h", yaxt="n", xaxt="n", ylab="",
xlab="")
axis(side=3, col="black", labels=FALSE)
at = axTicks(3)
mtext(side = 3, text = at, at = at, col="black", line = 1, las=0)
mtext("Day of Year", side=3, las=0, line = 3)
par(new=TRUE)
plot(cobs10$gdd, cobs10$temp, type="l", col="red", yaxt="n", ylab="", xlab="Thermal
Units")
axis(side=2, col='red', labels=FALSE)
at= axTicks(2)
mtext(side=2, text= at, at = at, col = "red", line = 1, las=0)
mtext("Temperature (C)", side=2, las=0, line=3)
par(new=TRUE)
plot(cobs10$gdd, cobs10$precip, type="h", col="blue", yaxt="n", xaxt="n", ylab="",
xlab="")
axis(side=4, col='blue', labels=FALSE)
at = axTicks(4)
mtext(side = 4, text = at, at = at, col = "blue", line = 1,las=0)
mtext("Precipitation (cm)", side=4, las=0, line = 3)
dev.off()
This is exactly what I want, but I realized the x-axis scales are linear here, and they should not be. I put the top x-axis in by making my precipitation data white and writing over it. See what happens when I make it green:
It's obvious things don't match up. So how can I make the two axes in scale with eachother?
Here's the little dataframe I have been using where the time units are matched up by predicting:
cobs10.txt. "gdd" is thermal units
EDIT: Here is some new code that doesn't use par(new=TRUE):
par(mar = c(5,4,4,4) + 0.3)
plot(cobs10$gdd, cobs10$temp, type="l", col="red", yaxt="n", xlab="", ylab="",
ylim=c(-25, 30))
lines(cobs10$gdd, cobs10$precip, type="h", col="blue", yaxt="n", xlab="", ylab="")
axis(side=3, col="black", at=cobs10$gdd, labels=cobs10$day)
want<-(c(1, 130, 150, 170, 190, 210, 230, 250, 270, 360))
mtext(side = 3, text = want, at = want, col="black", line = 1, las=0)
mtext("Day of Year", side=3, las=0, line = 3)
axis(side=2, col='red', labels=FALSE)
at= axTicks(2)
mtext(side=2, text= at, at = at, col = "red", line = 1, las=0)
mtext("Temperature (C)", side=2, las=0, line=3)
axis(side=4, col='blue', labels=FALSE)
at = axTicks(4)
mtext(side = 4, text = at, at = at, col = "blue", line = 1,las=0)
mtext("Precipitation (cm)", side=4, las=0, line = 3)

It is almost never a good idea to use par(new=TRUE), it causes more problems than it solves.
You should decide which x axis units you want to use, days or thermal units and create the initial graph using that unit, then use functions like points or lines to add any additional points or lines to the existing graph using the existing units. You can then use the axis function to add another axis, use the original units for the at argument, but then the conversion to the other units for the labels (you need to decide if you want the locations of the ticks to match the original units, or be in locations that give pretty values for the converted units).

Related

R - Dual y axis chart : align 0 and adjust scales

I used plot.new to combine two different graphs on R with this code:
par(mar=c(4, 4, 3, 5))
plot(d1, col="grey", yaxt="n", xlab="", ylab = "", ylim(0, 1.5), type = "h", fill ="grey")
axis(4, at=seq(0,1.5, by=0.05), col="grey", col.axis="grey")
mtext("Fn_1", side=4, line=2.5, col="grey")
mtext("Sqrt Insolation", side=1, line=2.5, col="black")
par(new=TRUE)
plot(h, axes=FALSE, type = "l", xlab = "", ylab="")
axis(2, ylim=c(-0.10, 0.1), at=seq(-0.10, 0.1, by=0.05), col="black", col.axis="black") mtext("Fn_2", side=2, line=2.5, col="black")
This is my result
https://i.stack.imgur.com/umCWu.png
I want to have a y axis like ylim = c (-0.10,1.5) and adjust the scale (h curb is way too big with this scale ..)
OR two y axes like here, but align on 0 with the same scale.
I tried to change the criteria of my axes but they seem to adjust automatically to make sense with the minimum and maximum of my data. I want to have one y axis as ylim = c(-0.10,1.5) and adjust scale (h courb is way too large with this actual scale..)
Can someone help me ?
Thank you very much

How to avoid wired ylab error when plotting in R

I need a two y-axes figure. hrbrmstr suggested to use simple plots. But when adapting the graph to my setting I observed I cannot add the ylab on the right hand side, getting a wired error:
Error in axis(4, ylim = c(0, 1), col = "black", col.axis = "black", las = 1, :
'labels' is supplied and not 'at'
Is this avoidable?
look at the code the bottom line fpr SOURCE OF ERROR
featPerf <- data.frame( expS=c("1", "2", "3", "4"),
exp1=c(1000, 0, 0, 0),
exp2=c(1000, 5000, 0, 0),
exp3=c(1000, 5000, 10000, 0),
exp4=c(1000, 5000, 10000,20000),
accuracy=c(0.4, 0.5, 0.65, 0.9) )
# make room for both axes ; adjust as necessary
par(mar=c(5, 5, 5, 7) + 0.2)
# plot the bars first with no annotations and specify limits for y
#barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", ylim=c(0, max(colSums(featPerf[2:5]))))
barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", beside=TRUE)
# make the bounding box (or not...it might not make sense for your plot)
#box()
# now make the left axis
axis(2, ylim=c(0, max(colSums(featPerf[2:5]))), col="black", las=1)
# start a new plot
par(new=TRUE)
# plot the line; adjust lwd as necessary
plot(x=1:4, y=featPerf[,6], xlab="Experiments", ylab="Abs. # of Features", axes=FALSE, type="l", ylim=c(0,1), lwd=5)
# annotate the second axis -- SOURCE OF ERROR -> VVVVVVVVVVVVVVVVVV
axis(4, ylim=c(0,1), col="black", col.axis="black", las=1, labels="Accuracy")
Like this?
par(mar=c(4,4,1,4) + 0.2)
barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", beside=TRUE)
axis(2, ylim=c(0, max(colSums(featPerf[2:5]))), col="black", las=1)
par(new=TRUE)
plot(x=1:4, y=featPerf[,6], xlab="Experiments", ylab="Abs. # of Features", axes=FALSE, type="l", ylim=c(0,1), lwd=5, col="blue")
axis(4, ylim=c(0,1), col="blue", col.axis="blue", las=1)
mtext("Accuracy",4,line=2, col="blue")
For the record, it is never a good idea to stack plots on top of each other this way (with two axes). I've made the line and the axis the same color in an attempt to draw attention to what you are doing, but this is still a very bad idea.
First of all it is not advisable to use two Y-axes in a same plot.
If you add at argument to the axis call, you get the name "Accuracy" on the right hand side of the plot.
axis(4, ylim=c(0,1), col="black", col.axis="black", las=1, labels="Accuracy",
at = .5)

overlaid plots using different axes in R

I am trying to overlay two graphs onto the same axes. I set up my axes limits and labels first, but then when I plot the graphs they resize and are not on my pre-determined scale.
I have pared my code down into a simple example. You can see that 100 and 10 are showing up at the same place on the y axis. Please help!
x<- 1:3
y1<- c(100, 75, 20)
y2<- c(10, 9, 4)
plot.new()
plot(0, type="n",
xlim=c(1,max(x)), ylim=c(0,max(y1,y2)),
xlab= "x label", ylab= "y label", main= "This stupid graph doesn't work!")
par(new=TRUE)
par(new=TRUE)
plot(x,y1, type="b", pch=19, col="orchid",
axes=FALSE, ann=FALSE)
par(new=TRUE)
plot(x,y2, type="b", pch=19, col="slateblue",
axes=FALSE, ann=FALSE)
legend("topright",c("This is","Annoying"), col=c("orchid","slateblue"), pch=19)
You want to use lines to add the second line, meanwhile making sure that ylim allows for all the values being plotted to fit within the plotting region.
plot(y1 ~ x, ylim = range(c(y1, y2)), xlab = "x label", ylab = "y label",
main = "This one might work!", type = 'b', pch = 19, col = "orchid")
lines(y2, type = 'b', pch = 19, col = 'slateblue')
legend("topright", c("R is", "awesome"), col = c("orchid","slateblue"), pch = 19)

Change the number of tick marks on a figure in R

I created a figure of two plots (two years) of climate data (temp and precip) that looks exactly like I want it, except that one of my axes has too many tick marks. With everything I have going on with this figure, I can't find a way to specify fewer tick marks without messing up other parts. I would also like to specify where the tick marks are. Here is the figure:
You can see that the tick marks for the top axis just blur together and the numbers chosen are not very meaningful to me. How can I tell R what I really want?
Here are the datasets I am using: cobs10 and
cobs11.
And here is my code:
par(mfrow=c(2,1))
par(mar = c(5,4,4,4) + 0.3)
plot(cobs10$day, cobs10$temp, type="l", col="red", yaxt="n", xlab="", ylab="",
ylim=c(-25, 30))
axis(side=3, col="black", at=cobs10$day, labels=cobs10$gdd)
at = axTicks(3)
mtext("Thermal Units", side=3, las=0, line = 3)
axis(side=2, col='red', labels=FALSE)
at= axTicks(2)
mtext(side=2, text= at, at = at, col = "red", line = 1, las=0)
mtext("Temperature (C)", side=2, las=0, line=3)
par(new=TRUE)
plot(cobs10$gdd, cobs10$precip, type="h", col="blue", yaxt="n", xaxt="n", ylab="",
xlab="")
axis(side=4, col='blue', labels=FALSE)
at = axTicks(4)
mtext(side = 4, text = at, at = at, col = "blue", line = 1,las=0)
mtext("Precipitation (cm)", side=4, las=0, line = 3)
par(mar = c(5,4,4,4) + 0.3)
plot(cobs11$day, cobs11$temp, type="l", col="red", yaxt="n", xlab="Day of Year",
ylab="", ylim=c(-25, 30))
axis(side=3, col="black", at=cobs11$day, labels=cobs11$gdd)
at = axTicks(3)
mtext("", side=3, las=0, line = 3)
axis(side=2, col='red', labels=FALSE)
at= axTicks(2)
mtext(side=2, text= at, at = at, col = "red", line = 1, las=0)
mtext("Temperature (C)", side=2, las=0, line=3)
par(new=TRUE)
plot(cobs11$gdd, cobs11$precip, type="h", col="blue", yaxt="n", xaxt="n", ylab="",
xlab="", ylim=c(0,12))
axis(side=4, col='blue', labels=FALSE)
at = axTicks(4)
mtext(side = 4, text = at, at = at, col = "blue", line = 1,las=0)
mtext("Precipitation (cm)", side=4, las=0, line = 3)
Thanks for thinking about it.
You've pretty much got the solution already:
axis(side=3, col="black", at=cobs10$day, labels=cobs10$gdd)
Except, you are asking to have ticks and labels at every single entry.
Take a look at the function pretty:
at <- pretty(cobs10$day)
at
# [1] 0 100 200 300 400
These are where the ticks should be placed on the x-axis. Now you need to find the corresponding labels. This is not straigtforward, but we will get:
lbl <- which(cobs10$day %in% at)
lbl
# [1] 100 200 300
lbl <- c(0, cobs10$gdd[lbl]
axis(side=3, at=at[-5], labels=lbl)
Update
I've been a bit annoyed by your use of three different series in a single plot. There are many reasons this is troublesome.
Having two y-values are always troublesome see this article from Stephen Few (go to page 5 for my favorite example); in your case it is not that serious due to the nature of the plots and your use of colours to indicate which y-axis the values belong to. But still, on principle.
Axis ticks should have a fixed function, e.g. linear or logarithm. With your Thermal Units, they appear "randomly" (I know that is not the case, but for an outsider they do).
We gotta do something about your x-axis ticks that just refer to "day of year".
First up, we take a look at your data and see what can be done naively. We recognize that your ''date'' variable is actual dates. Let's exploit it and make R aware of it!
cobs10 <- read.table('cobs10.txt',as.is=TRUE)
cobs10$date <- as.Date(cobs10$date)
plot(temp ~ date, data=cobs10, type='l')
Here, I really like the x-axis ticks and had some trouble replicating it. ''pretty'' on dates insisted on either 4 ticks or 12 ticks. But we will come back to that later.
Next, we can do something about the overlay plotting. Here I use ''par(mfrow=c(3,1))'' to instruct R to have three multiple plots stacked in a single window; with these multiple plots we can differentiate between inner and outer margins. The ''mar'' and ''oma'' arguments refers to the inner and outer margin.
Lets put all three variable together!
par(mfrow=c(3,1), mar=c(0.6, 5.1, 0, 0.6), oma=c(5.1, 0, 1, 0))
plot(temp ~ date, data=cobs10, type='l', ylab='Temperatur (C)')
plot(precip ~ date, data=cobs10, type='l', ylab='Precipitation (cm)')
plot(gdd ~ date, data=cobs10, type='l', ylab='Thermal units')
This looks okay, but not with ticks on top of the plots. Not good. Naturally, we can enable ticks in the first two plots (with ''plot(..., xaxt='n')''), but this will distort the bottom plot. So you will need to do so for all three plots and then add the axis to the outer plotting region.
par(mfrow=c(3,1), mar=c(0.6, 5.1, 0, 0.6), oma=c(5.1, 0, 1, 0))
plot(temp ~ date, data=cobs10, type='l', xaxt='n', ylab='Temperatur (C)')
plot(precip ~ date, data=cobs10, type='l', xaxt='n', ylab='Precipitation (cm)')
plot(gdd ~ date, data=cobs10, type='l', xaxt='n', ylab='Thermal units')
ticks <- seq(from=min(cobs10$date), by='2 months', length=7)
lbl <- strftime(ticks, '%b')
axis(side=1, outer=TRUE, at=ticks, labels=lbl)
mtext('2010', side=1, outer=TRUE, line=3, cex=0.67)
Since ''pretty'' doesn't behave as we want it to, we use ''seq'' to make the sequence of x-axis ticks. Then we format the dates to just display an abbreviation of the month name, but this is done with regard to local settings (I live in Denmark), see ''locale''.
To add the axis-ticks and a label to the outer region, we must remember to specify ''outer=TRUE''; otherwise it is added to the last subplot.
Also note that I specified ''cex=0.67'' to match the font size of the x-axis to the y-axis.
Now I agree that displaying the thermal units in a individual subplot is not optimal, although it is the correct way of displaying it. But there was the issue with the ticks. What we really want is to display some nice values that clearly display that they are not linear. But your data does not necessarily contain these nice values, so we will have to interpolate them ourselves.
For this, I use the ''splinefun''
lbl <- c(0, 2, 200, 1000, 2000, 3000, 4000)
thermals <- splinefun(cobs10$gdd, cobs10$date) # thermals is a function that returns the date (as an integer) for a requested value
thermals(lbl)
## [1] 14649.00 14686.79 14709.55 14761.28 14806.04 14847.68 14908.45
ticks <- as.Date(thermals(lbl), origin='1970-01-01') # remember to specify an origin when converting an integer to a Date.
Now the thermal ticks are in place, lets try it.
par(mfrow=c(2,1), mar=c(0.6, 5.1, 0, 0.6), oma=c(5.1, 0, 4, 0))
plot(temp ~ date, data=cobs10, type='l', xaxt='n', ylab='Temperatur (C)')
plot(precip ~ date, data=cobs10, type='l', xaxt='n', ylab='Precipitation (cm)')
usr <- par('usr')
x.pos <- (usr[2]+usr[1])/2
ticks <- seq(from=min(cobs10$date), by='2 months', length=7)
lbl <- strftime(ticks, '%b')
axis(side=1, outer=TRUE, at=ticks, labels=lbl)
mtext('2010', side=1, at=x.pos, line=3)
lbl <- c(0, 2, 200, 1000, 2000, 3000, 4000)
thermals <- splinefun(cobs10$gdd, cobs10$date) # thermals is a function that returns the date (as an integer) for a requested value
ticks <- as.Date(thermals(lbl), origin='1970-01-01') # remember to specify an origin when converting an integer to a Date.
axis(side=3, outer=TRUE, at=ticks, labels=lbl)
mtext('Thermal units', side=3, line=15, at=x.pos)
Update I changed the mtext function calls in the last code block to ensure that the x-axis texts are centred on the plotting region, not the entire region. You might want to tweak the vertical position by changing the line-argument.

R: plotting untransformed data on a log x axis (similar to plotting on log graph paper)

I have 3 sets of data that I am trying to plot on a single plot. The first data set x values range from ~ 1 to 1700 whereas the other two data sets x values are less than 20. Therefore I want to plot them on a log axis to show variations in all the data sets. However I do not want to transform the data as I want to be able to read the values off the graph. The x axis labels I would like are 1, 10, 100 and 1000 all equally spaced. Does anyone know how to do this? I can only find examples where the data is log as well as the axis. I have attached the code I am currently using below:
Thanks in advance for any help given.
Holly
Stats_nineteen<-read.csv('C:/Users/Holly/Documents/Software Manuals/R Stuff/Stats_nineteen.csv')
attach(Stats_nineteen)
x<-Max
x1<-Min
x2<-Max
y1<-Depth
y2<-Depth
par(bg="white")
par(xlog=TRUE)
plot(x2,y1, type="n", ylim=c(555,0), log="x", axes=FALSE, ann=FALSE)
box()
axis(3, at=c(1,10,100,1000), label=c(1,10,100,1000), pos=0, cex.axis=0.6)
axis(1, at=c(1,10,100,1000), label=c(1,10,100,1000), cex.axis=0.6)
axis(2, at=c(600,550,500,450,400,350,300,250,200,150,100,50,0), label=c
(600,"",500,"",400,"",300,"",200,"",100,"",0), cex.axis=0.6)
mtext("CLAST SIZE / mm", side=3, line=1, cex=0.6, las=0, col="black")
mtext("DEPTH / m", side=2, line=2, cex=0.6, las=0, col="black")
grid(nx = NULL, ny = NULL, col = "lightgray", lty = "solid",
lwd = par("lwd"), equilogs = TRUE)
par(new=TRUE)
lines(x1,y1, col="black", lty="solid", lwd=1)
lines(x2,y2, col="black", lty="solid", lwd=1)
polygon(c(x1,rev(x2)), c(y1,rev(y2)), col="grey", border="black")
par(new=TRUE)
plot(x=Average,y=Depth, type="o",
bg="red", cex=0.5, pch=21,
col="red", lty="solid",
axes=FALSE, xlim=c(0,1670), ylim=c(555,0),
ylab = "",xlab = "")
par(new=TRUE)
plot(x=Mode,y=Depth, type="o",
bg="blue", cex=0.5, pch=21,
col="blue", lty="solid",
axes=FALSE, xlim=c(0,1670), ylim=c(555,0),
ylab = "",xlab = "")
You can do this in ggplot using scale_x_log
so something like:
myplot <- ggplot( StatsNinetee,
aes (x = myResponse,
y = myPredictor,
groups = myGroupingVariable) ) +
geom_point() +
scale_x_log()
myplot
also, avoid attach() it can give odd behavior.

Resources