PinkyL
PinkyL

Reputation: 351

Facet Wrap ggplot geom_col has different bar widths

I'm creating a graph that has bars using geom_col, with gem_point (line) to compare performance against a "benchmark." The performance measures belong to different domains, so I used facet_wrap to visually split the domains into groups so it's easier to view. However, because there are different numbers of measures per domain, the bar "heights"/widths are different.

    df = data.frame(
  measure = c("Measure A","Measure B","Measure C","Measure D","Measure E","Measure F","Measure G"),
  domain = c("Efficiency","Efficiency","Satisfaction","Satisfaction", "Satisfaction","Satisfaction","Satisfaction"),
  overall = c(56, 78, 19, 87, 45, 51, 19),
  company = c(45, 89, 18, 98, 33, 55, 4)
)

ggplot(df %>% mutate(fill = ifelse(overall > company, " Below Overall  "," Above Overall  ")), aes(measure)) + 
  geom_col(aes(y=company, fill= fill)) + geom_point(aes(y=overall, color="overall"), size=6, shape=124) + coord_flip() + 
  scale_color_manual(values=c("grey3"),labels=c("Overall")) + scale_fill_manual(values=c(" Below Overall  "="lightpink2"," Above Overall  "="lightblue2")) + facet_wrap(~domain, ncol=1, scales="free_y")

enter image description here

I saw from an older question posted about calculating the number of bars per domain, and then multiplying the width by a factor. But I can't figure out how to do that and apply it to my graph.

Upvotes: 2

Views: 3304

Answers (2)

hrbrmstr
hrbrmstr

Reputation: 78792

I end up needing this quite a bit and have switched to geom_segment() for many of the traditional bar plots.

library(hrbrthemes)
library(ggplot2)
library(dplyr)

data_frame(
  measure = c("Measure A","Measure B","Measure C","Measure D","Measure E","Measure F","Measure G"),
  domain = c("Efficiency","Efficiency","Satisfaction","Satisfaction", "Satisfaction","Satisfaction","Satisfaction"),
  overall = c(56, 78, 19, 87, 45, 51, 19),
  company = c(45, 89, 18, 98, 33, 55, 4)
) %>% 
  mutate(fill = ifelse(overall > company, " Below Overall  "," Above Overall  ")) %>% 
  ggplot() +
  geom_segment(aes(x=company, xend=0, y=measure, yend=measure, color=fill), size=4) + 
  geom_point(aes(x=overall, y=measure, color="Overall"), size=6, shape=124, show.legend = FALSE) +
  scale_x_comma() +
  scale_color_manual(name=NULL,
                     values=c(`Overall`="grey3", " Below Overall  " = "lightpink2",
                              " Above Overall  " = "lightblue2")) +
  facet_wrap(~domain, ncol=1, scales="free_y") +
  labs(x="Measure", y=NULL) +
  theme_ipsum(grid="X")

enter image description here

Another benefit of this is no need for coord_flip().

If you really need the thin, separate Overall guide, there's a way to do that, too.

UPDATE

Facet ordering and spacing…

library(hrbrthemes)
library(ggplot2)
library(dplyr)
library(grid)
library(gridExtra)

data_frame(
  measure = c("Measure A","Measure B","Measure C","Measure D","Measure E","Measure F","Measure G"),
  domain = c("Efficiency","Efficiency","Satisfaction","Satisfaction", "Satisfaction","Satisfaction","Satisfaction"),
  overall = c(56, 78, 19, 87, 45, 51, 19),
  company = c(45, 89, 18, 98, 33, 55, 4)
) %>% 
  mutate(fill = ifelse(overall > company, " Below Overall  "," Above Overall  ")) %>% 
  arrange(desc(measure)) %>% 
  mutate(measure = factor(measure, levels=unique(measure))) %>% 
  ggplot() +
  geom_segment(aes(x=company, xend=0, y=measure, yend=measure, color=fill), size=4) + 
  geom_point(aes(x=overall, y=measure, color="Overall"), size=6, shape=124, show.legend = FALSE) +
  scale_x_comma() +
  scale_color_manual(name=NULL,
                     values=c(`Overall`="grey3", " Below Overall  " = "lightpink2",
                              " Above Overall  " = "lightblue2")) +
  facet_wrap(~domain, ncol=1, scales="free_y") +
  labs(x="Measure", y=NULL) +
  theme_ipsum(grid="X") -> gg

gb <- ggplot_build(gg)
gt <- ggplot_gtable(gb)

gt$heights[[7]] <- unit(2/5, "cm") # top panel has 2 out of 5 factors vs 5 out of 5 in the bottom one

grid.newpage() ;
grid.draw(gt)

enter image description here

Upvotes: 2

missuse
missuse

Reputation: 19716

Perhaps using facet_grid with space = "free" would be satisfactory:

ggplot(df %>% mutate(fill = ifelse(overall > company, " Below Overall  "," Above Overall  ")), aes(measure)) + 
  geom_col(aes(y=company, fill= fill)) + 
  geom_point(aes(y=overall, color="overall"), size=6, shape=124) +
  scale_color_manual(values=c("grey3"),labels=c("Overall")) +
  scale_fill_manual(values=c(" Below Overall  "="lightpink2"," Above Overall  "="lightblue2")) + 
  facet_grid(domain~., scales="free", space = "free") +
  coord_flip()

enter image description here

Upvotes: 6

Related Questions