Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2 - Stacked bar with different width

Tags:

graph

r

ggplot2

I want to produce a reverse pyramid graph where the bar stacked on each other with different width.

1st, I have a stacked bar chart as below code sample

library(dplyr)
library(ggplot2)
sample <- data_frame(x=c(1, 1, 1, 1, 2, 2, 2, 2),
                     y=c(5,10,15, 20, 10, 5, 20, 10),
                     w=c(1, 2, 3, 4, 1, 2, 3, 4),
                     group=c("a", "b", "c", "d", "a", "b", "c", "d"))

ggplot() +
    geom_bar(data=sample,
             aes(x=x,y=y,group=group, fill=group),
             stat="identity", position=position_stack())

enter image description here

Then I added the width to aes so the one with lower w value will be smaller while they still stacked on each other. However, the bars didn't stack with warnings.

ggplot() +
geom_bar(data=sample,
         aes(x=x,y=y,group=group, fill=group, width=w/5),
         stat="identity", position=position_stack())

enter image description here

Warning: Ignoring unknown aesthetics: width
Warning message:
position_stack requires non-overlapping x intervals 

Any helps on make bar plot stacked or ideas on different plot type that can cover similar concepts would be highly appreciated. Thanks!

like image 719
Sinh Nguyen Avatar asked Dec 05 '25 03:12

Sinh Nguyen


2 Answers

It's a bit of a hack.

I'll use geom_rect() instead of real columns. As such I need to create data.frame() with precalculated positions for rectangle boundaries.

df_plot <- sample %>% 
  arrange(desc(group)) %>% # Order so lowest 'groups' firtst
  group_by(x) %>% 
  mutate(yc = cumsum(y), # Calculate position of "top" for every rectangle
         yc2 = lag(yc, default = 0) ,# And position of "bottom"
         w2 = w/5) # Small scale for width


# Plot itself

ggplot(df_plot) +
  geom_rect(
    aes(xmin = x - w2 / 2, xmax = x + w2 / 2,
        ymin = yc, ymax = yc2,
        group = group, fill=group))

Resulting plot: enter image description here

like image 144
Andrey Kolyadin Avatar answered Dec 07 '25 19:12

Andrey Kolyadin


A rather lengthy version with ribbons

library(dplyr)
library(ggplot2)
sample <- data_frame(x=c(1, 1, 1, 1, 2, 2, 2, 2),
                     y=c(5,10,15, 20, 10, 5, 20, 10),
                     w=c(1, 2, 3, 4, 1, 2, 3, 4),
                     group=c("a", "b", "c", "d", "a", "b", "c", "d"))

# make factors for non-numeic items
sample$x <- factor(sample$x)
sample$group <- factor(sample$group)

# calcualte cumulative sums
sample2 <- sample %>%
  group_by(x) %>%
  arrange(desc(group)) %>%
  mutate(ycum=cumsum(y)) %>%
  ungroup()  %>%
  select(x, group, ycum, w) 

# Ffor each point, make another row lagged  
sample2lead <- sample2 %>%
  group_by(x) %>%
  mutate(ycum = lag(ycum, default=0), w=lag(w, default=max(sample2$w))) %>%
  ungroup() %>%
  select(x, group, ycum, w) 

# combine   
combined <- bind_rows(sample2, sample2lead) %>%
  arrange(x, ycum, desc(group))


# plot a ribbon forming trapezoids
ggplot() +
  geom_ribbon(data=combined,
             aes(x=ycum, ymin=-w/2, ymax=w/2, fill=group)) +
  coord_flip() +
  facet_grid(~x)

enter image description here

like image 26
Andrew Lavers Avatar answered Dec 07 '25 18:12

Andrew Lavers



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!