ggplot Donut chart
Asked Answered
W

2

17

Hi I really have googled this a lot without any joy. Would be happy to get a reference to a website if it exists. I'm struggling to understand the Hadley documentation on polar coordinates and I know that pie/donut charts are considered inherently evil.

That said, what I'm trying to do is

  1. Create a donut/ring chart (so a pie with an empty middle) like the tikz ring chart shown here
  2. Add a second layer circle on top (with alpha=0.5 or so) that shows a second (comparable) variable.

Why? I'm looking to show financial information. The first ring is costs (broken down) and the second is total income. The idea is then to add + facet=period for each review period to show the trend in both revenues and expenses and the growth in both.

Any thoughts would be most appreciated

Note: Completely arbitrarily if an MWE is needed if this was tried with

donut_data=iris[,2:4]
revenue_data=iris[,1]
facet=iris$Species

That would be similar to what I'm trying to do.. Thanks

Wavy answered 28/11, 2012 at 22:29 Comment(2)
The data visualization gurus would say: "What does the circular-ness of your plot bring to the representation or interpretation of the data?" If data clarity is the goal, why not a stacked or dodged bar?Davon
@Justin, a bit of YMMV I guess, but for myself, a better sense of the relative for a large data set (ten periods) within the donut and between the donut and the circle. I think it would be quite striking and draw the eye, but not intimidate. There will be a lot of data on the page and I don't want to scare the readers off with a lot of stack/dodge charts. I'd like to ease them into the dataset.. :-)Wavy
A
40

I don't have a full answer to your question, but I can offer some code that may help get you started making ring plots using ggplot2.

library(ggplot2)

# Create test data.
dat = data.frame(count=c(10, 60, 30), category=c("A", "B", "C"))

# Add addition columns, needed for drawing with geom_rect.
dat$fraction = dat$count / sum(dat$count)
dat = dat[order(dat$fraction), ]
dat$ymax = cumsum(dat$fraction)
dat$ymin = c(0, head(dat$ymax, n=-1))

p1 = ggplot(dat, aes(fill=category, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) +
     geom_rect() +
     coord_polar(theta="y") +
     xlim(c(0, 4)) +
     labs(title="Basic ring plot")

p2 = ggplot(dat, aes(fill=category, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) +
     geom_rect(colour="grey30") +
     coord_polar(theta="y") +
     xlim(c(0, 4)) +
     theme_bw() +
     theme(panel.grid=element_blank()) +
     theme(axis.text=element_blank()) +
     theme(axis.ticks=element_blank()) +
     labs(title="Customized ring plot")


library(gridExtra)
png("ring_plots_1.png", height=4, width=8, units="in", res=120)
grid.arrange(p1, p2, nrow=1)
dev.off()

enter image description here

Thoughts:

  1. You may get more useful answers if you post some well-structured sample data. You have mentioned using some columns from the iris dataset (a good start), but I am unable to see how to use that data to make a ring plot. For example, the ring plot you have linked to shows proportions of several categories, but neither iris[, 2:4] nor iris[, 1] are categorical.
  2. You want to "Add a second layer circle on top": Do you mean to superimpose the second ring directly on top of the first? Or do you want the second ring to be inside or outside of the first? You could add a second internal ring with something like geom_rect(data=dat2, xmax=3, xmin=2, aes(ymax=ymax, ymin=ymin))
  3. If your data.frame has a column named period, you can use facet_wrap(~ period) for facetting.
  4. To use ggplot2 most easily, you will want your data in 'long-form'; melt() from the reshape2 package may be useful for converting the data.
  5. Make some barplots for comparison, even if you decide not to use them. For example, try: ggplot(dat, aes(x=category, y=count, fill=category)) + geom_bar(stat="identity")
Ardent answered 29/11, 2012 at 22:36 Comment(3)
Thanks very much @bdemarest. Hadn't even thought of geom_rect(). Been bashing up against geom_bar() all evening. The overlay works perfectly too. And it is an overlay on the chart. Apologies for not putting a better example in place. I had actually thought this was a lost cause! Would have gone with the bar on reflection, but discovered I couldn't do a stack and dodge on the same graph. Its important to show costs in a breakdown and a full revenue number alongside for each year in this case. Thanks again.Wavy
Cool! Does someone know how I could choose my own colors instead of the defaults?Cissoid
@user3281413, get started by searching google or stackoverflow for scale_fill_manual().Ardent
H
4

Just trying to solve question 2 with the same approach from bdemarest's answer. Also using his code as a scaffold. I added some tests to make it more complete but feel free to remove them.

library(broom)
library(tidyverse)
# Create test data.
dat = data.frame(count=c(10,60,20,50),
                 ring=c("A", "A","B","B"),
                 category=c("C","D","C","D"))

# compute pvalue
cs.pvalue <- dat %>% spread(value = count,key=category) %>%
  ungroup() %>% select(-ring) %>% 
  chisq.test() %>% tidy()
cs.pvalue <- dat %>% spread(value = count,key=category) %>% 
  select(-ring) %>%
  fisher.test() %>% tidy() %>% full_join(cs.pvalue)

# compute fractions
#dat = dat[order(dat$count), ]
dat %<>% group_by(ring) %>% mutate(fraction = count / sum(count),
                                      ymax = cumsum(fraction),
                                      ymin = c(0,ymax[1:length(ymax)-1]))


# Add x limits
baseNum <- 4
#numCat <- length(unique(dat$ring))
dat$xmax <- as.numeric(dat$ring) + baseNum
dat$xmin = dat$xmax -1


# plot
p2 = ggplot(dat, aes(fill=category,
                     alpha = ring,
                     ymax=ymax, 
                     ymin=ymin, 
                     xmax=xmax, 
                     xmin=xmin)) +
  geom_rect(colour="grey30") +
  coord_polar(theta="y") +
  geom_text(inherit.aes = F,
            x=c(-1,1),
            y=0,
            data = cs.pvalue,aes(label = paste(method,
                                               "\n",
                                               format(p.value,
                                                      scientific = T,
                                                      digits = 2))))+
  xlim(c(0, 6)) +
  theme_bw() +
  theme(panel.grid=element_blank()) +
  theme(axis.text=element_blank()) +
  theme(axis.ticks=element_blank(),
        panel.border = element_blank()) +
  labs(title="Customized ring plot") + 
  scale_fill_brewer(palette = "Set1") +
  scale_alpha_discrete(range = c(0.5,0.9))

p2

And the result:

Imgur

Holloway answered 31/5, 2017 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.