2

Let's have the code following :

pie_chart <- function(vec){
  
  df <- as.data.frame(table(vec))
  colnames(df)[1] <- 'group'
  df$label <- paste0(df$Freq,' (', percent(df$Freq / sum(df$Freq)),')')
  df$angle <- ((cumsum(df$Freq) - 0.5 * df$Freq) / sum(df$Freq) * 360) %% 180 - 90  
  ggplot(df, aes(x = "", y = Freq, fill = group)) +
    geom_col(width = 1, show.legend = TRUE) +
    geom_text(
      aes(label = label, angle = angle), 
      position = position_stack(vjust = 0.5), 
      size = 5
    ) +
    coord_polar("y", start = 0) +
    theme_void()
}

pie_chart(c(rep(2,20),rep(3,30),rep(4,4),rep(5,50),rep(6,30)))

enter image description here

As you can see above, text is centered (with respect to each part of pie) and rotated to the middle of circle.

What I want to do is to add the arrow all values less than 5% of all pie chart. So in our example value '4(3.0)%' should not be in pie chart but it should be outstide followed by arrow.

Let's now study my research about the problem :

pie_chart <- function(vec){
  df <- as.data.frame(table(vec))
  colnames(df)[1] <- 'group'
  df$label <- paste0(df$Freq,' (', percent(df$Freq / sum(df$Freq)),')')
  df$angle <- ((cumsum(df$Freq) - 0.5 * df$Freq) / sum(df$Freq) * 360) %% 180 - 90  
  ggplot(df, aes(x = "", y = Freq, fill = group)) +
    geom_col(width = 1, show.legend = TRUE) +
    geom_text(
      aes(label = label, angle = angle, x = ifelse(rev(Freq) / sum(Freq) < 0.05, 1.7, 1)), 
      position = position_stack(vjust = 0.5), 
      size = 5
    ) +  geom_segment(aes(x = 1.6, xend = 1.5, 
                          y = rev(Freq)/2 + c(0, cumsum(rev(Freq))[-length(Freq)]),
                          yend = after_stat(y),
                          colour = I(ifelse(rev(Freq) / sum(Freq) < 0.05, "black", "transparent"))),
                      arrow = arrow(length = unit(1, "mm"))) +
    coord_polar("y", start = 0) +
    theme_void()
}

pie_chart(c(rep(2,20),rep(3,30),rep(4,4),rep(5,50),rep(6,30)))

enter image description here

And as you can see I'm facing two problems :

(1) The layout crashed (let's have a look at red part of pie)

(2) text '4(3.0%)' is above pie chart instead being connected with arrow (which is created properly)

Is there any way to solve problems (1) and (2) ?

Thanks in advance!

UPDATE

As SebSta proposed, I used his code with added arrows and it works for this specific case. But when I add three sevens the layout crashes (in my opinion)

pie_chart <- function(vec){
  
  df <- as.data.frame(table(vec))
  colnames(df)[1] <- 'group'
  df$label <- paste0(df$Freq,' (', round(df$Freq / sum(df$Freq),3)*100,'%)')
  df$angle <- ((cumsum(df$Freq) - 0.5 * df$Freq) / sum(df$Freq) * 360) %% 180 - 90
  
  ggplot(df, aes(x = "", y = Freq, fill = group)) +
    geom_col(width = 1, show.legend = TRUE) +
    geom_text(
      aes(label = label, angle = angle), color = ifelse(rev(df$Freq)/(sum(df$Freq))<0.05, "transparent", "black"), 
      position = position_stack(vjust = 0.5), 
      size = 5
    ) + geom_segment(aes(x = 1.6, xend = 1.5, 
                         y = rev(Freq)/2 + c(0, cumsum(rev(Freq))[-length(Freq)]),
                         yend = after_stat(y),
                         colour = I(ifelse(rev(Freq) / sum(Freq) < 0.05, "black", "transparent"))),
                     arrow = arrow(length = unit(1, "mm"))) +
    geom_text(aes(label =label, angle = angle, x =1.8), 
              color = ifelse(rev(df$Freq)/(sum(df$Freq))>0.05, "transparent", "black"), 
              size = 5,position = position_stack(vjust = 0.5), 
    ) +
    coord_polar("y", start = 0) +
    theme_void()
}

pie_chart(c(rep(2,20),rep(3,30),rep(4,4),rep(5,50),rep(6,30),rep(7,3)))

enter image description here

John
  • 1,849
  • 2
  • 13
  • 23

2 Answers2

2

You could use position_identity for the labels and calculate where exactly they should be:

pie_chart <- function(vec){
  
  df <- as.data.frame(table(vec))
  colnames(df)[1] <- 'group'
  df$label <- paste0(df$Freq,' (', scales::percent(df$Freq / sum(df$Freq)),')')
  df$angle <- ((cumsum(df$Freq) - 0.5 * df$Freq) / sum(df$Freq) * 360) %% 180 - 90
  ggplot(df, aes(x = "", y = Freq, fill = group)) +
    geom_col(width = 1, show.legend = TRUE) +
    geom_text(
      aes(x = ifelse(Freq/sum(Freq) < 0.05, 1.8, 1), 
          y = sum(Freq) - (cumsum(c(0, Freq[-length(Freq)])) + 0.5 * Freq),
          label = label, angle = angle), 
      position = position_identity(), vjust = 0.25, 
      size = 4
    ) +  geom_segment(aes(x = 1.6, xend = 1.5, 
                          y = rev(Freq)/2 + c(0, cumsum(rev(Freq))[-length(Freq)]),
                          yend = after_stat(y),
                          colour = I(ifelse(rev(Freq) / sum(Freq) < 0.05, "black",
                                            "transparent"))),
                      arrow = arrow(length = unit(1, "mm"))) +
    coord_polar("y", start = 0) +
    theme_void()
}

pie_chart(c(rep(2, 20), rep(3, 30), rep(4, 4), rep(5, 50), rep(6, 30)))

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
1

MY first idea here would be a hacky approach: Write the label two times, once the normal labels, where all values < 5% are transparent, and once around the pie chart, where all values >5% are transparent. Then you can adjust the distance to the pie with the x coordinate:

pie_chart <- function(vec){
  
  df <- as.data.frame(table(vec))
  colnames(df)[1] <- 'group'
  df$label <- paste0(df$Freq,' (', round(df$Freq / sum(df$Freq),3)*100,'%)')
  df$angle <- ((cumsum(df$Freq) - 0.5 * df$Freq) / sum(df$Freq) * 360) %% 180 - 90

  ggplot(df, aes(x = "", y = Freq, fill = group)) +
    geom_col(width = 1, show.legend = TRUE) +
    geom_text(
      aes(label = label, angle = angle), color = ifelse(rev(df$Freq)/(sum(df$Freq))<0.05, "transparent", "black"), 
      position = position_stack(vjust = 0.5), 
      size = 5
    ) +
    geom_text(aes(label =label, angle = angle, x =1.8), 
                   color = ifelse(rev(df$Freq)/(sum(df$Freq))>0.05, "transparent", "black"), 
              size = 5,position = position_stack(vjust = 0.5), 
    ) +
    coord_polar("y", start = 0) +
    theme_void()
}

pie_chart(c(rep(2,20),rep(3,30),rep(4,4),rep(5,50),rep(6,30)))

enter image description here

Note that i cant reproduce your arrows, as i dont have the after_stat function. I guess thats a new one for R 4.0.0, while i use 3.6.1.

SebSta
  • 476
  • 2
  • 12
  • The `after_stat()` function was introduced in ggplot2 v3.3.0. Prior to that it was either `stat(variable)` or `...variable...`. – teunbrand Aug 12 '20 at 15:02