Danny
Danny

Reputation: 574

Formatting GGplot stacked barplot

I am making a set of scorecards where I am generating a set of graphs that show the distribution of responses from a survey and also where the response for a specific company falls. I need to modify the formatting of a graph, a stacked barchart, and add a few features I’ve outlined below. I’ve already spent a few hours getting my chart to where it is now and would appreciate your help with the features I outline below.

Data is

Data<-data.frame(Reviewed = c("Annually", "Annually", "Hourly", "Monthly", "Weekly","Monthly","Weekly","Other","Other","Monthly","Weekly"),Company=c("a","b","c","d","e","f","g","h","i","j","k"),Question="Q1")

So far I’ve developed this

ggplot(Data, aes(x="Question", fill=Reviewed)) + geom_bar(position='fill' ) +
  coord_flip()

enter image description here

I would like to do the following:

This is what I'm hoping the finished graph will look like. enter image description here

Upvotes: 0

Views: 235

Answers (1)

Mike O&#39;Brien
Mike O&#39;Brien

Reputation: 156

There's a lot to unpack here, so I'll break it down bit by bit:

Order the variables so they are arranged on plot as follows: Annually,Monthly,Weekly,Hourly,Other

Assign "Reviewed" as an ordered factor. I'm reversing the order here since it wants to plot the "lowest" factor first (to the left).

Data$Reviewed <- factor(Data$Reviewed, 
                        levels = rev(c('Annually', 'Monthly', 'Weekly', 'Hourly', 'Other')),
                        ordered = T)

ggplot(Data, aes(x="Question", fill=Reviewed)) + geom_bar(position='fill' ) +
  coord_flip()

enter image description here

Express the y axis in terms of percent. I.e. 0.25 turns into 25%

Use scale_y_continuous(labels = scales::percent) to adjust the labels. I believe that the scales was pulled in when you installed ggplot2.

ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  scale_y_continuous(labels = scales::percent) +
  coord_flip()

enter image description here

Move y-axis directly underneath the bar. Remove gray background

These are done all at once by adding expand = F to coord_flip.

ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(expand = F)

enter image description here

Remove the legend...

Add theme(legend.position = 'none').

ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(expand = F) +
  theme(legend.position = 'none')

enter image description here

but move the terms underneath the respective part of the graph on a diagonal slant.

This is tougher and takes a good amount of fiddling.

  1. Use geom_text to make the labels
  2. Calculate the position along the bar using the 'count' stat
  3. Move the labels to the bottom of the plot by providing a fake x coordinate
  4. Align the labels in the center of the bars using position_stack, and make them abut the x axis using hjust.
  5. Add angle.
  6. Use clip = 'off' in coord_flip to make sure that these values are not cut out since they're outside the plotting area.
  7. Fiddle with the x limits to crop out empty plotting area.
  8. Adjust the plot margin in theme to make sure everything can be seen.
ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  geom_text(aes(label = Reviewed, x = 0.45,
                y = stat(..count../sum(..count..))), stat = 'count',
            position = position_stack(0.5),
            hjust = 0, 
            angle = 45) +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(xlim = c(0.555, 1.4), clip = 'off',expand = F) +
  theme(plot.margin = margin(0, 0, 35, 10),
        legend.position = 'none')

enter image description here

Add a black line that cuts down the 50% mark

Use geom_hline(yintercept = 0.5); remember that it's a "horizontal" line since the coordinates are flipped.

ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  geom_text(aes(label = Reviewed, x = 0.45,
                y = stat(..count../sum(..count..))), stat = 'count',
            position = position_stack(0.5),
            hjust = 0, 
            angle = 45) +
  geom_hline(yintercept = 0.5) +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(xlim = c(0.555, 1.4), clip = 'off',expand = F) +
  theme(plot.margin = margin(0, 0, 20, 10),
        legend.position = 'none')

enter image description here

Add a dot in at the midpoint of the stack for the value of company “e”.

This is pretty hack-y. Using the same y values as in geom_text, use geom_point to plot a point for every value of Reviewed, then use position_stack(0.5) to nudge them to the center of the bar. Then use scale_color_manual to only color "Weekly" values (which is the corresponding value of Reviewed for Company "e"). I'm sure there's a way to do this more programmatically.

ggplot(Data, aes(x="Question", fill=Reviewed)) +
  geom_bar(position = 'fill') +
  geom_text(aes(label = Reviewed, x = 0.45,
                y = stat(..count../sum(..count..))), stat = 'count',
            position = position_stack(0.5),
            hjust = 0, 
            angle = 45) +
  geom_hline(yintercept = 0.5) +
  geom_point(aes(y = stat(..count../sum(..count..)),
                 color = Reviewed), stat = 'count',
             position = position_stack(0.5), size = 5) +
  scale_color_manual(values = 'black', limits = 'Weekly') +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(xlim = c(0.555, 1.4), clip = 'off',expand = F) +
  theme(plot.margin = margin(0, 0, 20, 10),
        legend.position = 'none')

enter image description here

This is what I'm hoping the finished graph will look like.

Prettying things up:

ggplot(Data, aes(x="Question", fill = Reviewed)) +
  geom_bar(position = 'fill') +
  geom_text(aes(label = Reviewed, x = 0.45,
                y = stat(..count../sum(..count..))), stat = 'count',
            position = position_stack(0.5),
            hjust = 0, 
            angle = 45) +
  geom_hline(yintercept = 0.5) +
  geom_point(aes(y = stat(..count../sum(..count..)),
                 color = Reviewed), stat = 'count',
             position = position_stack(0.5), size = 5) +
  scale_color_manual(values = 'black', limits = 'Weekly') +
  scale_y_continuous(labels = scales::percent) +
  coord_flip(xlim = c(0.555, 1.4), clip = 'off', expand = F) +
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(plot.margin = margin(0, 0, 35, 10),
        legend.position = 'none')

enter image description here

Upvotes: 2

Related Questions