Arrange points and lines in an r plot legend - r

Is it possible to rearrange the legend of the following plot
plot(1,1, type="n")
legend("topleft", c("1", "2"), col=c("darkblue", "darkred"), pch = 1, bty = "n", horiz = T, lwd=1.25, cex=1.8)
to look like this ("point-line-point" pattern)?

Usually, if you want this level of control over plot elements, you'll have to do it manually with primitives (points(), lines()/segments(), text(), etc.) and careful calculations from the plot parameters (e.g. par('usr')). It's not easy. Here's an example of how this could be done:
point.line.point <- function(x1,y1,x2=x1,y2=y1,...) {
points(c(x1,x2),c(y1,y2),...);
segments(x1,y1,x2,y2,...);
};
legend.plp <- function(x,y,labels,col,linewidth=diff(par('usr')[1:2])/10,textgap=diff(par('usr')[1:2])/20,...) {
comb <- cbind(labels,col);
xc <- x;
for (i in seq_len(nrow(comb))) {
x2 <- xc+linewidth;
point.line.point(xc,y,x2,col=comb[i,'col'],...);
text(x2+textgap,y,comb[i,'labels'],...);
xc <- x2+textgap*1.5+strwidth(comb[i,'labels']);
};
};
plot(1,1,type="n");
legend.plp(par('usr')[1]+diff(par('usr')[1:2])/20,par('usr')[4]-diff(par('usr')[3:4])/20,1:2,c('darkblue','darkred'),font=2,cex=1.5);

Here is an alternative solution that is the opposite of elegant. It involves embedding a couple of plots (one per legend), and a great deal of manual manipulation (to set the 'legends' where you want them to be):
library(Hmisc)
data(mtcars)
#plots the one in blue
plot(mtcars$cyl, type="o", col="darkblue")
#plots the one in red
lines(mtcars$carb, type="o", col="darkred")
#name the legends
text(6.5,7, "Cyl", font=2)
text(14,7, "Carb", font=2)
#add the subplots, it's actually a normal plot wrapped around the subplot with the x and y positions
subplot(plot(c(1,0),c(1,1), xlab=NA, ylab=NA, xaxt="n", yaxt="n", col="darkblue", type="o", axes=FALSE), 3, 7)
subplot(plot(c(1,0),c(1,1), xlab=NA, ylab=NA, xaxt="n", yaxt="n", col="darkred", type="o", axes=FALSE), 10, 7)
That yields the following plot:

Related

Plotting multiple plots - rescaling of axes

I found, that axes were rescaled during multiple plotting using par(new=T) parameter.
An example to demonstrate this:
a <- seq(1,10, by = 0.25)
b <- sin(a)
c <- sin(2*a)+1
d <- sin(0.5*a)+2
df <- data.frame(a,b,c,d)
plot(df$a, df$b, type="l")
par(new=T)
plot(df$a, df$c, type="l", col="blue")
par(new=T)
plot(df$a, df$d, type="l", col="red")
This is the result.
Instead of real scales, I have a transformed curves.
And this is the real result:
I used parameters axes=F, xlab="", ylab="" and did not see this "rescaling".
I find it very dangerous, that it is so easy to transform the data during plotting if you do not control y-limits.
Are there better ways to control y-limits than looking for min and max values in all plotted data to avoid this "rescaling" effect?
I have several quite big files and each of them gives only one line from 10 in one plot and I have several plots on one page to compare my data.
The code for the last "correct" image:
plot(df$a, df$b, type="l", ylim=c(-1.5,3.5))
par(new=T)
plot(df$a, df$c, type="l", ylim=c(-1.5,3.5), col="blue", axes=F, xlab="", ylab="")
par(new=T)
plot(df$a, df$d, type="l", ylim=c(-1.5,3.5), col="red", axes=F, xlab="", ylab="")
#Create an empty plot with enough xlim and ylim to accomodate all data
plot(1, 1, xlim = range(df[,1]), ylim = range(df[,-1]), type = "n", ann = FALSE)
#Draw the three lines
lines(df$a, df$b)
lines(df$a, df$c, col="blue")
lines(df$a, df$d, col="red")

R horizontal barplot with aligned plot ontop

I am having trouble getting the spacing right on a plot on top of a horizontal barplot. It is the same general issue as described here:
http://www.r-bloggers.com/adding-lines-or-points-to-an-existing-barplot/
But I am trying to use "plot" instead of "points" or "lines". Is there a trick for using plot to get the spacing of the bars and the points to match?
Code:
barplot(df$DIC_mM,col=scalegreen, xlab="DIC mM", horiz=TRUE, xlim=c(0,0.7),
col.axis="white", col.lab="white", axes=FALSE, border="white")
axis(1,line=1,col="white",col.ticks="white",col.axis="white")
par(new = TRUE)
plot(df$d13DIC,df$Order, type="p", axes = FALSE, bty = "n", xlab ="",
col="deepskyblue2", lwd=5, xlim=c(-50,170), lend=2, col.lab="white", ylab="")
axis(3,at = c(-50,0,50,100,150), line=1, col="deepskyblue2", col.ticks="deepskyblue2",
col.axis="deepskyblue2")
mtext(expression(paste(delta ^{13},'DIC'," \u0028","\u2030","\u0029")), 3,
line=-0.5,at=50,col="deepskyblue2", cex=0.75)
Is there a reason why you don't want to use points to add the points? If you're willing to use points you can do it like this:
Create barplot and save the y-coordinates of the bars to y. You haven't provided sample data, so I'll use the built-in mtcars data frame:
y = barplot(mtcars$mpg[1:10], horiz=TRUE)
Now add the points. We use y as the y values, because those are the coordinates of the midpoints of each bar:
points(sqrt(mtcars$mpg[11:20]), y, col="red", pch=16, cex=2)
When you use par(new=TRUE) and then call plot again, you're overlaying a new plot with a new coordinate system that in general will be different from the original coordinate system.
This is what worked, based on this post suggested by eipi10: midpoints returned by barplot function do not actually line up with midpoints of bars
mp<-barplot(df$DIC_mM,col=scalegreen, xlab="DIC mM", horiz=TRUE, xlim=c(0,0.7), col.axis="white", col.lab="white", axes=FALSE, border="white", ylim=c(0,length(df$DIC_mM)+2))
axis(1,line=1,col="white",col.ticks="white",col.axis="white")
par(new = TRUE)
plot(df$d13DIC, mp, type="p", axes = FALSE, bty = "n", xlab ="",col="deepskyblue2", lwd=5, xlim=c(-50,170), lend=2, col.lab="white", ylab="", ylim=c(0,length(df$DIC_mM)+2))
axis(3,at = c(-50,0,50,100,150),line=1,col="deepskyblue2",col.ticks="deepskyblue2",col.axis="deepskyblue2")
mtext(expression(paste(delta ^{13},'DIC'," \u0028","\u2030","\u0029")),3,line=-0.5,at=50,col="deepskyblue2", cex=0.75)

R Plot multiple series with par(new=T) - axis labels are overlaying each other, making the plot unreadable

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)

Omit interval / split scales on axis? Fine grained for low and high interval?

Assuming I have two data sets I'd like to plot in one plot.
For instance something like this:
low <- runif(10,min=10, max=50)
high <- runif(10,min=10000, max=11000)
plot(
high,
type="l",
ylim=c(0,11000)
)
lines(low, type="l")
I dont want the y-axis to show all values in between these samples because the way it is, the low data would appear as flat line, even though there are variations.
Therefore, the axis should have a finer grained ticks between 10-50, and should omit values between 50 and 10000. Then again, the interval [10000-11000] should have finer grained ticks, too. Thus, variations for the low sample are also visible.
Is there any way to achieve this behavior?
If found this, bt
Could this be useful...?
set.seed(001)
low <- runif(10,min=10, max=50)
high <- runif(10,min=10000, max=11000)
plot(
high,
type="l",
ylim=c(0,11000)
)
lines(low, type="l")
plot(high,
type="l",
col="red",
bty='l',
ylab='', xlab='',
las=1,
cex.axis=.75)
par(new=TRUE)
plot(low,
type="l",
col="blue",
bty='n',
xaxt="n",
yaxt="n",
xlab="", ylab="",
cex.axis=.75)
axis(4, las=1, cex.axis=.75)
legend('topright', c('high', 'low'), col=c('red', 'blue'), lty=1, bty='n', cex=.75)
Questions of good graphical practice aside, this can be done using the plotrix package:
low <- runif(10,min=10, max=50)
high <- runif(10,min=10000, max=11000)
y <- c(low,high)
x <- rep(1:10,times = 2)
library(plotrix)
yl <- c(pretty(5:70,2),pretty(9900:11000,5))
gap.plot(x,y,
gap = c(70,9900),
gap.axis = "y",
ytics = yl,
type = "l")

Plotting a grid behind data, not in front in R

I like to produce my own grid lines when plotting so I can control tick marks, etc. and I am struggling with this with the 'hist' plotting routine.
hist(WindSpeed, breaks=c(0:31), freq=TRUE, col="blue", xaxt="n", yaxt="n", xlab="Wind Speed (m/s)",main="Foo", cex.main=1.5, cex.axis=1, cex.lab=1, tck=1, font.lab=2)
axis(1, tck=1, ,col.ticks="light gray")
axis(1, tck=-0.015, col.ticks="black")
axis(2, tck=1, col.ticks="light gray", lwd.ticks="1")
axis(2, tck=-0.015)
minor.tick(nx=5, ny=2, tick.ratio=0.5)
box()
Plot:
I have then just been able to use the 'lines' or 'points' command to replot the data over top for other types of plots, but with the histogram its not so easy.
Any help would be great.
I added my code below and image based upon John's response...
I added my code below and image based upon John's response...
hist(WindSpeed, breaks=30, freq=TRUE, col="blue", xaxt="n", yaxt="n", xlab="Wind Speed (m/s)",main="Foo", cex.main=1.5, cex.axis=1, cex.lab=1, font.lab=2)
axis(1, tck=1, col.ticks="light gray")
axis(1, tck=-0.015, col.ticks="black")
axis(2, tck=1, col.ticks="light gray", lwd.ticks="1")
axis(2, tck=-0.015)
minor.tick(nx=5, ny=2, tick.ratio=0.5)
box()
hist(WindSpeed, add=TRUE, breaks=30, freq=TRUE, col="blue", xaxt="n", yaxt="n", xlab="Wind Speed (m/s)", main="Foo", cex.main=1.5, cex.axis=1, cex.lab=1, font.lab=2)
Actually, R has a way to do this! It's the panel.first argument to plot.default, which hist calls to do most of the work. It takes an expression which is evaluated "after the plot axes are set up but before any plotting takes place. This can be useful for drawing background grids or scatterplot smooths," to quote from ?plot.default.
hist(WindSpeed, breaks=c(0:31), freq=TRUE, col="blue", xaxt="n", yaxt="n",
xlab="Wind Speed (m/s)", main="Foo",
cex.main=1.5, cex.axis=1, cex.lab=1, tck=1, font.lab=2,
panel.first={
axis(1, tck=1, col.ticks="light gray")
axis(1, tck=-0.015, col.ticks="black")
axis(2, tck=1, col.ticks="light gray", lwd.ticks="1")
axis(2, tck=-0.015)
minor.tick(nx=5, ny=2, tick.ratio=0.5)
box()
})
See How do I draw gridlines using abline() that are behind the data? for another question that uses this method.
This is relatively easy.
Generate the histogram but don't plot it.
h <- hist(y, plot = FALSE)
Now generate your base plot... I've added some features to make it look more like a standard historgram
plot(h$mids, h$counts, ylim = c(0, max(h$counts)), xlim = range(h$mids)*1.1,
type = 'n', bty = 'n', xlab = 'y', ylab = 'Counts', main = 'Histogram of y')
add your grid
grid()
add your histogram
hist(y, add = TRUE)
Or, as I discovered through this process... you can do it even easier
hist(y)
grid()
hist(y, add = TRUE, col = 'white')
This last method is just redrawing the histogram over the grid.
In R, order matters when you plot. As you've discovered, adding things to a plot adds on top of what you've plotted before. So we need a way to plot the grid first and then the histogram. Try something like this:
plot(1:10,1:10,type = "n")
grid(10,10)
hist(rnorm(100,5,1),add = TRUE)
I haven't recreated your example, since it isn't reproducible, but this general idea should work. But the key idea is to create an empty plot with the correct dimensions using the type = "n" option to plot, then add the grid, then add the histogram using the add = TRUE argument.
Note that the add argument is actually for plot.histogram, hist passes it along via ....
The base graphics solution suggested by #joran is fine. Alternatives:
d <- data.frame(x=rnorm(1000))
library(lattice)
histogram(~x,data=d,panel=function(...) {
panel.grid(...)
panel.histogram(...) }
)
Or:
library(ggplot2)
qplot(x,data=d,geom="histogram",binwidth=0.1)+theme_bw()+
labs(x="Wind speed", y="Frequency")
(But of course you will have to learn all the details of adjusting labels, titles, etc. ... I'm not actually sure how to do titles in ggplot ...)
Another methods for grid lines in background:
A)
hist( y, panel.first=grid() ) # see: help( plot.default )
box()
B)
plot.new() # new empty plot
nv <- length( pretty(x) ) - 1 # number of vertical grid lines (or set by hand)
nh <- length( pretty(y) ) - 1 # number of horizontal grid lines (or set by hand)
grid( nx = nv, ny = nh ) # preplot grid lines
par( new = TRUE ) # add next plot
plot( x, y ) # plot or hist, etc
box() # if plot hist
Arbitrary lines in background with abline:
C)
How do I draw gridlines using abline() that are behind the data?
D)
# first, be sure there is no +/-Inf, NA, NaN in x and y
# then, make the container plot with two invisible points:
plot( x = range( pretty( x ) ), y = range( pretty( y ) ), type = "n", ann = FALSE )
abline( h = hlines, v = vlines ) # draw lines. hlines, vlines: vectors of coordinates
par( new = TRUE ) # add next plot. It is not necessary with points, lines, segments, ...
plot( x, y ) # plot, hist, etc
box() # if plot hist

Resources