在ggplot图例中手动调整标签位置

4

我想知道在ggplot中是否可以手动定位图例中的标签?

我的例子是这样的:我有一些关于国家的数据,我正在为每个大陆制作一个100%堆叠条形图,所以我有:

dt <- data.table(continent = c(rep('Africa', 2), rep('Asia', 3), rep('Europe', 4)),
                 country = c('Nigeria', 'Kenya',
                             'China', 'India', 'Japan',
                             'Germany', 'Sweden', 'Spain', 'Croatia'),
                 value = runif(9, 0, 10),
                 number=(1:9))


ggplot(data=dt, 
       aes(x = continent, y = value, fill = as.factor(number))) +
  geom_bar(stat = "identity", position = "fill", color='white', width=0.3 ) + 
  labs(x = '', y = 'Percentage') +
  scale_y_continuous(expand=c(0,0)) +
  scale_fill_manual('Country',
                    labels = dt[, country],
                    values = (grDevices::colorRampPalette(c('#BB16A3', '#f8e7f5')))(9)) +
  theme(legend.position='bottom', aspect.ratio = 1) +
  guides(fill = guide_legend(title.position="top", title.hjust = 0.5, reverse=T)) +
  coord_flip()

我得到的图形是这个

所以我的问题是,是否可能重新定位图例中的标签,使每个大陆的国家位于单独的列或行?

谢谢!

1个回答

5
我注意到每个大洲的国家数量不同。 ggplot() 可以按行或列填充图例矩阵,但我从未见过有不同行/列中单元格数量不同的不规则矩阵。
尽管如此,仍然可以通过一些方法来“欺骗”看起来像是一个不规则的图例矩阵。以下是一些实现方法。如果您想按特定顺序排序大洲/国家标签,或者改变图例键之间的间距等参数,则可能需要调整参数。
准备工作:
# define fill mapping so that it can be re-used for both top plot & legend
scale_fill_country <- 
  scale_fill_manual(labels = dt[, country],
                    values = (grDevices::colorRampPalette(c('#BB16A3', '#f8e7f5')))(9))

# create top plot (without any legend)
gg.plot <- ggplot(data = dt, 
                  aes(x = continent, y = value, fill = as.factor(number))) +
  #note: geom_col is equivalent to geom_bar(stat = "identity")
  geom_col(position = "fill", color='white', width=0.3 ) + 
  labs(x = '', y = 'Percentage') +
  scale_y_continuous(expand=c(0,0)) +
  scale_fill_country +
  theme_classic() +
  theme(legend.position = "none") +
  coord_flip()

修改图例数据源:

library(dplyr)
dt.legend <- dt %>% 

  # pad with empty rows so that there are equal number of countries under
  # each continent
  group_by(continent) %>% 
  arrange(country) %>% 
  mutate(country.id = seq(1, n())) %>% 
  ungroup() %>% 
  tidyr::complete(continent, country.id, fill = list(country = " ")) %>%

  # make each empty row distinct (within the same continent), & sort them
  # after the original rows
  rowwise() %>%
  mutate(country = ifelse(country == " ", 
                          paste0(rep.int(" ", country.id), collapse = ""),
                          country)) %>%
  ungroup() %>%
  mutate(country = forcats::fct_reorder(country, country.id))

> dt.legend
# A tibble: 12 x 5
   continent country.id country   value number
   <chr>          <int> <fct>     <dbl>  <int>
 1 Africa             1 Kenya    2.02        2
 2 Africa             2 Nigeria  7.17        1
 3 Africa             3 "   "   NA          NA
 4 Africa             4 "    "  NA          NA
 5 Asia               1 China    3.21        3
 6 Asia               2 India    5.59        4
 7 Asia               3 Japan    9.31        5
 8 Asia               4 "    "  NA          NA
 9 Europe             1 Croatia  0.0131      9
10 Europe             2 Germany  0.0775      6
11 Europe             3 Spain    3.98        8
12 Europe             4 Sweden   0.703       7

版本1:每个中包含一个大陆,图例键下方的标签(下方)(如果您不想显示与每行相关联的大陆标签,请在theme()中添加axis.text.y = element_blank())。

gg.legend.rows1 <- ggplot(data = dt.legend,
                         aes(x = country, y = continent,
                             fill = as.factor(number))) +
  geom_tile(color = "white", size = 2) +
  facet_wrap(~ continent, scales = "free", ncol = 1) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_fill_country +
  theme_minimal() +
  theme(axis.title = element_blank(),
        strip.text = element_blank(),
        panel.grid = element_blank(),
        legend.position = "none")

cowplot::plot_grid(gg.plot, gg.legend.rows1,
                   ncol = 1,
                   rel_heights = c(1, 0.3))

v1

版本2:每个大洲都在一行中,图例键的标签位于右侧(我想不出一种方法将大洲标签放入此方法中,但我认为这在问题中并不需要...)

gg.legend.rows2 <- ggplot(data = dt.legend,
       aes(x = "", y = country, fill = as.factor(number))) +
  geom_tile() +
  scale_y_discrete(position = "right", expand = c(0, 0)) +
  facet_wrap(~ interaction(continent, country, lex.order = TRUE), 
             scales = "free") +
  scale_fill_country +
  theme_minimal() +
  theme(axis.title = element_blank(),
        axis.text.x = element_blank(),
        strip.text = element_blank(),
        panel.grid = element_blank(),
        panel.spacing = unit(0, "pt"),
        legend.position = "none")

cowplot::plot_grid(gg.plot, gg.legend.rows2,
                   axis = "l", align = "v",
                   ncol = 1,
                   rel_heights = c(1, 0.2))

v2

版本 3: 每个大陆在一个中,图例键的标签在右边(如果不想显示每个列关联的大陆标签,请将axis.text.x = element_blank()添加到theme()中)。

gg.legend.columns <- ggplot(data = dt.legend,
                            aes(x = continent, y = forcats::fct_rev(country), 
                                fill = as.factor(number))) +
  geom_tile(color = "white", size = 2) +
  facet_wrap(~ continent, scales = "free", nrow = 1) +
  scale_x_discrete(position = "top", expand = c(0, 0)) +
  scale_y_discrete(position = "right", expand = c(0, 0)) +
  scale_fill_country +
  theme_minimal() +
  theme(axis.title = element_blank(),
        strip.text = element_blank(),
        panel.grid = element_blank(),
        legend.position = "none")

cowplot::plot_grid(gg.plot, gg.legend.columns, 
                   axis = "l", align = "v",
                   ncol = 1, 
                   rel_heights = c(1, 0.3))

v3


太棒了的回答! - markus
亲爱的 farazan,如果答案解决了您的问题,请勾选它作为解决方案。这有助于其他人知道问题是否已经解决。 - massisenergy

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