创建一个半甜甜圈,或议会座位,图表

时间:2017-03-10 23:02:25

标签: r ggplot2

我想在ggplot2中创建一个显示议会席位大小的图表,例如下面的议席。我的主要问题是,如何将圆环图转换为半圆形图(半圆弧)?

Parliament chart

以上图为例,我不知道从哪里开始:

df <- data.frame(Party = c("GUE/NGL", "S&D", "Greens/EFA", "ALDE", "EPP", "ECR", "EFD", "NA"),
                             Number = c(35, 184, 55, 84, 265, 54, 32, 27))
df$Party <- factor(df$Party)
df$Share <- df$Number / sum(df$Number)
df$ymax <- cumsum(df$Share)
df$ymin <- c(0, head(df$ymax, n= -1))

ggplot(df, aes(fill = Party, ymax = ymax, ymin = ymin, xmax = 2, xmin = 1)) + geom_rect() + 
coord_polar(theta = "y") + xlim(c(0, 2))

enter image description here

3 个答案:

答案 0 :(得分:7)

FWIW,人们也可以查看好的ggforce包:

library(tidyverse)
library(ggforce)
library(scales)
df %>%
  mutate_at(vars(starts_with("y")), rescale, to=pi*c(-.5,.5), from=0:1) %>%
  ggplot + 
  geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = .5, r = 1, start = ymin, end = ymax, fill=Party)) + 
  coord_fixed() 

enter image description here

答案 1 :(得分:5)

这对你有用吗?

ggplot(df, aes(fill = Party, ymax = ymax, ymin = ymin, xmax = 2, xmin = 1)) + geom_rect() + 
  coord_polar(theta = "y",start=-pi/2) + xlim(c(0, 2)) + ylim(c(0,2))

基本上你只需将ylim设置为你的最大值的2倍,所以它只将它绘制为一半。在这种情况下,我们将y限制设置为0到2.然后,您可以在coord_polar(start=)中偏移开头,以使其处于适当的位置。

enter image description here

答案 2 :(得分:5)

要获得标签等,您可以使用单位圆属性!我写了一个小函数试图在你的问题中重新创建情节的风格:)

library(ggforce)

parlDiag <- function(Parties, shares, cols = NULL, repr=c("absolute", "proportion")) {
  repr = match.arg(repr)
  stopifnot(length(Parties) == length(shares))
  if (repr == "proportion") {
    stopifnot(sum(shares) == 1)
  }
  if (!is.null(cols)) {
    names(cols) <- Parties
  }

  # arc start/end in rads, last one reset bc rounding errors
  cc <- cumsum(c(-pi/2, switch(repr, "absolute" = (shares / sum(shares)) * pi, "proportion" = shares * pi)))
  cc[length(cc)] <- pi/2

  # get angle of arc midpoints
  meanAngles <- colMeans(rbind(cc[2:length(cc)], cc[1:length(cc)-1]))

  # unit circle
  labelX <- sin(meanAngles)
  labelY <- cos(meanAngles)

  # prevent bounding box < y=0
  labelY <- ifelse(labelY < 0.015, 0.015, labelY)

  p <- ggplot() + theme_no_axes() + coord_fixed() +
    expand_limits(x = c(-1.3, 1.3), y = c(0, 1.3)) + 
    theme(panel.border = element_blank()) +
    theme(legend.position = "none") +

    geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = 0.5, r = 1,
                     start = cc[1:length(shares)], 
                     end = c(cc[2:length(shares)], pi/2), fill = Parties)) +

    switch(is.null(cols)+1, scale_fill_manual(values = cols), NULL) + 

    # for label and line positions, just scale sin & cos to get in and out of arc
    geom_path(aes(x = c(0.9 * labelX, 1.15 * labelX), y = c(0.9 * labelY, 1.15 * labelY),
                  group = rep(1:length(shares), 2)), colour = "white", size = 2) +
    geom_path(aes(x = c(0.9 * labelX, 1.15 * labelX), y = c(0.9 * labelY, 1.15 * labelY),
                  group = rep(1:length(shares), 2)), size = 1) +

    geom_label(aes(x = 1.15 * labelX, y = 1.15 * labelY, 
                   label = switch(repr,
                                  "absolute" = sprintf("%s\n%i", Parties, shares),
                                  "proportion" = sprintf("%s\n%i%%", Parties, round(shares*100)))), fontface = "bold", 
               label.padding = unit(1, "points")) +

    geom_point(aes(x = 0.9 * labelX, y = 0.9 * labelY), colour = "white", size = 2) +
    geom_point(aes(x = 0.9 * labelX, y = 0.9 * labelY)) +
    geom_text(aes(x = 0, y = 0, label = switch(repr, 
                                               "absolute" = (sprintf("Total: %i MPs", sum(shares))), 
                                               "proportion" = "")),
              fontface = "bold", size = 7) 

  return(p)
}

bt <- data.frame(parties = c("CDU", "CSU", "SPD", "AfD", "FDP", "Linke", "Grüne", "Fraktionslos"),
                 seats   = c(200, 46, 153, 92, 80, 69, 67, 2),
                 cols    = c("black", "blue", "red", "lightblue", "yellow", "purple", "green", "grey"),
                 stringsAsFactors = FALSE)

parlDiag(bt$parties, bt$seats, cols = bt$cols)

enter image description here