Та же ширина баров в geom_bar (position = "dodge" )

Я хотел бы нарисовать сюжет с одинаковой шириной баров. Вот мой минимальный пример кода:

data <- data.frame(A = letters[1:17],
                   B = sample(1:500, 17),
                   C = c(rep(1, 5), rep(2, 6), rep(c(3,4,5), each = 2)))

ggplot(data,
       aes(x = C,  y = B, label = A,
           fill = A)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(position = position_dodge(width = 0.9), angle = 90)

Результат показан на картинке выше: enter image description here

Ширина столбцов зависит от количества наблюдений в группе, указанных в переменной C Я хочу, чтобы каждая полоса имела одинаковую ширину.

facet_grid(~C) работает (facet_grid(~C) одинаковой ширины), это не то, что я имею в виду:

ggplot(data,
       aes(x = C,  y = B, label = A,
           fill = A)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(position = position_dodge(width = 0.9), angle = 90) +
  facet_grid(~C)

enter image description here

Я хочу, чтобы график был похож на первый снимок, но с шириной столбцов, не зависящей от количества наблюдений на каждом уровне из столбца C Как мне это сделать?

[EDIT] geom_bar(width) изменяет ширину группы баров, но все же бары в пятой группе шире, чем в первой группе, поэтому это не ответ на мой вопрос.

Ответ 1

Обновить

Начиная ggplot2_3.0.0 версии ggplot2_3.0.0 вы теперь можете использовать position_dodge2 с preserve = c("total", "single")

ggplot(data,aes(x = C,  y = B, label = A, fill = A)) +
  geom_col(position = position_dodge2(width = 0.9, preserve = "single")) +
  geom_text(position = position_dodge2(width = 0.9, preserve = "single"), angle = 90, vjust=0.25)

enter image description here

Оригинальный ответ

Как уже отмечалось, вы можете сделать это, как в этом ответе: Преобразуйте A и C в факторы и добавьте невидимые переменные, используя tidyr complete. Начиная с последней версии ggplot2, рекомендуется использовать geom_col вместо geom_bar в случае stat = "identity":

data %>% 
  as.tibble() %>% 
  mutate_at(c("A", "C"), as.factor) %>% 
  complete(A,C) %>% 
  ggplot(aes(x = C,  y = B, fill = A)) +
  geom_col(position = "dodge")

enter image description here

Или работа с термином взаимодействия:

data %>% 
  ggplot(aes(x = interaction(C, A),  y = B, fill = A)) +
  geom_col(position = "dodge")

enter image description here

И, наконец, преобразовав взаимодействие в числовое, вы можете настроить ось X в соответствии с желаемым результатом. Группируя (group_by), вы можете рассчитать соответствующие разрывы. Необычные вещи с аргументом {} вокруг аргумента ggplot необходимы для непосредственного использования vaiables Breaks и C внутри канала.

data %>% 
  mutate(gr=as.numeric(interaction(C, A))) %>% 
  group_by(C) %>% 
  mutate(Breaks=mean(gr)) %>% 
  {ggplot(data=.,aes(x = gr,  y = B, fill = A, label = A)) +
   geom_col(position = "dodge") +
   geom_text(position = position_dodge(width = 0.9), angle = 90 ) +
   scale_x_continuous(breaks = unique(.$Breaks),
                     labels = unique(.$C))}

enter image description here

Редактировать:

Другой подход заключается в использовании аспектов. Использование space = "free_x" позволяет установить ширину, пропорциональную длине шкалы x.

library(tidyverse)
data %>% 
  ggplot(aes(x = A,  y = B, fill = A))  +  
   geom_col(position = "dodge") +
   facet_grid(~C, scales = "free_x", space = "free_x")

enter image description here

Вы также можете нанести метки фасета внизу с помощью switch и удалить метки оси x

data %>% 
  ggplot(aes(x = A,  y = B, fill = A))  +  
  geom_col(position = "dodge") +
  facet_grid(~C, scales = "free_x", space = "free_x", switch = "x") + 
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.background = element_blank())

enter image description here