Barplot -adding percentages - r

So i have this numeric variables which reflect percentages
data1.pct<-19
data2.pct<-5
data3.pct<-76
class1.pct<-35
class2.pct<-18
class3.pct<-47
Now i am using this code to generate barplot
CairoPDF(paste('data1/', data, '_plot1.pdf', sep=''), family='sans', pointsize=9, width=6, height=3.25)
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
barplot(cbind(
c(data1.pct, data2.pct, data3.pct),
c(class1.pct, class2.pct, class3.pct)), col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1)
)
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')
dev.off()
and the result is
I would like to add corresponding percentages inside barplot, that is numbers/percentages in my variables. So My output should be:
I would like to use barplot funcion to do this and NOT ggplot2
I have tried adding percentages with
text(mydata, 0, round(data1.pct), 1),cex=1,pos=3) but this is not right.

To get the y-values for the text, you can use cumsum along with tail and head to get the midpoints of each bar section.
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
## Make the matrix for barplot
mat <- cbind(c(data1.pct, data2.pct, data3.pct), c(class1.pct, class2.pct, class3.pct))
## Get the y-values for text
ys <- apply(mat, 2, function(x) c(x[1]/2, head(cumsum(x),-1) + tail(x,-1)/2))
## Make barplot, store x data
xs <- barplot(mat, col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1))
## Add text
text(rep(xs, each=nrow(ys)), c(ys), labels=c(mat))
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')

Related

adding horizontal "separating" lines in a single boxplot in Base R plotting

I need to add a "separating" line in Base R boxplot to separate difference groups. In the example below, I want to separate groups A and B (each having 2 levels) using a horizontal line (in red). R codes for reproducible results:
dat = data.frame(A1 = rnorm(1000, 0, 1), A2 = rnorm(1000, 1, 2),
B1 = rnorm(1000, 0.5, 0.5), B2 = rnorm(1000, 1.5, 1.5))
boxplot(dat, horizontal = T, outline=F)
Is there an easy way to do in Base R?
Also, is there an easy way to color the y-axis labels? I want to have A1 and B1 shown as red, and A2 and B2 shown as blue in the axis.
Thanks!
Use abline. To get the right position take the mean of the axTicks of the y-axis.
To get the colored labels, first omit yaxt and rebuild axis ticks and mtext, also using axTicks.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n")
ats <- axTicks(2)
axis(2, labels=F)
mtext(b$names, 2, 1, col=c(2, 4), at=ats)
abline(h=mean(ats), lwd=2, col=2)
If you want axis tick label colors corresponding to the labels, use segments instead.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n")
ats <- axTicks(2)
abline(h=mean(ats), lwd=2, col=2)
pu <- par()$usr
Map(function(x, y) segments(pu[1] - .2, x, pu[1], x, xpd=T, col=y), ats, c(2, 4))
mtext(b$names, 2, 1, col=c(2, 4), at=ats)
Edit: To adjust the space a little more use at=option in boxplot and leave out the middle axTicks.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n", at=c(1, 2, 4, 5))
ats <- axTicks(2)[-3]
abline(h=mean(ats), lwd=2, col=2)
pu <- par()$usr
Map(function(x, y) segments(pu[1] - .2, x, pu[1], x, xpd=T, col=y), ats, c(2, 4))
mtext(b$names, 2, 1, col=c(2, 4), at=ats)

R base plotting, arrange multiple plots

I am working in RStudio and trying to make a 3x3 grid of the triangle plots built with the functions below. I’ve included a reproducible example, and the error I am running into is that the margins are too large to plot multiple plot, even though I am reducing the width and height.
I’ve also tried saving these as png and loading them in to arrange with cowplot, but the figure is very blurry and I’m not sure how to adjust the text size or line thickness to make the figures more legible.
#Data
iris$nrm.Sepal <- iris$Sepal.Width / iris$Sepal.Length
iris$nrm.Petal <- iris$Petal.Width / iris$Petal.Length
df_list <- split(iris, (iris$Species))
top.triangle <- function() {
plot(my.y ~ my.x, data= my.data, axes=FALSE, ylab='', xlab="",
main='', xlim=c(0, 1), ylim=c(0, 1), xaxt="n", yaxt="n", asp=1)
mtext("Here could be your title", 3, 5, font=2, cex=1.3, adj=.95)
mtext("Position.2", 2, .75)
mtext("Position.1", 3, 2)
axis(side=2, las=1, pos=0)
axis(side=3, las=1, pos=1)
lines(0:1, 0:1)
}
bottom.triangle <- function() {
points(my.x ~ my.y, data=my.data.2, xpd=TRUE)
mtext("Position.2", 1, 1.5, at=mean(par()$usr[1:2]) + x.dist)
mtext("Position.1", 4, 3, padj=par()$usr[1] + 10)
x.at <- axisTicks(par()$usr[1:2], 0) + x.dist
axis(side=1, las=1, pos=0, at=x.at,
labels=F, xpd=TRUE)
mtext(seq(0, 1, .2), 1, 0, at=x.at)
axis(4, las=1, pos=1 + x.dist)
lines(0:1 + x.dist, 0:1, xpd=TRUE)
}
#loop for generating species specific plots
for(i in 1:(length(df_list))){
current.strain <- as.character(df_list[[i]]$Species[1])
#declare file for saving png
# png(paste0("~.test.triangle_", current.strain, ".png"), width=650, height=500)
plot.new()
my.data = iris
my.x.top = (iris %>% filter(Species == current.strain) )$nrm.Petal
my.y.top = (iris %>% filter(Species == current.strain) )$nrm.Sepal
my.x.bottom = (iris %>% filter(Species == current.strain) )$nrm.Petal
my.y.bottom = (iris %>% filter(Species == current.strain) )$nrm.Sepal
op <- par(mar=c(3, 2, 2, 2) + 0.1, oma=c(2, 0, 0, 2))
top.triangle(my.y.top, my.x.top, my.data)
bottom.triangle(my.y.bottom+x.dist, my.x.bottom, my.data)
par(op)
RP[[i]] <- recordPlot()
dev.off()
}
#for margins too large error
graphics.off()
par("mar")
par(mar=c(.1,.1,.1,.1))
#draw and arrange the plots
ggdraw() +
draw_plot(RP[[1]], x=0, y=0)
#Add remaining plots
#draw_plot(RP[[2]], x=.25, y=.25)
#draw_plot(RP[[3]], x=.25, y=.25)
(this is built off the answer I posted from this question, R base plot, combine mirrored right triangles )
To use plot solution at specified link, you need to adjust to the iris data including your calculated columns, nrm.Sepal and nrm.Petal inside both functions. Then, instead of split, consider by to pass subsets into both functions for plotting. However, the plot will only generate 1 X 3. It is unclear how 3 X 3 is generated. Your posted link above actually duplicates
Data
iris$nrm.Sepal <- iris$Sepal.Width / iris$Sepal.Length
iris$nrm.Petal <- iris$Petal.Width / iris$Petal.Length
Functions
top.triangle <- function(my.data) {
plot(nrm.Sepal ~ nrm.Petal, data= my.data, axes=FALSE, ylab="", xlab="",
main='', xlim=c(0, 1), ylim=c(0, 1), xaxt="n", yaxt="n", asp=1)
mtext(my.data$Species[[1]], 3, 5, font=2, cex=1.3, adj=.95)
mtext("Position.2", 2, .75)
mtext("Position.1", 3, 2)
axis(side=2, las=1, pos=0)
axis(side=3, las=1, pos=1)
lines(0:1, 0:1)
}
bottom.triangle <- function(my.data) {
x.dist <- .5
my.data.2 <- transform(my.data, nrm.Sepal=nrm.Sepal + x.dist)
points(nrm.Petal ~ nrm.Sepal, data=my.data.2, col="red", xpd=TRUE)
mtext("Position.2", 1, 1.5, at=mean(par()$usr[1:2]) + x.dist)
mtext("Position.1", 4, 3, padj=par()$usr[1] + 3)
x.at <- axisTicks(par()$usr[1:2], 0) + x.dist
axis(side=1, las=1, pos=0, at=x.at,
labels=FALSE, xpd=TRUE)
mtext(seq(0, 1, 0.2), 1, 0, at=x.at, cex=0.7)
axis(4, las=1, pos=1 + x.dist)
lines(0:1 + x.dist, 0:1, xpd=TRUE)
}
Plot
par(mar=c(1, 4, 8, 6), oma=c(2, 0, 0, 2), mfrow=c(2,3))
by(iris, iris$Species, function(sub){
top.triangle(sub)
bottom.triangle(sub)
})

Eliminating dead space from a plot consisting of two pie charts

I have this plot which I want to save as PDF.
pdf(file="pie_charts.pdf", width=8, height=5, onefile=F)
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0,0,0,0), xpd=TRUE)
pie(c(1,9),col=c("black","white"))
pie(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
dev.off()
And this is what I am getting
It looks quite good but I want to eliminate as much empty space as possible between the individual pie charts as well as between them and the legend. Any suggestions?
With layout(), I think you may be a bit limited with changing margins to squeeze the pie charts together.
This isn't an elegant solution but it works. I went in to the pie() function and modified the xlim arguments. This was my only change.
In other words, pie has this inside its function:
xlim <- ylim <- c(-1, 1)
Change the xlim to shift the pie charts left or right.
I made mypieleft() and mypieright().
mypieleft<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-1.20, 0.80)
ylim <- c(-1, 1)
[untouched code from pie]
}
and
mypieright<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-0.75, 1.25)
ylim <- c(-1, 1)
[untouched code from pie]
}
Then change your code slightly:
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(oma=c(0,0,0,0), xpd=TRUE)
mypieleft(c(1,9),col=c("black","white"))
mypieright(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
I get this image.
Just increase the radius of the pies:
layout(matrix(c(1, 2, 3, 3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0, 1, 0, 0)) # increase left margin to accommodate text
pie(c(1, 9), col=c("black","white"), radius=1)
par(mar=c(0, 0, 0, 1)) # increase right margin to accommodate text
pie(c(1, 3), col=c("black", "white"), radius=1)
plot.new()
legend(x="center", ncol=2, legend=c("Black", "White"),
fill=c("black", "white"), bty="n", cex=1.3)
See the radius arg at ?pie.

Gradient legend in base

Earlier I asked about creating a gradient of n values in base graphics (LINK). Now I'd like to create a gradient legend that goes with it. My ideal would be something like ggplot2's gradient legends:
Here's some code similar to what I'm working with:
colfunc <- colorRampPalette(c("red", "blue"))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
Here is an example of how to build a legend from first principles using rasterImage from grDevices and layout to split the screen
layout(matrix(1:2,ncol=2), width = c(2,1),height = c(1,1))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot(c(0,2),c(0,1),type = 'n', axes = F,xlab = '', ylab = '', main = 'legend title')
text(x=1.5, y = seq(0,1,l=5), labels = seq(0,1,l=5))
rasterImage(legend_image, 0, 0, 1,1)
Late to the party, but here is a base version presenting a legend using discrete cutoffs. Thought it might be useful for future searchers.
layout(matrix(1:2,nrow=1),widths=c(0.8,0.2))
colfunc <- colorRampPalette(c("white","black"))
par(mar=c(5.1,4.1,4.1,2.1))
plot(1:10,ann=FALSE,type="n")
grid()
points(1:10,col=colfunc(10),pch=19,cex=1.5)
xl <- 1
yb <- 1
xr <- 1.5
yt <- 2
par(mar=c(5.1,0.5,4.1,0.5))
plot(NA,type="n",ann=FALSE,xlim=c(1,2),ylim=c(1,2),xaxt="n",yaxt="n",bty="n")
rect(
xl,
head(seq(yb,yt,(yt-yb)/10),-1),
xr,
tail(seq(yb,yt,(yt-yb)/10),-1),
col=colfunc(10)
)
mtext(1:10,side=2,at=tail(seq(yb,yt,(yt-yb)/10),-1)-0.05,las=2,cex=0.7)
And an example image:
The following creates a gradient color bar with three pinpoints without any plot beforehand and no alien package is needed. Hope it is useful:
plot.new()
lgd_ = rep(NA, 11)
lgd_[c(1,6,11)] = c(1,6,11)
legend(x = 0.5, y = 0.5,
legend = lgd_,
fill = colorRampPalette(colors = c('black','red3','grey96'))(11),
border = NA,
y.intersp = 0.5,
cex = 2, text.font = 2)
As a refinement of #mnel's great answer, inspired from another great answer of #Josh O'Brien, here comes a way to display the gradient legend inside the plot.
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
## layer 1, base plot
plot(1:20, 1:20, pch=19, cex=2, col=colfunc(20), main='
Awesome gradient legend inside')
## layer 2, legend inside
op <- par( ## set and store par
fig=c(grconvertX(c(0, 10), from="user", to="ndc"), ## set figure region
grconvertY(c(4, 20.5), from="user", to="ndc")),
mar=c(1, 1, 1, 9.5), ## set margins
new=TRUE) ## set new for overplot w/ next plot
plot(c(0, 2), c(0, 1), type='n', axes=F, xlab='', ylab='') ## ini plot2
rasterImage(legend_image, 0, 0, 1, 1) ## the gradient
lbsq <- seq.int(0, 1, l=5) ## seq. for labels
axis(4, at=lbsq, pos=1, labels=F, col=0, col.ticks=1, tck=-.1) ## axis ticks
mtext(sq, 4, -.5, at=lbsq, las=2, cex=.6) ## tick labels
mtext('diff', 3, -.125, cex=.6, adj=.1, font=2) ## title
par(op) ## reset par

Adding multi-plot axes

In order to generate a layout with multiple plots, I have the following code with some dummy plots:
jpeg("/path/to/file",height=10000,width=5000)
plot.new()
par(mar=c(2,2,1,1), oma=c(2,4,0,0), xpd=NA)
for (i in 1:10) {
par(mar=c(2,2,1,1),fig=c(0, 0.5, (10-i)/10, (11-i)/10), new=T)
matplot(rnorm(20)*sample(100,1),
col="blue",axes=F,type="l",lwd=10, xlab="",ylab="")
par(mar=c(2,2,1,1),fig=c(0.5, 1, (10-i)/10, (11-i)/10), new=T)
matplot(rnorm(20)*sample(100,1),
col="red",axes=F,type="l",lwd=10, xlab="",ylab="")
}
dev.off()
I want to add a vertical line/axis on the far LHS and the far RHS that span all 10 plots in a column. Since I will use this line as an axis, I need to be able to add ticks and labels.
You could draw axis by ?axis or ?Axis. To span your axis on multiple plots you have to reset the usr coordinates.
Please find a base graphics solution below:
## store number of rows
nRow <- 10
## your example code
## (only the number "10" is replaced by nRow and oma is adapted)
plot.new()
par(mar=c(2, 2, 1, 1), oma=c(2, 4, 0, 4), xpd=NA)
for (i in 1:nRow) {
par(mar=c(2, 2, 1, 1), fig=c(0, 0.5, (nRow-i)/nRow, ((nRow+1)-i)/nRow), new=TRUE)
matplot(rnorm(20)*sample(100, 1),
col="blue", axes=F, type="l", lwd=10, xlab="", ylab="")
par(mar=c(2, 2, 1, 1), fig=c(0.5, 1, (nRow-i)/nRow, ((nRow+1)-i)/nRow), new=TRUE)
matplot(rnorm(20)*sample(100, 1),
col="red", axes=F, type="l", lwd=10, xlab="", ylab="")
}
## define new user coordinates
usr <- c(0, 1, 0, 1) ## x1, x2, y1, y2
## calculate tick positons
## in general: (usr[3]+(diff(usr[3:4])/(nRow-1))*0:(nRow-1))
## but our usecase is much easier:
ticksAt <- 1/(nRow-1)*0:(nRow-1)
## choose left column and reset user plotting area (usr)
par(mar=c(2, 2, 1, 1), fig=c(0, 0.5, 0, 1), usr=usr, new=TRUE)
## draw axis; see ?Axis for details
Axis(side=2, at=ticksAt, labels=as.character(1:(nRow)), line=0.5)
## choose right column and reset user plotting area (usr, not needed because already done)
par(mar=c(2, 2, 1, 1), fig=c(0.5, 1, 0, 1), usr=usr, new=TRUE)
## draw axis; see ?Axis for details
Axis(side=4, at=ticksAt, labels=as.character((nRow+1):(2*nRow)), line=0.5)
You could make one overall plot of the entire device, add the axes there, then use the subplot function (TeachingDemos package) to do the plots within the large plot.

Resources