R 中分组、堆叠、圆形条形图的标签/定位

问题描述 投票:0回答:1

我正在尝试使用 ggplot2 创建圆形、堆叠和分组条形图。我遇到的问题是

  1. 调整图表本身内的道具标签。条形内的标签(比例)与它们应该代表的区域不相关
  2. 我希望当前位于外侧的标签位于内圈内,类似于:https://r-graph-gallery.com/299-circular-stacked-barplot.html

我的数据如下所示:

    structure(list(`No of Carbon` = c(9, 9, 10, 8, 6, 4, 8, 9, 8, 
8, 13, 4, 6, 10, 6, 7, 6, 5, 5, 8, 11, 6, 2, 9, 8, 4, 14, 12, 
4, 4, 10, 6, 6, 9, 10, 9, 10, 4, 8, 6), `Functional Group` = c("Carboxylate", 
"Carboxylate", "Carboxylate", "Sulfonate", "Carboxylate", "Sulfonate", 
"Carboxylate", "Carboxylate", "Carboxylate", "Sulfonate", "Carboxylate", 
"Carboxylate", "Sulfonate", "Sulfonate", "Sulfonate", "Carboxylate", 
"Carboxylate", "Carboxylate", "Carboxylate", "Sulfonate", "Carboxylate", 
"Sulfonate", "Carboxylate", "Carboxylate", "Carboxylate", "Sulfonate", 
"Carboxylate", "Carboxylate", "Carboxylate", "Carboxylate", "Sulfonate", 
"Sulfonate", "Carboxylate", "Carboxylate", "Sulfonate", "Carboxylate", 
"Carboxylate", "Sulfonate", "Carboxylate", "Sulfonate"), `PFAS Type` = structure(c(28L, 
28L, 20L, 4L, 26L, 19L, 30L, 28L, 30L, 33L, 41L, 18L, 27L, 7L, 
27L, 24L, 26L, 37L, 37L, 33L, 17L, 27L, 36L, 28L, 30L, 19L, 40L, 
22L, 18L, 18L, 6L, 27L, 14L, 28L, 23L, 28L, 20L, 19L, 30L, 27L
), levels = c("4:2 Cl-PFESA", "4:2 FTS", "6:2 Cl-PFESA", "6:2 FTS", 
"6:2 H-PFESA", "8:2 Cl-PFESA", "8:2 FTS", "ADONA", "FBSA", "FHxSA", 
"FOSA", "FOSAA", "GenX", "HFPO-DA", "HFPO-TA", "N-Et-FOSAA", 
"N-Me-FOSAA", "PFBA", "PFBS", "PFDA", "PFDDA", "PFDoA", "PFDS", 
"PFHpA", "PFHpS", "PFHxA", "PFHxS", "PFNA", "PFNS", "PFOA", "PFONDS", 
"PFONS", "PFOS", "PFOSA", "PFOUDS", "PFPA", "PFPeA", "PFPeS", 
"PFTDA", "PFTeDA", "PFTrDA", "PFUA", "PFUdA", "PFUnA"), class = "factor"), 
    concentrations = c(0.45, 5.9, 0.83, 1.2, 0.82, 2.04, 12.7, 
    1.15, 2.71, 156, 0.71, 5.37, 0.00007, 0.04, 0.35, 0.19, 2.95, 
    0.26, 2.63, 0.96, 0.33, 117.18, 16, 0.4, 24, 0.45, 1.33, 
    3.57, 3.6, 3.15, 0.02, 0.97, 1.38, 0.34, 0.22, 1.34, 3.3, 
    5.27, 3.69, 3.54), carbon_chain = c("Long Chain PFAS", "Long Chain PFAS", 
    "Long Chain PFAS", "Short Chain PFAS", "Short Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS", "Long Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS", "Long Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS", "Long Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS", "Short Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS", "Short Chain PFAS", 
    "Long Chain PFAS", "Short Chain PFAS", "Short Chain PFAS", 
    "Long Chain PFAS", "Short Chain PFAS", "Short Chain PFAS", 
    "Long Chain PFAS", "Long Chain PFAS", "Short Chain PFAS", 
    "Short Chain PFAS", "Long Chain PFAS", "Short Chain PFAS", 
    "Short Chain PFAS", "Long Chain PFAS", "Long Chain PFAS", 
    "Long Chain PFAS", "Long Chain PFAS", "Short Chain PFAS", 
    "Short Chain PFAS", "Short Chain PFAS"), group = structure(c(3L, 
    3L, 3L, 3L, 3L, 3L, 2L, 3L, 3L, 2L, 3L, 3L, 1L, 3L, 2L, 1L, 
    3L, 1L, 3L, 3L, 3L, 2L, 2L, 1L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 
    3L, 3L, 3L, 3L, 3L, 2L, 3L, 3L, 3L), levels = c("rain", "stormwater", 
    "surface"), class = "factor")), row.names = c(NA, -40L), na.action = structure(c(`7` = 7L, 
`8` = 8L, `9` = 9L, `10` = 10L, `11` = 11L, `12` = 12L, `13` = 13L, 
`14` = 14L, `15` = 15L, `16` = 16L, `17` = 17L, `18` = 18L, `19` = 19L, 
`20` = 20L, `21` = 21L, `22` = 22L, `42` = 42L, `43` = 43L, `44` = 44L, 
`59` = 59L, `60` = 60L, `61` = 61L, `62` = 62L, `63` = 63L, `64` = 64L, 
`65` = 65L, `66` = 66L, `76` = 76L, `77` = 77L, `78` = 78L, `79` = 79L, 
`80` = 80L, `81` = 81L, `82` = 82L, `83` = 83L, `84` = 84L, `85` = 85L, 
`86` = 86L, `87` = 87L, `88` = 88L, `94` = 94L, `95` = 95L, `96` = 96L, 
`97` = 97L, `98` = 98L, `99` = 99L, `100` = 100L, `101` = 101L, 
`102` = 102L, `103` = 103L, `104` = 104L, `105` = 105L, `106` = 106L, 
`107` = 107L, `108` = 108L, `109` = 109L, `110` = 110L, `136` = 136L, 
`137` = 137L, `138` = 138L, `139` = 139L, `140` = 140L, `141` = 141L, 
`142` = 142L, `143` = 143L, `144` = 144L, `145` = 145L, `146` = 146L, 
`147` = 147L, `148` = 148L, `149` = 149L, `150` = 150L, `151` = 151L, 
`152` = 152L, `153` = 153L, `154` = 154L, `157` = 157L, `158` = 158L, 
`159` = 159L, `160` = 160L, `161` = 161L, `162` = 162L, `163` = 163L, 
`164` = 164L, `165` = 165L, `166` = 166L, `167` = 167L, `168` = 168L, 
`169` = 169L, `170` = 170L, `171` = 171L, `172` = 172L, `173` = 173L, 
`174` = 174L, `175` = 175L, `176` = 176L, `180` = 180L, `181` = 181L, 
`182` = 182L, `183` = 183L, `184` = 184L, `185` = 185L, `186` = 186L, 
`187` = 187L, `188` = 188L, `189` = 189L, `190` = 190L, `191` = 191L, 
`192` = 192L, `193` = 193L, `194` = 194L, `195` = 195L, `196` = 196L, 
`197` = 197L, `198` = 198L, `200` = 200L, `201` = 201L, `202` = 202L, 
`203` = 203L, `204` = 204L, `205` = 205L, `206` = 206L, `207` = 207L, 
`208` = 208L, `209` = 209L, `210` = 210L, `211` = 211L, `212` = 212L, 
`213` = 213L, `214` = 214L, `215` = 215L, `216` = 216L, `217` = 217L, 
`218` = 218L, `219` = 219L, `220` = 220L, `224` = 224L, `225` = 225L, 
`226` = 226L, `227` = 227L, `228` = 228L, `229` = 229L, `230` = 230L, 
`231` = 231L, `232` = 232L, `233` = 233L, `234` = 234L, `235` = 235L, 
`236` = 236L, `237` = 237L, `238` = 238L, `239` = 239L, `240` = 240L, 
`241` = 241L, `242` = 242L, `248` = 248L, `249` = 249L, `250` = 250L, 
`251` = 251L, `252` = 252L, `253` = 253L, `254` = 254L, `255` = 255L, 
`256` = 256L, `257` = 257L, `258` = 258L, `259` = 259L, `260` = 260L, 
`261` = 261L, `262` = 262L, `263` = 263L, `264` = 264L, `270` = 270L, 
`271` = 271L, `272` = 272L, `273` = 273L, `274` = 274L, `275` = 275L, 
`276` = 276L, `277` = 277L, `278` = 278L, `279` = 279L, `280` = 280L, 
`281` = 281L, `282` = 282L, `283` = 283L, `284` = 284L, `285` = 285L, 
`286` = 286L, `290` = 290L, `291` = 291L, `292` = 292L, `293` = 293L, 
`294` = 294L, `295` = 295L, `296` = 296L, `297` = 297L, `298` = 298L, 
`299` = 299L, `300` = 300L, `301` = 301L, `302` = 302L, `303` = 303L, 
`304` = 304L, `305` = 305L, `306` = 306L, `307` = 307L, `308` = 308L, 
`311` = 311L, `312` = 312L, `313` = 313L, `314` = 314L, `315` = 315L, 
`316` = 316L, `317` = 317L, `318` = 318L, `319` = 319L, `320` = 320L, 
`321` = 321L, `322` = 322L, `323` = 323L, `324` = 324L, `325` = 325L, 
`326` = 326L, `327` = 327L, `328` = 328L, `329` = 329L, `330` = 330L, 
`333` = 333L, `334` = 334L, `335` = 335L, `336` = 336L, `337` = 337L, 
`338` = 338L, `339` = 339L, `340` = 340L, `341` = 341L, `342` = 342L, 
`343` = 343L, `344` = 344L, `345` = 345L, `346` = 346L, `347` = 347L, 
`348` = 348L, `349` = 349L, `350` = 350L, `351` = 351L, `352` = 352L, 
`355` = 355L, `356` = 356L, `357` = 357L, `358` = 358L, `359` = 359L, 
`360` = 360L, `361` = 361L, `362` = 362L, `363` = 363L, `364` = 364L, 
`365` = 365L, `366` = 366L, `367` = 367L, `368` = 368L, `369` = 369L, 
`370` = 370L, `371` = 371L, `372` = 372L, `373` = 373L, `374` = 374L
), class = "omit"), class = c("tbl_df", "tbl", "data.frame"))

这就是我目前所做的:

    df %>% 
  summarise(N = n(), .by = c(group, carbon_chain, `Functional Group`)) |> 
  arrange(desc(`Functional Group`), carbon_chain, group) |> 
  mutate(
    prop = N / sum(N), 
    label_y = cumsum(prop),
    x = paste0(group, "\n", carbon_chain),
    .by = c(group, carbon_chain)
  ) %>%  
  ggplot(aes(x = x, y = prop, fill = `Functional Group`)) +
  geom_col(position = "fill", color = "black") +
  geom_label(aes(y = label_y, label = scales::percent(prop, 1)), fill = "white",
             show.legend = F, position = position_fill(vjust = 0.5), size = 2) +
  coord_polar() + 
  ylim(c(-1, NA)) + # hole size
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(
    panel.grid.major.y = element_line(color = "gray", linetype = "dotted"),
    axis.text.y = element_blank(),
    legend.position = "bottom"
  )

What my current graph looks like

所以你可以看到道具标签没有以各自的部分为中心,并且外部标签看起来很混乱。我正在考虑将这些当前外部标签放在内部并且不重复。因此,标签的一个内层将是“雨”、“地表”和“暴雨”,每个标签将覆盖两个“条”。第二个内部标签将涵盖长链和短链

r ggplot2 plot bar-chart polar-coordinates
1个回答
0
投票

正如 @teunbrand 在评论中已经提到的,您可以通过将

prop
映射到
y
并使用
position_stack
来解决第一个问题。对于第二个问题,您可以按照 R 图库中的示例,首先创建一个数据框来绘制内圆的线段,其中我使用
geom_segment
代替
geom_text
geomtextpath::geom_textsegment
,它允许绘制线段和文本一次完成,并允许弯曲文本。此外,我切换到
geomtextpath::coord_curvedpolar()
,它允许弯曲的 x 轴文本标签,对于后者,我将信息放在组上,因为这现在反映在内圈中。如果您希望将轴文本作为第二个内圆,您可以遵循我用于第一个内圆的方法。

library(dplyr, warn = FALSE)
library(ggplot2)
library(geomtextpath)

# prepare a data frame for base lines
base_data <- df %>%
  distinct(group, carbon_chain) %>%
  arrange(group, carbon_chain) |>
  mutate(id = row_number()) |>
  summarise(
    start = min(id) - .4,
    end = max(id) + .4,
    .by = group
  ) |>
  mutate(title = rowMeans(across(c(start, end))))

df %>%
  summarise(N = n(), .by = c(group, carbon_chain, `Functional Group`)) |>
  arrange(desc(`Functional Group`), carbon_chain, group) |>
  mutate(
    prop = N / sum(N),
    x = paste0(group, "_", carbon_chain),
    .by = c(group, carbon_chain)
  ) %>%
  ggplot(aes(x = x, y = prop, fill = `Functional Group`)) +
  geom_col(position = "fill", color = "black") +
  geom_label(
    aes(y = prop, label = scales::percent(prop, 1)),
    fill = "white",
    show.legend = FALSE,
    position = position_stack(vjust = 0.5), 
    size = 8 / .pt
  ) +
  # Add base line information
  geomtextpath::geom_textsegment(
    data = base_data,
    aes(
      x = start, y = -.1,
      xend = end, yend = -.1,
      label = group
    ),
    colour = "black",
    linewidth = 0.6,
    size = 8 / .pt,
    inherit.aes = FALSE,
    gap = FALSE,
    offset = unit(-8, "pt")
  ) +
  scale_x_discrete(
    labels = ~ gsub("^.*?_", "", .x)
  ) +
  geomtextpath::coord_curvedpolar() +
  ylim(c(-1, NA)) + # hole size
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(
    panel.grid.major.y = element_line(color = "gray", linetype = "dotted"),
    axis.text.y = element_blank(),
    axis.text.x = element_text(vjust = .5),
    legend.position = "bottom"
  )

© www.soinside.com 2019 - 2024. All rights reserved.