Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a circular barplot with a hollow center (aka race track plot)

Tags:

plot

r

ggplot2

I was asked to recreate the following style of plot. (Please ignore the question of whether this is a good type of visualization and charitably consider this as adding a colorful element to a numeric table.)

Most of it is pretty straightforward, but I have not yet found a good way to make the center hollow. In the interest of time, I may resort to the kludge of adding invisible dummy data (I'll post that approach if no one else does, but it seems less optimal than one that modifies the theme). Is there a theme-based solution or a non-ggplot2 R solution?

What we're imitating

enter image description here

Simple ggplot2 result (undesired filled center)

library(ggplot2)  # make sample dataframe  Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing",              "Cars", "Food/Beverages", "Personal Hygiene",              "Personal Health/OTC", "Hair Care") Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)  internetImportance<-data.frame(Category,Percent)  # append number to category name internetImportance$Category <-      paste0(internetImportance$Category," - ",internetImportance$Percent,"%")  # set factor so it will plot in descending order  internetImportance$Category <-     factor(internetImportance$Category,      levels=rev(internetImportance$Category))  # plot  ggplot(internetImportance, aes(x = Category, y = Percent,     fill = Category)) +      geom_bar(width = 0.9, stat="identity") +      coord_polar(theta = "y") +     xlab("") + ylab("") +     ylim(c(0,100)) +     ggtitle("Top Product Categories Influenced by Internet") +     geom_text(data = internetImportance, hjust = 1, size = 3,               aes(x = Category, y = 0, label = Category)) +     theme_minimal() +     theme(legend.position = "none",           panel.grid.major = element_blank(),           panel.grid.minor = element_blank(),           axis.line = element_blank(),           axis.text.y = element_blank(),           axis.text.x = element_blank(),           axis.ticks = element_blank()) 

enter image description here

How can we plot these data with a hollow center?

like image 768
MattBagg Avatar asked Apr 01 '13 20:04

MattBagg


People also ask

What is a circular Barplot?

A circular barplot is a plot where each bar is displayed along a circle instead of a line. Here no Y scale is displayed since exact values are written on each bar which could also be represented on interaction with the chart.


2 Answers

Here's a non-ggplot2 (base R graphics) solution using the plotrix package, which contains two nice functions: draw.circle() and draw.arc():

circBarPlot <- function(x, labels, colors=rainbow(length(x)), cex.lab=1) {   require(plotrix)   plot(0,xlim=c(-1.1,1.1),ylim=c(-1.1,1.1),type="n",axes=F, xlab=NA, ylab=NA)   radii <- seq(1, 0.3, length.out=length(x))   draw.circle(0,0,radii,border="lightgrey")   angles <- (1/4 - x)*2*pi   draw.arc(0, 0, radii, angles, pi/2, col=colors, lwd=130/length(x), lend=2, n=100)   ymult <- (par("usr")[4]-par("usr")[3])/(par("usr")[2]-par("usr")[1])*par("pin")[1]/par("pin")[2]   text(x=-0.02, y=radii*ymult, labels=paste(labels," - ", x*100, "%", sep=""), pos=2, cex=cex.lab) }  circBarPlot(Percent/100, Category) text(0,0,"GLOBAL",cex=1.5,col="grey") 

It gives me:

Circular bar plot

like image 132
Theodore Lytras Avatar answered Sep 23 '22 07:09

Theodore Lytras


I think an immediate fix is to create some "empty" entries. I'd create internetImportance data.frame like this:

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing",          "Cars", "Food/Beverages", "Personal Hygiene",          "Personal Health/OTC", "Hair Care") Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)  internetImportance <- data.frame(Category,Percent)  len <- 4 df2 <- data.frame(Category = letters[1:len], Percent = rep(0, len),                                   Category2 = rep("", len)) internetImportance$Category2 <-   paste0(internetImportance$Category," - ",internetImportance$Percent,"%")  # append number to category name internetImportance <- rbind(internetImportance, df2)  # set factor so it will plot in descending order  internetImportance$Category <-     factor(internetImportance$Category,      levels=rev(internetImportance$Category)) 

And then I'd plot ggplot2 with fill=category2 as follows:

ggplot(internetImportance, aes(x = Category, y = Percent,     fill = Category2)) +      geom_bar(width = 0.9, stat="identity") +      coord_polar(theta = "y") +     xlab("") + ylab("") +     ylim(c(0,100)) +     ggtitle("Top Product Categories Influenced by Internet") +     geom_text(data = internetImportance, hjust = 1, size = 3,               aes(x = Category, y = 0, label = Category2)) +     theme_minimal() +     theme(legend.position = "none",           panel.grid.major = element_blank(),           panel.grid.minor = element_blank(),           axis.line = element_blank(),           axis.text.y = element_blank(),           axis.text.x = element_blank(),           axis.ticks = element_blank()) 

This gives me:

enter image description here

You can add a geom_text(label="GLOBAL", x=.5, y=.5, size=4) + before theme_minimal to add the text GLOBAL.

like image 39
Arun Avatar answered Sep 22 '22 07:09

Arun