创建半圆环形或议会式座位图表

10

我想在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


我不确定在ggplot中是否有其他方法,除了这种hacky的方式(https://dev59.com/hmEh5IYBdhLWcg3wbjLc),但你可以查看`circular`包(https://dev59.com/n5Dea4cB1Zd3GeqPXygP)。 - Lucy
1
这可能对你有用:https://github.com/leeper/ggparliament? - hrbrmstr
3个回答

11

想要获得标签等内容,可以利用单位圆性质!我写了一个小函数,试图重现你问题中的绘图风格 :)

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)

在此输入图片描述


太棒了,感谢您花时间设置这个。 - Phil
精彩的回答。 - skoh
1
@skoh 谢谢 - 我已经在我的 ggpol 包中实现了部分内容。还有一个新的包叫做 ggparliament,你可能想要查一下 :) - erocoar

9

这对你来说可行吗?

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

9

顺便说一句,您还可以查看不错的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


如何使用geom_text()添加标签(例如座位数),使它们位于每个对应的切片中间? - Phil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接