Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The same width of the bars in geom_bar(position = "dodge")

Tags:

r

ggplot2

I would like to draw plot with the same width of the bars. Here's my minimal example code:

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)

The result is shown in the picture above: enter image description here

The width of the bars is dependent on numbers of observation in group given in variable C. I want to have each bar to have the same width.

The facet_grid(~C) works (bars are the same width) it's not what I mean:

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

What I want is to have plot like in the first picture but with bars's width independent on number of observation in each level from column C. How can I do it?

[EDIT] geom_bar(width) changes width of the bars'group but still bars in fifth group are wider than in the first group, so it's not the answer to my question.

like image 754
jjankowiak Avatar asked Jun 29 '16 13:06

jjankowiak


People also ask

What does position Dodge mean in Ggplot?

position_dodge.Rd. Dodging preserves the vertical position of an geom while adjusting the horizontal position.

What is the difference between Geom_col and Geom_bar?

geom_bar() makes the height of the bar proportional to the number of cases in each group (or if the weight aesthetic is supplied, the sum of the weights). If you want the heights of the bars to represent values in the data, use geom_col() instead.

What is Dodge doing in R?

Dodging preserves the vertical position of an geom while adjusting the horizontal position. position_dodge() requires the grouping variable to be be specified in the global or ⁠geom_*⁠ layer. Unlike position_dodge() , position_dodge2() works without a grouping variable in a layer.

How do you change the width of a BarPlot?

To Increase or Decrease width of Bars of BarPlot, we simply assign one more width parameter to geom_bar() function. We can give values from 0.00 to 1.00 as per our requirements.


1 Answers

Update

Since ggplot2_3.0.0 version you are now be able to use position_dodge2 with 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

Original answer

As already commented you can do it like in this answer: Transform A and C to factors and add unseen variables using tidyr's complete. Since the recent ggplot2 version it is recommended to use geom_col instead of geom_bar in cases of 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

Or work with an interaction term:

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

enter image description here

And by finally transforming the interaction to numeric you can setup the x-axis according to your desired output. By grouping (group_by) you can calculate the matching breaks. The fancy stuff with the {} around the ggplot argument is neseccary to directly use the vaiables Breaks and C within the pipe.

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

Edit:

Another approach would be to use facets. Using space = "free_x" allows to set the width proportional to the length of the x scale.

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

You can also plot the facet labels on the bottom using switch and remove x axis labels

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

like image 174
Roman Avatar answered Oct 04 '22 16:10

Roman