Multiple Colour Scales in One Stacked Bar Plot Using Ggplot

Multiple colour scales in one stacked bar plot using ggplot

# load relevant packages
library(scales)
library(grid)
library(ggplot2)
library(plyr)

# assume your data is called df
# order data by year, ideology and party
df2 <- arrange(df, year, ideology, party)

########################################

# create one colour palette per ideology

# count number of parties per ideology
tt <- with(df2[!duplicated(df2$party), ], table(ideology))

# conservative parties blues
# progressive parties green
# moderate parties red
blue <- brewer_pal(pal = "Blues")(tt[names(tt) == "c"])
green <- brewer_pal(pal = "Greens")(tt[names(tt) == "p"])
red <- brewer_pal(pal = "Reds")(tt[names(tt) == "m"])

# create data on party and ideology
party_df <- df2[!duplicated(df2$party), c("party", "ideology")]

# set levels of ideologies; c, p, m
party_df$ideology <- factor(party_df$ideology, levels = c("c", "p", "m"))

# order by ideology and party
party_df <- arrange(party_df, ideology, party)

# add fill colours
party_df$fill <- c(blue, green, red)

# set levels of parties based on the order of parties in party_df
party_df$party <- factor(party_df$party, levels = party_df$party)

# use same factor levels for parties in df2
df2$party <- factor(df2$party, levels = party_df$party)

##################################

# Alternative 1. Plot with one legend

g1 <- ggplot(data = df2, aes(x = as.factor(year),
y = seats,
fill = party)) +
geom_bar(stat = "identity", position = "fill") +
labs(x = "year", y = "% of seats for municipality") +
coord_cartesian(ylim = c(0, 1)) +
scale_fill_manual(values = party_df$fill, name = "Parties") +
theme_classic()
g1

Sample Image

#####################################3
# alt 2. Plot with separate legends for each ideology

# create separate plots for each ideology to get legends

# conservative parties blue
cons <- ggplot(data = df2[df2$ideology == "c", ],
aes(x = as.factor(year),
y = seats,
fill = party)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = blue, name = "Conservative parties" )

# extract 'cons' legend
tmp <- ggplot_gtable(ggplot_build(cons))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend_cons <- tmp$grobs[[leg]]

# progressive parties green
prog <- ggplot(data = df2[df2$ideology == "p", ],
aes(x = as.factor(year),
y = seats,
fill = party)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = green, name = "Progressive parties" )

# extract 'prog' legend
tmp <- ggplot_gtable(ggplot_build(prog))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend_prog <- tmp$grobs[[leg]]

# moderate parties red
mod <- ggplot(data = df2[df2$ideology == "m", ],
aes(x = as.factor(year),
y = seats,
fill = party)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = red, name = "Moderate parties" )

# extract 'mod' legend
tmp <- ggplot_gtable(ggplot_build(mod))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend_mod <- tmp$grobs[[leg]]

#######################################

# arrange plot and legends

# define plotting regions (viewports) for plot and legends
vp_plot <- viewport(x = 0.25, y = 0.5,
width = 0.5, height = 1)

vp_legend_cons <- viewport(x = 0.66, y = 0.87,
width = 0.5, height = 0.15)

vp_legend_prog <- viewport(x = 0.7, y = 0.55,
width = 0.5, height = 0.60)

vp_legend_mod <- viewport(x = 0.75, y = 0.2,
width = 0.5, height = 0.30)

# clear current device
grid.newpage()

# add objects to the viewports
# plot without legend
print(g1 + theme(legend.position = "none"), vp = vp_plot)
upViewport(0)

# legends
pushViewport(vp_legend_cons)
grid.draw(legend_cons)
upViewport(0)

pushViewport(vp_legend_prog)
grid.draw(legend_prog)
upViewport(0)

pushViewport(vp_legend_mod)
grid.draw(legend_mod)

Sample Image

Using multiple color scales in stacked bar plots with ggplot

Edit: this is probably the more correct way to do it, using interaction to assign unique colors for each factor pair:

ggplot(fruit, aes(Fruit)) +
geom_bar( aes(fill=interaction(Color,Ripe), color=Color), stat="count", position="fill")+
scale_y_continuous(labels=scales::percent)+
scale_fill_manual(values=c("pink","lightgreen","lightblue","red", "green","blue"))+
theme_bw()

Sample Image

Map Color to fill, and Ripe to alpha:

ggplot(fruit, aes(Fruit)) +
geom_bar(stat="count", position="fill",
aes(fill=Color, color=Color,alpha=Ripe)) +
scale_y_continuous(labels=scales::percent)+
scale_alpha_discrete(range=c(.5,1))+
theme_bw()

Sample Image

How to change colours in multiple stacked bar charts in R?

You are mapping a numeric on the fill aes. Hence you get a continuous fill color scale. If you want to fill your bars by answer map this column on the fill aes. But as this column is a numeric too, convert it to factor to make scale_fill_manual work:

library(ggplot2)

p <- ggplot(sample1, aes(y = proportion, x = group, fill = factor(answer))) +
geom_bar(position = "stack", stat = "identity") +
scale_fill_manual(values = c("#E7344E", "#0097BF", "#E7344E", "#0097BF", "#E7344E")) +
facet_grid(~"") +
theme_minimal()

p1 <- ggpubr::ggpar(p, xlab = F, ylab = F, legend = "", ticks = F)

p1 + geom_text(aes(label = proportion),
position = position_stack(vjust = 0.5),
check_overlap = T,
colour = "white"
)

Sample Image

DATA

sample1 <- structure(list(group = c(
"first", "first", "first", "first",
"first", "second", "second", "second", "second", "second", "third",
"third", "third", "third", "third", "fourth", "fourth", "fourth",
"fourth", "fourth"
), answer = c(
1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L,
4L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L
), count = c(
67L,
119L, 6L, 116L, 45L, 102L, 197L, 10L, 232L, 55L, 49L, 143L, 1L,
142L, 44L, 45L, 93L, 3L, 118L, 44L
), proportion = c(
19, 33.7,
1.7, 32.9, 12.7, 17.1, 33.1, 1.7, 38.9, 9.2, 12.9, 37.7, 0.3,
37.5, 11.6, 14.9, 30.7, 1, 38.9, 14.5
)), class = "data.frame", row.names = c(
NA,
-20L
))

Create a different color scale for each bar in a ggplot2 stacked bar graph

The various color palettes above do not consistently transfer to the different classes - instead they plot according to the named vector (a,b,c...) and thus are split across the various classes. See ??scale_fill_manual for details.

In order to "match" them to each set of bars, we need to order the data.frame by class, and align the color palettes appropriately with the names.

Create repeating palettes to test correct (expected) ordering.

 repeating.pal = mapply(function(x,y) brewer.pal(x,y), ncol,        c("Set2","Set2","Set2","Set2"))

repeating.pal[[2]] = repeating.pal[[2]][1:2] # We only need 2 colors but brewer.pal creates 3 minimum

repeating.pal = unname(unlist(repeating.pal))

Sort the data according to class (the order we want the colors to remain in!)

 df_count_sorted <- df_count[order(df_count$drug_class),]

Copy the original ordering of the drug names.

 df_count_sorted$labOrder <- df_count$drug_name

Add in test color palette.

 df_count$colours<-repeating.pal

Alter the plot routine, with fill = labOrder.

ggplot(data = df_sorted, aes(x=drug_class, y=freq, fill=labOrder) ) + 
geom_bar(stat="identity", colour="black", lwd=0.2) +
geom_text(aes(label=paste0(drug_name,": ", freq), y=cum.freq), colour="grey20") +
scale_fill_manual(values=df_sorted$colours) +
guides(fill=FALSE)

Palette follows expected order

Side by side stacked barplots with different colors

One option to achieve your desired result would be the ggnewscale package which allows for multiple scales for the same aesthetic. To make this work you have to add the bars for each small.cat via a separate geom_col:

library(ggplot2)
library(ggnewscale)

ggplot(data = synth.data, aes(small.cat, y = response.var)) +
geom_col(data = subset(synth.data, small.cat == "a"), aes(color = response.var, fill = response.var), position = "fill") +
new_scale_fill() +
new_scale_color() +
geom_col(data = subset(synth.data, small.cat == "b"), aes(color = response.var, fill = response.var), position = "fill") +
scale_fill_gradient(low = "red", high = "darkred") +
scale_color_gradient(low = "red", high = "darkred") +
facet_wrap(~year)

Sample Image

Stacked barplot with colour gradients for each bar

I have made a function ColourPalleteMulti, which lets you create a multiple colour pallete based on subgroups within your data:

ColourPalleteMulti <- function(df, group, subgroup){

# Find how many colour categories to create and the number of colours in each
categories <- aggregate(as.formula(paste(subgroup, group, sep="~" )), df, function(x) length(unique(x)))
category.start <- (scales::hue_pal(l = 100)(nrow(categories))) # Set the top of the colour pallete
category.end <- (scales::hue_pal(l = 40)(nrow(categories))) # set the bottom

# Build Colour pallette
colours <- unlist(lapply(1:nrow(categories),
function(i){
colorRampPalette(colors = c(category.start[i], category.end[i]))(categories[i,2])}))
return(colours)
}

Essentially, the function identifies how many different groups you have, then counts the number of colours within each of these groups. It then joins together all the different colour palettes.

To use the palette, it is easiest to add a new column group, which pastes together the two values used to make the colour palette:

library(ggplot2)

# Create data
df <- diamonds
df$group <- paste0(df$color, "-", df$clarity, sep = "")

# Build the colour pallete
colours <-ColourPalleteMulti(df, "color", "clarity")

# Plot resultss
ggplot(df, aes(color)) +
geom_bar(aes(fill = group), colour = "grey") +
scale_fill_manual("Subject", values=colours, guide = "none")

Sample Image


Edit:

If you want the bars to be a different colour within each, you can just change the way the variable used to plot the barplot:

# Plot resultss
ggplot(df, aes(cut)) +
geom_bar(aes(fill = group), colour = "grey") +
scale_fill_manual("Subject", values=colours, guide = "none")

Sample Image


A Note of Caution: In all honesty, the dataset you have want to plot probably has too many sub-categories within it for this to work.

Also, although this is visually very pleasing, I would suggest avoiding the use of a colour scale like this. It is more about making the plot look pretty, and the different colours are redundant as we already know which group the data is in from the X-axis.

Changing y-axis scale to counts using multiple color scales in stacked bar plot

Your ggplot2 code is somewhat overcomplicated. You have to remove scale_y_continuous(labels = scales::percent) to get rid of percentages. And remove stat = "count", position = "fill" to get counts of observations (ie. use simple geom_bar()).

# Using OPs data
library(ggplot2)
ggplot(fruit, aes(Color, fill = Color, alpha = Ripe)) +
geom_bar(color = "black") +
scale_alpha_discrete(range = c(1, 0.6)) +
theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank()) +
guides(fill = guide_legend(override.aes = list(colour = NA)))

Sample Image

Also, you specify color = Color and then overwrite it with scale_color_manual(values = c("Black", "Black", "Black"))

Set colours for both dimensions of a stacked bar plot

This could be achieved like so:

  1. The issue with the alpha is that you mapped alph on alpha. However, the values for alpha are chosen by ggplot. To set specific alpha values you can e.g. map week on alpha and use scale_alpha_manual to set the alpha values.

  2. To add your colors add the colors as a column to your data, map this column on fill and make use of scale_fill_identity.

library(tidyverse)

teams <- data.frame(
team = factor(LETTERS[1:6], levels = rev(LETTERS[1:6]), ordered = T),
goal = c(200, 160, 200, 250, 220, 180))

weeks <- teams %>%
slice(rep(1:n(), each = 3)) %>%
mutate(week = factor(rep(c(1:3), 6), levels = c(3:1), ordered = T),
alph = 1 - 0.1 * as.numeric(week),
value = c(40, 55, 54, 34, 36, 34, 31, 46, 46, 59, 63, 67, 31, 54, 52, 38, 46, 44),
week_progress = value / goal)

teams <- teams %>%
inner_join(weeks %>% group_by(team) %>% summarise(progress = sum(value)), by = 'team') %>%
mutate(team_progress = progress / goal)
#> `summarise()` ungrouping output (override with `.groups` argument)

ggplot(weeks, aes(x = week_progress, y = team, fill = team)) +
geom_bar(aes(alpha = week), stat = 'identity', position = position_stack(), color = 'black', show.legend = F) +
scale_alpha_manual(values = c(`1` = 0.7, `2` = 0.8, `3` = 0.9)) +
geom_text(aes(group = week, label = scales::percent_format(accuracy = 0.1)(week_progress), x = week_progress),
position = position_stack(vjust = 0.5), color = 'blue')

Sample Image

library(RColorBrewer)

pal <- c(brewer.pal(9, 'YlOrRd')[4:6], brewer.pal(9, 'YlGnBu')[4:6], brewer.pal(9, 'RdPu')[4:6], brewer.pal(9, 'PuBuGn')[4:6], brewer.pal(9, 'Greens')[4:6], brewer.pal(9, 'BrBG')[4:2] )

weeks <- mutate(weeks, cols = pal)

ggplot(weeks, aes(x = week_progress, y = team, fill = cols)) +
geom_bar(stat = 'identity', position = position_stack(), color = 'black', show.legend = F) +
scale_fill_identity() +
geom_text(aes(group = week, label = scales::percent_format(accuracy = 0.1)(week_progress), x = week_progress),
position = position_stack(vjust = 0.5), color = 'blue')

Sample Image



Related Topics



Leave a reply



Submit