I'm generating numerous plots with xlim and ylim values that I'm calculating on a per-plot basis. I want to put my legend outside the plot area (just above the box around the actual plot), but I can't figure out how to get the maximum y-value of the box around my plot area.
Is there a method for even doing this? I can move the legend where I want it by manually changing the legend() x and y values, but this takes a LONG time for the amount of graphs I'm creating.
Thanks!
-JM
Here's a basic example illustrating what I think you're looking for using one of the code examples from ?legend.
#Construct some data and start the plot
x <- 0:64/64
y <- sin(3*pi*x)
plot(x, y, type="l", col="blue")
points(x, y, pch=21, bg="white")
#Grab the plotting region dimensions
rng <- par("usr")
#Call your legend with plot = FALSE to get its dimensions
lg <- legend(rng[1],rng[2], "sin(c x)", pch=21,
pt.bg="white", lty=1, col = "blue",plot = FALSE)
#Once you have the dimensions in lg, use them to adjust
# the legend position
#Note the use of xpd = NA to allow plotting outside plotting region
legend(rng[1],rng[4] + lg$rect$h, "sin(c x)", pch=21,
pt.bg="white", lty=1, col = "blue",plot = TRUE, xpd = NA)
The command par('usr') will return the coordinates of the bounding box, but you can also use the grconvertX and grconvertY functions. A simple example:
plot(1:10)
par(xpd=NA)
legend(par('usr')[1], par('usr')[4], yjust=0, legend='anything', pch=1)
legend( grconvertX(1, from='npc'), grconvertY(1, from='npc'), yjust=0,
xjust=1, legend='something', lty=1)
The oma, omd, and omi arguments of par() control boundaries and margins of plots - they can be queried using par()$omd (etc). and set (if needed) using par(oma=c()) (where the vector can have up to 4 values - see ?par)
Related
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)
I am trying to make a scatter plot in R using these points:
(as far as I can trace, W. Cleveland suggested using these for sparse and dense, respectively, scatter plots -- I now call them "Cleveland points".)
The bottom row should be straightforward, and the top row could be achieved by doing some smart overplotting (i.e. plotting two symbols on top of each other). But this will not suffice when plotting the legend.
As a solution I was therefore imagining getting a font that contains these 10 symbols and instruct R to use this font when plotting the points + legend.
(And now it occurs to me, that I might just use text with the custom font to plot the points. But this does not solve the legend issue.)
I have also considered using ggplot2, but this would perhaps easiest be implemented via a scale?
Question: Any suggestions on how to use these symbols (preferable the top row), that can also be used in the legend?
Here is one approach using base graphics and the my.symbols function from the TeachingDemos package:
library(TeachingDemos)
ms.Cleveland <- function(num.pt = 1, cex=2, ...) {
funs <- list(
open = function(cex, ...)
points(0,0, pch=1, lwd=2, cex=cex, ...),
filled = function(cex, ...)
points(0,0, pch=16, cex=cex, ...),
half = function(cex, ...)
points(0,0, pch=1, lwd=5, cex=cex, ...),
vert = function(cex, ...) {
points(0,0, pch=1, lwd=2, cex=cex, ...)
points(0,0, pch='|', lwd=2, cex=cex/2, ...)
},
dot = function(....) {
points(0,0, pch=1, lwd=2, cex=cex, ...)
points(0,0, pch=16, cex=cex/3, ...)
}
)
funs[[num.pt]](cex, ...)
}
### create size variable for mtcars
sz <- findInterval( mtcars$wt, quantile( mtcars$wt, c(0.2, 0.4, 0.6, 0.8) ) ) + 1
with(mtcars, my.symbols(wt, mpg, ms.Cleveland, num.pt=sz, add=FALSE,
symb.plots=TRUE))
tmp <- legend('topright', pch=1, col=NA, pt.cex=2,
legend=c('Small', 'SMed', 'Medium', 'LMed', 'Large'))
my.symbols( (tmp$rect$left + tmp$text$x)/2, tmp$text$y, ms.Cleveland, num.pt=1:5,
symb.plots=TRUE)
The ms.Cleveland function plots the points according to a number (1 to 5) or name and uses overplotting for the last 2 points (may want to tweak the functions a bit).
Then the legend is drawn using the regular legend function, but with col=NA there is place for the symbols, but they are not drawn. Then my.symbols is used again to place the symbols within the legend at the point halfway between the left edge of the box and the start of the text.
I used this code to make this plot:
plot(p, cv2,col=rgb(0,100,0,50,maxColorValue=255),pch=16,
panel.last=abline(h=67,v=1.89, lty=1,lwd=3))
My plot looks like this:
1.) How can I plot the value of the ablines in a simple plot?
2.) How can I scale my plot so that both lines appear in the middle?
to change scale of plot so lines are in the middle change the axes i.e.
x<-1:10
y<-1:10
plot(x,y)
abline(a=1,b=0,v=1)
changed to:
x<-1:10
y<-1:10
plot(x,y,xlim=c(-30,30))
abline(a=1,b=0,v=1)
by "value" I am assuming you mean where the line cuts the x-axis? Something like text? i.e.:
text((0), min(y), "number", pos=2)
if you want the label on the x axis then try:
abline(a=1,b=0,v=1)
axis(1, at=1,labels=1)
to prevent overlap between labels you could remove the zero i.e.:
plot(x,y,xlim=c(-30,30),yaxt="n")
axis(2, at=c(1.77,5,10,15,20,25))
or before you plot extend the margins and add the labels further from the axis
par(mar = c(6.5, 6.5, 6.5, 6.5))
plot(x,y,xlim=c(-30,30))
abline(a=1,b=0,v=1)
axis(2, at=1.77,labels=1.77,mgp = c(10, 2, 0))
Similar in spirit to the answer proposed by #user1317221, here is my suggestion
# generate some fake points
x <- rnorm(100)
y <- rnorm(100)
# positions of the lines
vert = 0.5
horiz = 1.3
To display the lines at the center of the plot, first compute the horizontal and vertical distances between the data points and the lines, then adjust the limits adequately.
# compute the limits, in order for the lines to be centered
# REM we add a small fraction (here 10%) to leave some empty space,
# available to plot the values inside the frame (useful for one the solutions, see below)
xlim = vert + c(-1.1, 1.1) * max(abs(x-vert))
ylim = horiz + c(-1.1, 1.1) * max(abs(y-horiz))
# do the main plotting
plot(x, y, xlim=xlim, ylim=ylim)
abline(h=horiz, v=vert)
Now, you could plot the 'values of the lines', either on the axes (the lineparameter allows you to control for possible overlapping):
mtext(c(vert, horiz), side=c(1,2))
or alternatively within the plotting frame:
text(x=vert, y=ylim[1], labels=vert, adj=c(1.1,1), col='blue')
text(x=xlim[1], y=horiz, labels=horiz, adj=c(0.9,-0.1), col='blue')
HTH
I know I can create a plot with line and dots using the type = "o" argument in the plot command. I would like some more control over this -- I want to be able to draw the "o" as full dots, with black border and fill-in color of my choice, of customized size and of a different color than the line. Same for the line, I want to make it thicker, and of my choice of color. How would I go on about doing that?
What I found until now is just a plain
plot(y, type= "o")
which is too poor for my needs.
I am not interested in using ggplot, but instead use the internal plot library of R.
Any help appreciated.
All the information you need should be present in ?plot and ?points, as suggested by #BenBolker. In particular, you want to be using pch=21, and specifying background colour with the bg argument, size with cex, and line width with lwd.
If you want the line to be a different thickness to the point borders, you need to plot the line first, and then overlay the points.
For example:
y <- sample(10)
plot(y, lwd=6, type='l')
points(y, bg='tomato2', pch=21, cex=3, lwd=3) # tomato2 is a personal fave
You could also provide a vector of lwd, cex and col to the points call:
plot(y, lwd=6, type='l')
points(y, bg=rainbow(10), pch=21, cex=seq(1, by=0.2, length.out=10),
lwd=seq(2, by=1, length.out=10))
You could use layering (I don't work in base too much any more as a social researcher I love the facet_grid of ggplot, so there may be a better way) as in:
x <- sort(rnorm(25))
y <- sort(rnorm(25))
z <- as.factor(sample(LETTERS[1:5], 25, r=TRUE))
plot(x, y, pch = 19, cex = 1.3)
par(new = TRUE)
plot(x, y, pch = 19, cex = 1, col = z)
Which gives you:
How do I change the background color for a plot, only for a specific area?
For example, from x=2 to x=4?
Bonus question: is it also possible for a combination of x and y coordinates? (for example from (1,2) to (3,4))?
Many thanks!
This can be achieved by thinking about the plot somewhat differently to your description. Basically, you want to draw a coloured rectangle between the desired positions on the x-axis, filling the entire y-axis limit range. This can be achieved using rect(), and note how, in the example below, I grab the user (usr) coordinates of the current plot to give me the limits on the y-axis and that we draw beyond these limits to ensure the full range is covered in the plot.
plot(1:10, 1:10, type = "n", axes = FALSE) ## no axes
lim <- par("usr")
rect(2, lim[3]-1, 4, lim[4]+1, border = "red", col = "red")
axis(1) ## add axes back
axis(2)
box() ## and the plot frame
rect() can draw a sequence of rectangles if we provide a vector of coordinates, and it can easily handle the case for the arbitrary x,y coordinates of your bonus, but for the latter it is easier to avoid mistakes if you start with a vector of X coordinates and another for the Y coordinates as below:
X <- c(1,3)
Y <- c(2,4)
plot(1:10, 1:10, type = "n", axes = FALSE) ## no axes
lim <- par("usr")
rect(X[1], Y[1], X[2], Y[2], border = "red", col = "red")
axis(1) ## add axes back
axis(2)
box() ## and the plot frame
You could just as easily have the data as you have it in the bonus:
botleft <- c(1,2)
topright <- c(3,4)
plot(1:10, 1:10, type = "n", axes = FALSE) ## no axes
lim <- par("usr")
rect(botleft[1], botleft[2], topright[1], topright[2], border = "red",
col = "red")
axis(1) ## add axes back
axis(2)
box() ## and the plot frame