I would like to create a polar/radar style graph in R that only shows a partial arc instead of a full circle.
I have looked at both the Plotrix and Pracma packages, but I've only been able to create a full circle polar chart like what I'm displaying here (created this is Pracma)
What I would like to do is create a 1/4 circle arc in the 0 to 270 degrees range of a polar arc chart and then plot data points that would radiate from the center towards the outer edges to indicate relative popularity. Data would indicate increasingly lower popularity/adoption rates as it approached the outer ring.
We create a polar plot layout from scratch in base graphics:
## Fake data
set.seed(493)
dat = data.frame(theta=runif(20, 3*pi/2, 2*pi), r=sample(1:10, 20, replace=TRUE))
# Calculate x and y values from r and theta
dat$x = dat$r * cos(dat$theta)
dat$y = dat$r * sin(dat$theta)
## 1:1 aspect ratio for plot
par(pty="s")
## Create plot layout
plot(NA,NA,
xlim=c(0, ceiling(max(c(dat$x,dat$y)))),
ylim=c(-ceiling(max(c(dat$x,dat$y))),0),
frame.plot=FALSE,
xaxt="n", yaxt="n", xlab="", ylab="")
## Add axes
axis(3, pos=0, at=seq(0,10,1))
axis(2, pos=0, at=seq(-10,0,1), las=1, labels=c(10:0))
## Add grid lines
angle = seq(0, -pi/2, length.out=100)
invisible(lapply(1:10, function(r) {
lines(r*cos(angle), r*sin(angle), lwd=0.5, col="grey40", lty="12")
}))
## Add data
invisible(lapply(1:nrow(dat), function(i) {
lines(c(0,dat$x[i]), c(0,dat$y[i]), col="blue")
}))
Original Answer
Using ggplot2:
library(ggplot2)
set.seed(493)
dat = data.frame(x=runif(20, 3*pi/2, 2*pi), value=sample(1:10, 20, replace=TRUE))
ggplot(dat, aes(x=x, xend=x, y=0, yend=value)) +
geom_segment(x=3*pi/2, xend=2*pi, y=0, yend=0 ,color="red") +
geom_segment(color="blue") +
scale_y_continuous(limits=c(-2,10), breaks=seq(0,10,2)) +
scale_x_continuous(limits=c(0,2*pi), breaks=seq(0, 2*pi, length.out=13)[-13],
labels=seq(0,360,length.out=13)[-13]) +
coord_polar(start=-pi/2, direction=-1)
Related
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)
The goal is to reproduce this Bid-Rent graph in R:
The challenge is to draw the projected circles. So far I got:
The 2D part is created by the R code below with the traditional graphic system in base R:
#Distance
X <- seq(0,7,1)
#Bid Rent Curves: Commercial, Industrial, Residential
com <- -5*X + 10
ind <- -2*X + 7
res <- -0.75*X + 4
graph <- plot(X, com, type="l", col="green", ylim=c(0,10), xlab="", ylab="", axes=FALSE)
lines(X, ind, col="red")
lines(X, res, col="blue")
abline(v=0, h=0)
segments(1,0, 1,5, lty=2)
segments(2.5,0, 2.5,2, lty=2)
title(main="Bid Rent Curves", sub="Alonso Model",
xlab="Distance from CBD", ylab="Rent per m2")
text(2.5,7.5, "Commercial", col="green")
text(3.5,4, "Industrial", col="red")
text(5.5,2, "Residential", col="blue")
(Detail: Why the curves do not respect the ylim = 0 ?)
How make the projection and draw the semi-circles?
It is not exactly a 3D plot. I have looked into plot3D and rgl. I am not sure which packages or strategy to use from here.
I'm taking you at your word that you want circles, so you need to push the plot area into the upper right corner:
outHalfCirc <- function(r,colr) {opar=par(xpd=TRUE, new=TRUE) #plot ouside plot area
polygon(x=seq(r,-r,by=-0.1),
y= -sqrt(r^2 - seq(r,-r,by=-0.1)^2) , # solve r^2 = x^2 +y^2 for y
xlim =c(0,7 ), ylim=c(0,10), col=colr, # need xlim and ylim to match base plot ranges
yaxs="i", yaxt="n", xaxs="i") # yaxis off; x and y axes meet at origin
par(opar)}
Then push plot up and to the right: This will draw a colored half-circles (largest first so they overlap) below the y=0 line.
png() # send to image file; not needed for testing
opar <- par(mar=c(15, 15, 2,2) ) # default units are in widths of text-"line".
# the margins start at lower, then clockwise
# run your code
outHalfCirc(5.5, "blue")
outHalfCirc(2.5, "red")
outHalfCirc(1, "green")
dev.off() # complete image production
par(opar) # different than the 'opar' inside the function
Voila! Although not really circles because the aspect ratio is not 1. That can be fixed (or you could set the xlim and ylim to be equal.
I'm working on a 3d graph with the package scatterplot3d,
but I need another dimension to the graph.
I would like to increase the size of each point inside the scatter plot, according to a numeric vaule of a fourth variable.
IS IT POSSIBLE?
my code for the plot is:
library(scatterplot3d)
with(masad, {
s3d <- scatterplot3d(HLC, cites, E.publications,
color=title, pch=19,
angle=20,
main="English",
xlim=c(0,1),
xlab="HLC",
ylab="cites",
zlab="E.publications")
s3d.coords <- s3d$xyz.convert(HLC, cites, E.publications)
legend("topright", inset=.10,
bty="n", cex=.5,
title="title",
c("none/else","doctor","professor") , fill=c("red",
"blue", "darkgreen"))
})
Here's one way to do it
library(scatterplot3d)
library(plotrix) # for rescale()
with(mtcars,
scatterplot3d(disp,
hp,
wt,
cex.symbol = rescale(qsec, c(.5, 5)), # .5 <= size <= 5
color = "#00000066", # overplotting, so add transparency
pch = 19)
)
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 would like to make a plot with 4 axes in R so that it looks similar to this plot:
I've looked at the Quick-R website for advice and modified one of their examples (called A Silly Axis Example):
# specify the data
x <- c(1:5); y <- x/2;
w <- c(2:4)
z <- c(1:5)
# create extra margin room on the right for an axis
par(mar=c(5, 4, 4, 8) + 0.1)
# plot x vs. y
plot(x, y,type="b", pch=21, col="red",
yaxt="n", lty=3, xlab="", ylab="")
# add x vs. 1/x
lines(x, z, type="b", pch=22, col="blue", lty=2)
# draw an axis on the left
axis(2, at=x,labels=x, col.axis="red", las=2)
# draw an axis on the right, with smaller text and ticks
axis(4, at=w,labels=round(w,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
# draw an axis on the top
axis(3, at=z,labels=round(z,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
# add a title for the right axis
mtext("L", side=3, line=3, cex.lab=1,las=2, col="blue")
# add a title for the right axis
mtext("OSR", side=4, line=3, cex.lab=1,las=2, col="red")
# add a main title and bottom and left axis labels
title("", xlab="GSI", ylab="FSI")
This code produces the following plot:
I'm having difficulty figuring out how different axes can have different scales. For example, I want the top axis L, to go from 5 - 13, but if I set z <-c(5:13) it will not set the axis to these values. However, I can overwrite what the labels are:
axis(3, at=z,labels=round(c(9:13),digits=2), col.axis="blue",
las=2, cex.axis=0.7, tck=-.01)
but then if I want to plot a point with these four parameters, the point will not show up in the correct place. How should I do this?
One (perhaps cumbersome) option would be to write conversion functions that transform values between your two scales. Assuming you know the data ranges for both the top and bottom axes ahead of time, you could write a function like this:
convertScaleToBottom <- function(x,botRange,topRange){
temp <- (x - topRange[1]) / (topRange[2] - topRange[1])
return(botRange[1] + (temp * (botRange[2] - botRange[1])))
}
that takes a set of values, x, in the top axis scale and converts them to the bottom axis scale. Then you can plot the converted values and retain the originals as the labels:
z1 <- 5:13
z1Adj <- convertScaleToBottom(z1,range(x),range(z1))
# draw an axis on the top
axis(3, at=z1Adj,labels=round(z1,digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01)
This method is easily modified to reverse the order of the top axis:
convertScaleToBottomRev <- function(x,botRange,topRange){
temp <- (x - topRange[1]) / (topRange[2] - topRange[1])
return(botRange[2] - (temp * (botRange[2] - botRange[1])))
}