ggplot2: Display shift in stacked histogram [duplicate] - r

This question already has answers here:
Connect stack bar charts with multiple groups with lines or segments using ggplot 2
(2 answers)
Closed 3 days ago.
I am trying to replicate a figure used in Stroke research to display results of clinical trials.
I have included a figure down below as an example of the end result.
In a nutshell, the figure is a stacked histogram in percentage for two conditions (such as drug vs placebo).
To indicate the shift, line are drawn from one bar to the next to emphasize the effect of treatment.
Image:
Image from published paper here https://doi.org/10.3389/fneur.2022.973095
The data will be something like:
mRS Drug1 Drug2
0 10% 8%
1 12% 10%
2 22% 18%
3 9% 14%
4 15% 16%
5 12% 12%
6 20% 22%
Creating an horizontal stacked histogram is relatively simple, but I have no clue on how to create the line between the horizontal bars.
Any helps or just hint toward an appropriate method to do so would be much appreciated!
Thanks,
I tried to look on google for images of "horizontal stacked histogram" and apparently no one published something like that ;-)

A very similar approach to stefan here, but trying to match the original aesthetic more closely:
library(tidyverse)
df %>%
mutate(across(Drug1:Drug2, ~ as.numeric(sub('%', '', .x))/100)) %>%
pivot_longer(-mRS, names_to = 'Drug', values_to = 'percent') %>%
mutate(mRS = factor(mRS), y = as.numeric(factor(Drug))) %>%
ggplot(aes(percent, y, fill = mRS)) +
geom_segment(data = . %>% select(-y) %>%
pivot_wider(names_from = 'Drug', values_from = 'percent') %>%
mutate(across(Drug1:Drug2, cumsum)),
aes(y = 1.2, yend = 1.8, x = Drug1, xend = Drug2),
alpha = 0.3) +
geom_col(orientation = 'y', position = position_stack(reverse = TRUE),
width = 0.4, col = 'gray60', linewidth = 0.2) +
geom_text(data = . %>%
group_by(Drug) %>%
mutate(value = cumsum(percent),
stackval = lag(value, default = 0) + 0.5 * percent),
aes(x = stackval, label = scales::percent(percent))) +
scale_fill_brewer(palette = 'Blues') +
scale_y_continuous(NULL, breaks = 1:2, labels = c('Drug 1', 'Drug 2'),
expand = c(0.3, 0)) +
scale_x_continuous(NULL, labels = scales::percent, expand = c(0, 0)) +
guides(fill = guide_legend(nrow = 1, byrow = TRUE)) +
theme_minimal() +
theme(legend.position = 'bottom',
panel.grid = element_blank(),
axis.line.y = element_line(color = 'gray50'),
plot.margin = margin(20, 20, 20, 20))

Besides some data wrangling and cleaning drawing the connecting lines requires setting the start and end positions right.
library(ggplot2)
library(tidyr)
library(dplyr)
width <- .6
dat <- dat |>
pivot_longer(-mRS) |>
mutate(value = readr::parse_number(value) / 100) |>
mutate(
name_num = as.numeric(factor(name)),
nudge_y = width / 2 * if_else(name_num == 1, 1, -1)
)
ggplot(dat, aes(value, name, fill = factor(mRS))) +
geom_col(aes(color = factor(mRS)), width = width, position = position_stack(reverse = TRUE)) +
geom_line(aes(y = name_num + nudge_y, group = mRS), position = position_stack(reverse = TRUE), orientation = "y") +
geom_text(aes(label = scales::percent(value, accuracy = 1)), position = position_stack(reverse = TRUE, vjust = .5)) +
scale_x_continuous(labels = scales::label_percent()) +
scale_fill_brewer(aesthetics = c("color", "fill"), guide = guide_legend(nrow = 1)) +
theme(legend.position = "bottom") +
labs(x = NULL, y = NULL, fill = NULL, color = NULL)
DATA
dat <- structure(list(mRS = 0:6, Drug1 = c(
"10%", "12%", "22%", "9%",
"15%", "12%", "20%"
), Drug2 = c(
"8%", "10%", "18%", "14%", "16%",
"12%", "22%"
)), class = "data.frame", row.names = c(NA, -7L))

Related

How to make A racing Bar Chart in R

I have a dataset that has a column of years from 1965 to 2020 and Teams that have won the championship in the respective years.
I am trying to create a racing bar chart and so far I have been struggling to create the required dataset to create the animated GIF
df1 <- df %>%
group_by(Team) %>%
mutate(cups = 1:n()) %>%
ungroup() %>%
group_by(Year) %>% spread(Year, cups) %>%
replace(is.na(.),0)
which brings a result of the following format.
Kindly assist in how I should go about completing this racing bar chart as I have browsed through several resources but I still cant seem to crack it..
Check if this work, as Jon mentioned you need to pivot your data using pinot_longer
df1 <- pivot_longer(df, -1, names_to = 'Year') %>%
rename(Team= ï..Team) %>%
mutate(Year = as.numeric(substr(Year, 2, 5)))
Then this should create the racing barchart"
df1 <- df1 %>%
group_by(Year) %>%
# The * 1 makes it possible to have non-integer ranks while sliding
mutate(rank = min_rank(-value) * 1,
Value_rel = value/value[rank==1],
Value_lbl = paste0(" ",value)) %>%
filter(rank <=10) %>% # This would show the top 10 teams
ungroup()
p <- ggplot(df1, aes(rank, group = Team,
fill = as.factor(Team), color = as.factor(Team))) +
geom_tile(aes(y = value/2,
height = value,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(Team, " ")), vjust = 0.2, hjust = 2) +
geom_text(aes(y=value,label = Value_lbl, hjust=0)) +
coord_flip(clip = "off", expand = FALSE) +
scale_y_continuous(labels = scales::comma) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state}', x = "", y = "Your Title",
caption = "Your Caption") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(Year, transition_length = 4, state_length = 1) +
ease_aes('cubic-in-out')
animate(p, 200, fps = 10, duration = 40, width = 800, height = 600, renderer = gifski_renderer("gganim.gif"))
anim_save("YourPath//Name.gif")

gganimate horizontal column chart won't transition bars smoothly

I am creating a bar chart race animation using geom_colh from the ggstance package. Right now, the animation is very choppy and doesn't appear to be one continuous animation, instead just one image after another. Below is what the current animation looks like:
Instead, I want the bars to "glide" from one position to another when they pass each other. Below is the reprex of the code I currently have:
library(tidyverse)
library(dplyr)
library(ggplot2)
library(gganimate)
library(ggstance)
library(zoo)
library(gifski)
library(shadowtext)
stats <- read_csv(url("https://raw.githubusercontent.com/samhoppen/Fantasy-Evaluator/main/Data/Animation%20Test%20Data.csv")) %>%
mutate(unique_id = paste0(player_name, recent_team))
all_weeks <- read_csv(url("https://raw.githubusercontent.com/samhoppen/Fantasy-Evaluator/main/Data/Animation%20Weeks%20Data.csv"))
NFL_pri <- stats$team_color
names(NFL_pri) <- stats$unique_id
NFL_sec <- stats$team_color2
names(NFL_sec) <- stats$unique_id
rb_ani <- ggplot(data = stats, aes(group = player_name)) +
geom_colh(aes(x = tot_fpts, y = rank, color = unique_id, fill = unique_id), position = 'identity',
size = 2, width = 0.8) +
scale_x_continuous(expand = expansion(mult = c(0, 0.05))) +
scale_y_reverse(expand = expansion(mult = c(0.01, 0.01)))+
geom_shadowtext(aes(x = name_loc, y = rank, label = player_name, color = unique_id),
bg.color = 'white', size = 5.5, na.rm = T, bg.r = 0.075, show.legend = FALSE) +
scale_color_manual(values = NFL_sec)+
scale_fill_manual(values = NFL_pri)+
labs(title = "Highest-scoring Fantasy Running Backs of the Past Decade",
subtitle = paste0("{all_weeks$week_name[as.numeric(previous_state)]}"),
caption = "Figure: #SamHoppen | Data: #nflfastR",
y = "",
x = "Total Fantasy Points")+
theme(legend.position = "none",
plot.title = element_text(size = 24, face = "bold", margin = margin(0,0,10,0)),
plot.subtitle = element_text(size = 12, margin = margin(0,0,10,0)),
plot.caption = element_text(size = 12)) +
transition_states(states = week_order, transition_length = 2, state_length = 1, wrap = F) +
view_follow(fixed_y = TRUE) +
enter_fly(y_loc = -21) +
exit_fly(y_loc = -21) +
ease_aes('linear')
anim <- animate(rb_ani, nframes = 100, fps = 5,renderer = gifski_renderer(), height = 900, width = 1600)
I've tried changing the transition length/state length, removing the theme items, removing the colors, removing the stat = 'identity' argument, changing the group variable, and the number of frames/fps. I'm at a loss of what to try next. Any suggestions would be great!
Part of the challenge here is very choppy rankings week to week. To make the animation smooth, you'll need to either make the animation pretty long, or select a subset of weeks to calculate rankings on. Here I've limited to just week 30-39, and added more frames.
I also did some more data cleaning to give all the players a rank in each week even if they aren't included in stats that week.
animate(
stats %>%
# Some week_name missing from stats, will use week_order to get from all_weeks
select(-week_name) %>%
left_join(all_weeks %>% select(week_order, week_name), by = "week_order") %>%
# add every week for each player, and fill in any missing tot_fpts or team_colors
select(week_order, week_name, player_name, tot_fpts,
unique_id, team_color, team_color2) %>%
complete(week_order, player_name) %>%
fill(tot_fpts, .direction = "down") %>%
fill(unique_id, team_color, team_color2, .direction = "downup") %>%
# only keep players who had >0 max_tot_fpts and weeks 30-39
group_by(player_name) %>%
mutate(max_tot_fpts = max(tot_fpts)) %>%
filter(max_tot_fpts > 0, week_order >= 30, week_order < 40) %>%
# smooth out tot_fpts
mutate(tot_fpts_smooth = spline(x = week_order, y = tot_fpts, xout = week_order)$y) %>%
# Calc rank for every week, only keep top 20
group_by(week_order) %>%
arrange(-tot_fpts_smooth, player_name) %>%
mutate(rank = row_number()) %>%
ungroup() %>%
filter(rank <= 20) %>%
ggplot(aes(group = player_name, y = rank)) +
geom_tile(aes(x = tot_fpts/2, height = 0.9, width = tot_fpts,
color = unique_id, fill = unique_id)) +
geom_shadowtext(aes(x = tot_fpts, y = rank, label = player_name, color = unique_id),
bg.color = 'white', size = 3.5, na.rm = T, bg.r = 0.075,
show.legend = FALSE, hjust = 1.1) +
# geom_text(aes(x = tot_fpts, label = paste(player_name, " ")), vjust = 0.2, hjust = 1) +
scale_y_reverse(breaks = 1:20, minor_breaks = NULL) +
scale_color_manual(values = NFL_sec)+
scale_fill_manual(values = NFL_pri)+
labs(title = "Highest-scoring Fantasy Running Backs of the Past Decade",
subtitle = paste0("{all_weeks$week_name[as.numeric(previous_state)]}"),
caption = "Figure: #SamHoppen | Data: #nflfastR",
y = "",
x = "Total Fantasy Points")+
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(size = 14, face = "bold", margin = margin(0,0,10,0)),
plot.subtitle = element_text(size = 12, margin = margin(0,0,10,0)),
plot.caption = element_text(size = 12)) +
transition_states(week_order, state_length = 0) +
view_follow(fixed_y = TRUE) +
enter_fly(y_loc = -21) +
exit_fly(y_loc = -21) +
ease_aes('linear'),
fps = 20, duration = 4, width = 400, height = 300)

R label with commas but no decimals

My goal is to produce labels with commas, but no decimals. Let's say I have a ggplot with the following section:
geom_text(aes(y = var,
label = scales::comma(round(var))), hjust = 0, nudge_y = 300 )
This is almost what I need. It gives me the commas, but has a decimal. I have seen here (axis labels with comma but no decimals ggplot) that comma_format() could be good, but I think the label in my case needs a data argument, which comma_format() does not take. What can I do?
Update:
As an example of when this problem occurs, see the following, which uses gganimate and has a lot more going on. Code derived from Jon Spring's answer at Animated sorted bar chart with bars overtaking each other
library(gapminder)
library(gganimate)
library(tidyverse)
gap_smoother <- gapminder %>%
filter(continent == "Asia") %>%
group_by(country) %>%
complete(year = full_seq(year, 1)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
group_by(year) %>%
mutate(rank = min_rank(-gdpPercap) * 1) %>%
ungroup() %>%
group_by(country) %>%
complete(year = full_seq(year, .5)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
mutate(rank = approx(x = year, y = rank, xout = year)$y) %>%
ungroup() %>%
arrange(country,year)
gap_smoother2 <- gap_smoother %>% filter(year<=2007 & year>=1999)
gap_smoother3 <- gap_smoother2 %<>% filter(rank<=8)
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap))), hjust = 0, nudge_y = 300 ) +
coord_flip(clip = "off", expand = FALSE) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state %>% as.numeric %>% floor}',
x = "", y = "GFP per capita") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(year, transition_length = 1, state_length = 0) +
enter_grow() +
exit_shrink() +
ease_aes('linear')
animate(p, fps = 2, duration = 5, width = 600, height = 500)
In addition to the solution provided by #drf, you need to add scale_y_continuous(scales::comma) to your ggplot commands. But put it before the coord_flip function.
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap), accuracy=1)),
hjust = 0, nudge_y = 300 ) +
scale_y_continuous(labels = scales::comma) +
... etc.

Animated sorted bar chart with bars overtaking each other

Edit: keyword is 'bar chart race'
How would you go at reproducing this chart from Jaime Albella in R ?
See the animation on visualcapitalist.com or on twitter (giving several references in case one breaks).
I'm tagging this as ggplot2 and gganimate but anything that can be produced from R is relevant.
data (thanks to https://github.com/datasets/gdp )
gdp <- read.csv("https://raw.github.com/datasets/gdp/master/data/gdp.csv")
# remove irrelevant aggregated values
words <- scan(
text="world income only total dividend asia euro america africa oecd",
what= character())
pattern <- paste0("(",words,")",collapse="|")
gdp <- subset(gdp, !grepl(pattern, Country.Name , ignore.case = TRUE))
Edit:
Another cool example from John Murdoch :
Most populous cities from 1500 to 2018
Edit: added spline interpolation for smoother transitions, without making rank changes happen too fast. Code at bottom.
I've adapted an answer of mine to a related question. I like to use geom_tile for animated bars, since it allows you to slide positions.
I worked on this prior to your addition of data, but as it happens, the gapminder data I used is closely related.
library(tidyverse)
library(gganimate)
library(gapminder)
theme_set(theme_classic())
gap <- gapminder %>%
filter(continent == "Asia") %>%
group_by(year) %>%
# The * 1 makes it possible to have non-integer ranks while sliding
mutate(rank = min_rank(-gdpPercap) * 1) %>%
ungroup()
p <- ggplot(gap, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
# text in x-axis (requires clip = "off" in coord_*)
# paste(country, " ") is a hack to make pretty spacing, since hjust > 1
# leads to weird artifacts in text spacing.
geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
coord_flip(clip = "off", expand = FALSE) +
scale_y_continuous(labels = scales::comma) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state}', x = "", y = "GFP per capita") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(year, transition_length = 4, state_length = 1) +
ease_aes('cubic-in-out')
animate(p, fps = 25, duration = 20, width = 800, height = 600)
For the smoother version at the top, we can add a step to interpolate the data further before the plotting step. It can be useful to interpolate twice, once at rough granularity to determine the ranking, and another time for finer detail. If the ranking is calculated too finely, the bars will swap position too quickly.
gap_smoother <- gapminder %>%
filter(continent == "Asia") %>%
group_by(country) %>%
# Do somewhat rough interpolation for ranking
# (Otherwise the ranking shifts unpleasantly fast.)
complete(year = full_seq(year, 1)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
group_by(year) %>%
mutate(rank = min_rank(-gdpPercap) * 1) %>%
ungroup() %>%
# Then interpolate further to quarter years for fast number ticking.
# Interpolate the ranks calculated earlier.
group_by(country) %>%
complete(year = full_seq(year, .5)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
# "approx" below for linear interpolation. "spline" has a bouncy effect.
mutate(rank = approx(x = year, y = rank, xout = year)$y) %>%
ungroup() %>%
arrange(country,year)
Then the plot uses a few modified lines, otherwise the same:
p <- ggplot(gap_smoother, ...
# This line for the numbers that tick up
geom_text(aes(y = gdpPercap,
label = scales::comma(gdpPercap)), hjust = 0, nudge_y = 300 ) +
...
labs(title='{closest_state %>% as.numeric %>% floor}',
x = "", y = "GFP per capita") +
...
transition_states(year, transition_length = 1, state_length = 0) +
enter_grow() +
exit_shrink() +
ease_aes('linear')
animate(p, fps = 20, duration = 5, width = 400, height = 600, end_pause = 10)
This is what I came up with, so far, based in good part on #Jon's answer.
p <- gdp %>%
# build rank, labels and relative values
group_by(Year) %>%
mutate(Rank = rank(-Value),
Value_rel = Value/Value[Rank==1],
Value_lbl = paste0(" ",round(Value/1e9))) %>%
group_by(Country.Name) %>%
# keep top 10
filter(Rank <= 10) %>%
# plot
ggplot(aes(-Rank,Value_rel, fill = Country.Name)) +
geom_col(width = 0.8, position="identity") +
coord_flip() +
geom_text(aes(-Rank,y=0,label = Country.Name,hjust=0)) + #country label
geom_text(aes(-Rank,y=Value_rel,label = Value_lbl, hjust=0)) + # value label
theme_minimal() +
theme(legend.position = "none",axis.title = element_blank()) +
# animate along Year
transition_states(Year,4,1)
animate(p, 100, fps = 25, duration = 20, width = 800, height = 600)
I might come back to improve it.
The moving grid could be simulated by removing the actual grid and having geom_segment lines moving and fading out thanks to an alpha parameter changing when it approaches 100 billion.
To have labels changing values between years (which gives a nice feeling of urgency in the original chart) I think we have no choice but multiplying the rows while interpolating labels, we'll need to interpolate Rank too.
Then with a few minor cosmetic changes we should be pretty close.
This is what I came up, I just use Jon and Moody code as a template and make few changes.
library(tidyverse)
library(gganimate)
library(gapminder)
theme_set(theme_classic())
gdp <- read.csv("https://raw.github.com/datasets/gdp/master/data/gdp.csv")
words <- scan(
text="world income only total dividend asia euro america africa oecd",
what= character())
pattern <- paste0("(",words,")",collapse="|")
gdp <- subset(gdp, !grepl(pattern, Country.Name , ignore.case = TRUE))
colnames(gdp) <- gsub("Country.Name", "country", colnames(gdp))
colnames(gdp) <- gsub("Country.Code", "code", colnames(gdp))
colnames(gdp) <- gsub("Value", "value", colnames(gdp))
colnames(gdp) <- gsub("Year", "year", colnames(gdp))
gdp$value <- round(gdp$value/1e9)
gap <- gdp %>%
group_by(year) %>%
# The * 1 makes it possible to have non-integer ranks while sliding
mutate(rank = min_rank(-value) * 1,
Value_rel = value/value[rank==1],
Value_lbl = paste0(" ",value)) %>%
filter(rank <=10) %>%
ungroup()
p <- ggplot(gap, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = value/2,
height = value,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y=value,label = Value_lbl, hjust=0)) +
coord_flip(clip = "off", expand = FALSE) +
scale_y_continuous(labels = scales::comma) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state}', x = "", y = "GDP in billion USD",
caption = "Sources: World Bank | Plot generated by Nitish K. Mishra #nitishimtech") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(year, transition_length = 4, state_length = 1) +
ease_aes('cubic-in-out')
animate(p, 200, fps = 10, duration = 40, width = 800, height = 600, renderer = gifski_renderer("gganim.gif"))
Here I am using duration 40 second, which is slow. You can change duration and make it faster or slower as you needed.

R - How can I add a bivariate legend to my ggplot2 chart?

I'm trying to add a bivariate legend to my ggplot2 chart but I don't know whether (a) this is possible through some guides options and (b) how to achieve it.
The only way I've managed to produce something close to the desired outcome was by specifically creating a new chart which resembles a legend (named p.legend below) and inserting it, via the cowplot package, somewhere in the original chart (named p.chart below). But surely there must be a better way than this, given that this approach requires creating the legend in the first place and fiddling with its size/location to fit it in the original chart.
Here's code for a dummy example of my approach:
library(tidyverse)
# Create Dummy Data #
set.seed(876)
n <- 2
df <- expand.grid(Area = LETTERS[1:n],
Period = c("Summer", "Winter"),
stringsAsFactors = FALSE) %>%
mutate(Objective = runif(2 * n, min = 0, max = 2),
Performance = runif(2 * n) * Objective) %>%
gather(Type, Value, Objective:Performance)
# Original chart without legend #
p.chart <- df %>%
ggplot(., aes(x = Area)) +
geom_col(data = . %>% filter(Type == "Objective"),
aes(y = Value, fill = Period),
position = "dodge", width = 0.7, alpha = 0.6) +
geom_col(data = . %>% filter(Type == "Performance"),
aes(y = Value, fill = Period),
position = "dodge", width = 0.7) +
scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) +
theme_minimal() +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank())
# Create a chart resembling a legend #
p.legend <- expand.grid(Period = c("Summer", "Winter"),
Type = c("Objective", "Performance"),
stringsAsFactors = FALSE) %>%
ggplot(., aes(x = Period, y = factor(Type, levels = c("Performance", "Objective")),
fill = Period, alpha = Type)) +
geom_tile() +
scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) +
scale_alpha_manual(values = c("Objective" = 0.7, "Performance" = 1), guide = FALSE) +
ggtitle("Legend") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5),
rect = element_rect(fill = "transparent"),
axis.title = element_blank(),
panel.grid.major = element_blank())
# Add legend to original chart #
p.final <- cowplot::ggdraw() +
cowplot::draw_plot(plot = p.chart) +
cowplot::draw_plot(plot = p.legend, x = 0.5, y = 0.65, width = 0.4, height = 0.28, scale = 0.7)
# Save chart #
cowplot::ggsave("Bivariate Legend.png", p.final, width = 8, height = 6, dpi = 500)
... and the resulting chart:
Is there an easier way of doing this?
This might work at some point, but right now the colorbox seems to ignore all breaks, names and labels (#ClausWilke?). Probably because the multiscales package is in really early stages.
Posting since it might work when future readers are here.
library(multiscales)
df %>%
mutate(
period = as.numeric(factor(Period)),
type = as.numeric(factor(Type))
) %>%
ggplot(., aes(x = Area, y = Value, fill = zip(period, type), group = interaction(Area, Period))) +
geom_col(width = 0.7, position = 'dodge') +
bivariate_scale(
"fill",
pal_hue_sat(c(0.07, 0.6), c(0.4, 0.8)),
guide = guide_colorbox(
nbin = 2,
name = c("Period", "Type"), #ignored
breaks = list(1:2, 1:2), #ignored
labels = list(levels(.$Period), levels(.$Type)) #ignored
)

Resources