I am trying to add arrows marking specific x coordinates below the x axis in an R plot. My x axis is at y=0 and when I try to use negative y-coordinates in arrows, so the arrows will be perpendicular to x axis, I get only the very edges of the arrow plotted (although is some space, e,g where the x-axis label and tickmarks are plotted).
The xpd option can be used in arrows so you can just set your coordinates to be outside your plot region and set xpd to TRUE. For example, assuming xlim = c(0,10) and ylim = (0,10), and you set the x-axis to 0 then
arrows(1.4, -1, 1.4, 0, xpd = TRUE)
draws a vertical arrow pointing up at the x-axis at position 1.4 on that axis.
You can do this by adding an extra overlay, by calling par(new=TRUE), with reduced margins. For example:
plot(1,1) ## start a plot
opar <- par(new = TRUE, ## add a new layer
mar = c(0,0,0,0)) ## with no margins margins
## set up the plotting area for this layer
plot(1,1,xlim=c(0,1),ylim=c(0,1),type='n',xlab='',ylab='')
arrows(0.1,0.05,0.5,0.05) ## add arrow
par(opar) ## return the plot parameters to their prior values
Edit: If you want to keep the same coordinates as in the original plot, you have to choose the x- and y-axis limits carefully. This is illustrated belo:
plot(1,1,xlim=0:1,ylim=0:1)
arrows(0.1,0.05,0.5,0.05)
gpar <- par()
opar <- par(new = TRUE, mar = c(0,0,0,0),xaxs='i',yaxs='i')
m1 <- (gpar$usr[2] - gpar$usr[1])/(gpar$plt[2] - gpar$plt[1])
c1 <- gpar$usr[1] - m1*gpar$plt[1]
m2 <- (gpar$usr[4] - gpar$usr[3])/(gpar$plt[4] - gpar$plt[3])
c2 <- gpar$usr[3] - m2*gpar$plt[3]
xlim <- c(c1, m1 + c1)
ylim <- c(c2, m2 + c2)
plot(1,1,xlim=xlim,ylim=ylim,type='n',xlab='',ylab='')
arrows(0.1,0.05,0.5,0.05,col='red')
points(1,1,col='red')
par(opar)
Related
I am trying to draw a plot in R that its x axis and y axis have an arrow in their tips. I googled it and did not found anything helpful. I found it how to do it in Python in this page.
I want something like this picture:
My attempt:
I remove the plot frame and add axis separately.
x=seq(0,5,by=.1)
y=x*x
plot(x, y, axes = FALSE, frame.plot = FALSE, type="l")
box(bty="l")
axis(1)
axis(2)
Function Arrowhead from package shape draws arrowhead-shaped polygons centered at user-supplied points. To draw arrowheads at the top left and bottom right corners of the plot region pointing in the direction of your axes, you can do:
usr <- par("usr")
shape::Arrowhead(
x0 = usr[1:2],
y0 = usr[4:3],
angle = c(90, 0),
xpd = TRUE
)
where par("usr") is a vector c(left, right, bottom, top) specifying the boundary of the plot region in user coordinates. xpd = TRUE ensures that arrowheads are not clipped to the plot region.
If you want to avoid installing a package, then note that base R has arrows, but it does not draw filled arrowheads and it complains if you ask for a zero-length arrow (i.e., an arrow with a head but no shaft). One way around the latter issue is to draw arrows that are collinear with the axes:
usr <- par("usr")
arrows(
x0 = usr[1L],
x1 = usr[1:2],
y0 = usr[3L],
y1 = usr[4:3],
length = 0.1,
angle = 20,
xpd = TRUE
)
As you might expect, both functions have optional arguments that you can use to adjust the appearance of the arrow(head)s.
I am trying to display barchart overlayed with line plot on secondary y-axis. I was following example here: http://robjhyndman.com/hyndsight/r-graph-with-two-y-axes/. I successfully display my data, however the beginning of the y1 and y2 axis do not start on the common base (on the common 0), the y2 is located further up.
How to correctly align y1 and y2 axes on the common basis? Can I extent both of my y1 and y2 axis in the same size? And, how can I adjust the position of the points in the middle of the bars?
My dummy data:
x <- 1:5
y1 <- c(10,53,430,80,214)
y2 <- c(0.2,1.2,3.3, 3.5, 4.2)
# create new window
windows()
# set margins
par(mar=c(5,4,4,5)+.1)
# create bar plot with primary axis (y1)
barplot(y1, ylim= c(0,500))
mtext("y1",side=2,line=3)
# add plot with secondary (y2) axis
par(new=TRUE)
plot(x, y2,,type="b",col="red",xaxt="n",yaxt="n",xlab="",ylab="", ylim= c(0,10), lwd = 2, lty = 2, pch = 18)
axis(4)
mtext("y2",side=4,line=3)
When you check the documentation for par() you will find the options xaxsand yaxs with which you can control the interval calculation for both axes. Calling par(yaxs = 'i') prior to your plot() command or using the option directly as an argument to plot() will change the interval calculation in the following way:
Style "i" (internal) just finds an axis with pretty labels that fits
within the original data range.
Additional information for the TO concerning his comment:
In order to center the points of the line go with lines instead and you can use the x-axis created by barplot:
par(mar=c(5,4,4,5)+.1)
# create bar plot with primary axis (y1)
par(xpd = F)
ps <- barplot(y1, ylim= c(0,500), xpd = F)
axis(4, at = 0:5 * 100, labels = 0:5 * 2) # transform values
mtext('y1',side = 2, line = 3)
lines(x = ps, y = y2 * 50, type = 'b', col = 'red') # transform values
By default, the cartesian axes in R are on the bottom and left side of a plot.
How do I center the axes, as shown in the picture below?
Example
## using; data; generated; by; bgoldst;;
## estimate curve
x <- seq(-1,1.5,0.1);
y <- c(1.3,1.32,1.33,1.32,1.25,1.1,0.7,0.5,0.4,0.38,0.4,0.41,0.42,0.43,0.44,0.4,0.3,0.1,0,-0.05,-0.1,-0.15,-0.2,-0.24,-0.28,-0.3);
f <- splinefun(x,y);
## calculate precise points along estimated curve
x <- seq(-1,1.5,0.01);
y <- f(x);
plot(x, y, type = 'l')
#ForrestRStevens was too quick for me, I was too busy trying to estimate your curve using a spline :)
## estimate curve
x <- seq(-1,1.5,0.1);
y <- c(1.3,1.32,1.33,1.32,1.25,1.1,0.7,0.5,0.4,0.38,0.4,0.41,0.42,0.43,0.44,0.4,0.3,0.1,0,-0.05,-0.1,-0.15,-0.2,-0.24,-0.28,-0.3);
f <- splinefun(x,y);
## calculate precise points along estimated curve
x <- seq(-1,1.5,0.01);
y <- f(x);
## precompute limits
xlim <- c(min(x),max(x));
ylim <- c(min(y)-0.4,max(y)+0.2);
## set global plot params
par(xaxs='i',yaxs='i',mar=c(1,1,3,3)+0.1); ## "internal" axis spacing, meaning no extended range, and slightly adjust margins
## draw plot
plot(NA,xlim=xlim,ylim=ylim,axes=F,ann=F); ## set plot bounds, no default ornaments
arrows(c(0,xlim[1]),c(ylim[1],0),c(0,xlim[2]),c(ylim[2],0),0.05); ## draw custom axes
mtext('y',3,1,at=0,las=1,cex=0.8,family='serif'); ## y label
mtext('x',4,1,at=0,las=1,cex=0.8,family='serif'); ## x label
lines(x,y,col='#aaaacc'); ## draw line on top
In general, you can draw pretty much anything with base graphics, but it's often more involved than if you used more sophisticated packages, because you have to draw everything by hand.
I think something like the following does what you'd like in base graphics:
## Simulate your data:
x <- seq(-3, 3, by=0.01)
y <- 0.5*x - 0.3*x^2 + 0.4*x^3
## Plot the polynomial function, removing axis ticks and bounding box,
## as well as the axis labels:
plot(x, y,
type="l",
xaxt='n', yaxt='n',
bty='n',
xlab='', ylab='',
col="blue")
## Next add in your axis arrows:
arrows(min(x), 0, max(x), 0, lwd=1, length=0.15)
arrows(0, min(y), 0, max(y), lwd=1, length=0.15)
## And plot your x/y labels. Note that if you want them
## actually at the end of the arrows you would need to
## remove the pos= argument and shorten your arrows by
## a small amount. To match your original figure, you can
## alter the x/y coordinate to be the max() instead.
text(0, min(y), "y", pos=2)
text(min(x), 0, "x", pos=3)
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
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