Variable width bar using geom_col and position_dodge?

I don't know if this is possible using geom_col(), but I'm wondering if it is possible to change the widths of the dodged bars in the below plot. For example, I would like to set the width of all t3 bars to double the width of t1 and t2 bars

library(ggplot2)
library(tibble)

df <- tribble(
  ~loc, ~time, ~value,
  "a",  "t1", 4,
  "a",  "t2", 6,
  "a",  "t3", 7,
  "b",  "t1", 2,
  "b",  "t2", 4,
  "b",  "t3", 8,
  "c",  "t1", 6,
  "c",  "t2", 4,
  "c",  "t3", 2
)

df %>% 
  ggplot(aes(x = loc, y = value, fill = time)) +
  geom_col(position = "dodge")

Created on 2020-05-26 by the reprex package (v0.3.0)

I would call this "proof of concept" at best. The x axis labels can be adjusted with scale_x_continuous, I think.

library(tibble)
library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
df <- tribble(
  ~loc, ~time, ~value,
  "a",  "t1", 4,
  "a",  "t2", 6,
  "a",  "t3", 7,
  "b",  "t1", 2,
  "b",  "t2", 4,
  "b",  "t3", 8,
  "c",  "t1", 6,
  "c",  "t2", 4,
  "c",  "t3", 2)

df <- df %>% mutate(locInt = case_when(loc == "a" ~ 1,
                                       loc == "b" ~ 2,
                                       loc == "c" ~ 3),
                    locFine = case_when(time == "t1" ~ locInt - 0.2,
                                        time == "t2" ~ locInt,
                                        time == "t3" ~ locInt + 0.3))

df %>% filter(time %in% c("t1", "t2")) %>% 
  ggplot(aes(x = locFine, y = value, fill = time)) +
  geom_col(position = position_identity(), orientation = "x", width = 0.2) +
  geom_col(data = filter(df, time == "t3"), orientation = "x", width = 0.4)

Created on 2020-05-26 by the reprex package (v0.3.0)

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

Yeah, that definitely get's the job done! I made a couple changes to your code and placed the labels in the middle of each set of bars. I'm sure there is a generalization here that would take into account the width of the bars and the placement of the label, but this works great. Thank you!

library(tidyverse)

df <- tribble(
  ~loc, ~time, ~value,
  "a",  "t1", 4,
  "a",  "t2", 6,
  "a",  "t3", 7,
  "b",  "t1", 2,
  "b",  "t2", 4,
  "b",  "t3", 8,
  "c",  "t1", 6,
  "c",  "t2", 4,
  "c",  "t3", 2
  )

df <- df %>% 
  mutate(
    gap = case_when(
      time == "t1" ~ 0.5,
      time == "t2" ~ 0.2,
      time == "t3" ~ 0.3
    ),
    x = cumsum(gap) + 0.3
    )

df %>%
  filter(time %in% c("t1", "t2")) %>% 
  ggplot(aes(x = x, y = value, fill = time)) +
  geom_col(orientation = "x", width = 0.2) +
  geom_col(data = filter(df, time == "t3"), orientation = "x", width = 0.4) +
  scale_x_continuous(
    breaks = df$x[df$time == "t2"] + 0.1,
    labels = df$loc[df$time == "t2"]
  )

Created on 2020-05-27 by the reprex package (v0.3.0)