12

I am struggling with getting the pie chart labels correct. Looked around and thought that I could easily implement what mathematicalCoffee did. So far I have this code:

ltr = LETTERS[seq( from = 1, to = 26)]

wght = runif(length(ltr))
wght = wght/sum(wght)
wght = round(wght, digits = 2)

alloc = as.data.frame(cbind(ltr, wght))
alloc$wght = as.numeric(as.character(alloc$wght))

ggpie <- function (dat, by, totals) {
  ggplot(dat, aes_string(x=factor(1), y=totals, fill=by)) +
    geom_bar(stat='identity', color='black') +
    guides(fill=guide_legend(override.aes=list(colour=NA))) +
    coord_polar(theta='y') +
    theme(axis.ticks=element_blank(),
          axis.text.y=element_blank(),
          axis.text.x=element_text(colour='black'),
          axis.title=element_blank()) +
    ## scale_fill_brewer(palette = "GnBu") +
    scale_y_continuous(breaks=cumsum(dat[[totals]]) - dat[[totals]] / 2, labels=paste(dat[[by]], ":", dat[[totals]]))    
}

AA = ggpie(alloc, by = "ltr", totals = "wght") +
  ggtitle("Letter weights")

AA

The resulting pie chart:Pie

Is there any way to generate something like this, for example:

CoolPie

Update for suggested dup - I think that thread is more about alternatives to pie charts and why pie charts are bad. I would like to stick to pie charts and want to find a solution to handling labels correctly/user-friendly.

AK88
  • 2,946
  • 2
  • 12
  • 31
  • 1
    Possible duplicate of [beautiful Pie Charts with R](https://stackoverflow.com/questions/33594642/beautiful-pie-charts-with-r) – yeedle Jun 08 '17 at 12:57
  • You have the excellent package ggrepel, which can adjust label placement and make them non-overlapping, but you do have to change your code and add the text as a label and not as breaks. More info: https://cran.r-project.org/web/packages/ggrepel/vignettes/ggrepel.html – Marinka Jun 08 '17 at 12:59

2 Answers2

19

For pie charts plotly works a lot easier than ggplot. Perhaps something like this:

library(plotly)

p <- plot_ly(alloc, labels = ~ltr, values = ~wght, type = 'pie',textposition = 'outside',textinfo = 'label+percent') %>%
  layout(title = 'Letters',
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

enter image description here

timfaber
  • 2,060
  • 1
  • 15
  • 17
  • Appreciate your response. Can you show any pie charts produced using `plotly` that give user-friendly labels? – AK88 Jun 08 '17 at 13:07
  • Check out the styled pie chart example in this reference: https://plot.ly/r/pie-charts/ Here you can set both labels/colors as well as define the label position. – timfaber Jun 08 '17 at 13:10
  • Well, it partially accomplishes what I am after. Let's say that pieces of the pie are really slim and multiple labels should be located outside the slice-area. Like in the example I provided above. Is `plotly` good at this? – AK88 Jun 08 '17 at 13:14
  • Hm not really sure, I believe labels are dodged whenever they overlap so it should cover this in case of superslim labels. There are annotate options in plotly that could deliver you the lines shown in your second example but I m afraid that would cost a lot of custom work. – timfaber Jun 08 '17 at 13:22
  • Ha, I tried replacing ltr with `ltr = 1:150` to give this a go, rerunning the script gives you a pretty amazing pie but not sure if it's very readable – timfaber Jun 08 '17 at 13:26
  • Alright, thanks. Will take a look at `plotly` after the weekend. – AK88 Jun 08 '17 at 13:27
  • Yes, that's the issue. I potentially will have around 30 pieces. – AK88 Jun 08 '17 at 13:28
  • Accepted. There doesn't seem to be a better alternative. Thank you. – AK88 Jun 26 '17 at 11:30
  • This great, but I find the labels disappear off the top of the figure when I have a plot with small groups (eg. a=94, b=3, c=2, d=2). @timfaber – sar Nov 15 '19 at 00:02
11

We can make it work with ggplot2 and the ggrepel package.

Unfortunately geom_text_repel() does not support a position = argument, so we have to calculate the starting position of the line by hand.

With your data.frame:

alloc$pos = (cumsum(c(0, alloc$wght)) + c(alloc$wght / 2, .01))[1:nrow(alloc)]

This calculates the mean point for each group (or obs, or whtvr you want to call it).

Plugging it in in the geom_text_repel's y aes gives a nice result:

library(ggplot2)
library(ggrepel)
ggplot(alloc, aes(1, wght, fill = ltr)) +
    geom_col(color = 'black', 
             position = position_stack(reverse = TRUE), 
             show.legend = FALSE) +
    geom_text_repel(aes(x = 1.4, y = pos, label = ltr), 
                    nudge_x = .3, 
                    segment.size = .7, 
                    show.legend = FALSE) +
    coord_polar('y') +
    theme_void()

I made some choices, as the use of text instead of label, the removal of the legend and the axes. Feel free to change them

GGamba
  • 13,140
  • 3
  • 38
  • 47