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)
Related
I would like to plot something like this (from this paper) where icons, in this case small graphs, are used as tick labels.
I get this far, where icons are more or less properly placed:
This is the code:
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
par(fig=c(0.05,1,0.3,1), new=FALSE)
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
xspan <- 0.9
xoffset <- (0.07+0.5/npoints)*xspan
for(i in 1:npoints){
x1 <- (xoffset+(i-1)/npoints)*xspan
x2 <- min(xspan*(xoffset+(i)/npoints),1)
par(fig=c(x1,x2,0,0.5), new=TRUE)
plot(graph.ring(i), vertex.label=NA)
}
However, if the number of points grows (e.g. npoints <- 15) it complains because there is no place for the icons:
Error in plot.new() : figure margins too large
I wonder wether there is a more natural way to do this so that it works for any (reasonable) number of points.
Any advice is welcome.
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
# reserve some extra space on bottom margin (outer margin)
par(oma=c(3,0,0,0))
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
# graph numbers
x = 1:npoints
# add offset to first graph for centering
x[1] = x[1] + 0.4
x1 = grconvertX(x=x-0.4, from = 'user', to = 'ndc')
x2 = grconvertX(x=x+0.4, from = 'user', to = 'ndc')
# abline(v=1:npoints, xpd=NA)
for(i in x){
print(paste(i, x1[i], x2[i], sep='; '))
# remove plot margins (mar) around igraphs, so they appear bigger and
# `figure margins too large' error is avoided
par(fig=c(x1[i],x2[i],0,0.2), new=TRUE, mar=c(0,0,0,0))
plot(graph.ring(i), vertex.label=NA)
# uncomment to draw box around plot to verify proper alignment:
# box()
}
the type argument to xyplot() can take "s" for "steps." From help(plot):
The two step types differ in their x-y preference: Going from
(x1,y1) to (x2,y2) with x1 < x2, 'type = "s"' moves first
horizontal, then vertical, whereas 'type = "S"' moves the other
way around.
i.e. if you use type="s", the horizontal part of the step has its left end attached to the data point, while type="S" has its right end attached to the data point.
library(lattice)
set.seed(12345)
num.points <- 10
my.df <- data.frame(x=sort(sample(1:100, num.points)),
y=sample(1:40, num.points, replace=TRUE))
xyplot(y~x, data=my.df, type=c("p","s"), col="blue", main='type="s"')
xyplot(y~x, data=my.df, type=c("p","S"), col="red", main='type="S"')
How could one achieve a "step" plot, where the vertical motion happens between data points points, i.e. at x1 + (x2-x1)/2, so that the horizontal part of the step is centered on the data point?
Edited to include some example code. better late than never I suppose.
I am using excellent #nico answer to give its lattice version. Even I am ok with #Dwin because the question don't supply a reproducible example, but customizing lattice panel is sometimes challenging.
The idea is to use panel.segments which is the equivalent of segments of base graphics.
library(lattice)
xyplot(y~x,
panel =function(...){
ll <- list(...)
x <- ll$x
y <- ll$y
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
panel.segments(x.start, y, x.end, y, col="orange", lwd=2)
panel.segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
## this is optional just to compare with type s
panel.xyplot(...,type='s')
## and type S
panel.xyplot(...,type='S')
})
This is a base graphics solution, as I am not too much of an expert in lattice.
Essentially you can use segments to draw first the horizontal, then the vertical steps, passing the shifted coordinates as a vector.
Here is an example:
set.seed(12345)
# Generate some data
num.points <- 10
x <- sort(sample(1:100, num.points))
y <- sample(1:40, num.points, replace=T)
# Plot the data with style = "s" and "S"
par(mfrow=c(1,3))
plot(x, y, "s", col="red", lwd=2, las=1,
main="Style: 's'", xlim=c(0, 100))
points(x, y, pch=19, col="red", cex=0.8)
plot(x, y, "S", col="blue", lwd=2, las=1,
main="Style: 'S'", xlim=c(0, 100))
points(x, y, pch=19, col="blue", cex=0.8)
# Now plot our points
plot(x, y, pch=19, col="orange", cex=0.8, las=1,
main="Centered steps", xlim=c(0, 100))
# Calculate the starting and ending points of the
# horizontal segments, by shifting the x coordinates
# by half the difference with the next point
# Note we leave the first and last point as starting and
# ending points
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
# Now draw the horizontal segments
segments(x.start, y, x.end, y, col="orange", lwd=2)
# and the vertical ones (no need to draw the last one)
segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
Here is the result:
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 have a density plot graphed using:
plot(density(x))
What I am interested in doing is creating a line for something like x = 5 from the x-axis to the corresponding spot on the curve.
Like this:
You can do this by first storing the density values in an object, and then retrieving the x and y elements from this object. In the following example I use findInterval to retrieve the y-value for the given x-value:
x <- rnorm(1000) # Sample data
y <- density(x) # Calculate and store density
x0 <- 2 # Desired position on x-axis
y0 <- y$y[findInterval(x0, y$x)] # Corresponding y value
plot(density(x))
segments(x0, 0, x0, y0)
If it's really z-scores then just actually plot the density function dnorm(). It also looks like you'd like to have your actual x-axis at 0.
zmax <- 4
curve(dnorm, -zmax, zmax, xaxt = 'n', bty = 'n')
axis(1, -zmax:zmax, pos = 0)
To draw in your line you can use the dnorm function again.
zscore <- 1.65
segments(zscore, 0, zscore, dnorm(zscore))
You could also even shade it out nicely... :)
x <- seq(zscore, zmax, 0.01)
y <- c(0, dnorm(x)[1:(length(x)-2)],0)
polygon(x,y, density = 20)
You can also use segments and the text command to put the labels right on your graph of what the shaded and unshaded areas mean.
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)