I'm creating a stacked bar chart using ggplot like this:
plot_df <- df[!is.na(df$levels), ]
ggplot(plot_df, aes(group)) + geom_bar(aes(fill = levels), position = "fill")
Which gives me something like this:
How do I reverse the order the stacked bars themselves, so that level 1 is at the bottom, and level 5 is at the top of each bar?
I've seen a number of questions on this (e.g. How to control ordering of stacked bar chart using identity on ggplot2) and the common solution seems to be to reorder the dataframe by that level as that what ggplot is using the determine the order
So I've tried reordering using dplyr:
plot_df <- df[!is.na(df$levels), ] %>% arrange(desc(levels))
However, the plot comes out the same. It also doesn't seem to make a difference whether I arrange by ascending or descending order
Here is a reproducible example:
group <- c(1,2,3,4, 1,2,3,4, 1,2,3,4, 1,2,3,4, 1,2,3,4, 1,2,3,4)
levels <- c("1","1","1","1","2","2","2","2","3","3","3","3","4","4","4","4","5","5","5","5","1","1","1","1")
plot_df <- data.frame(group, levels)
ggplot(plot_df, aes(group)) + geom_bar(aes(fill = levels), position = "fill")
The release notes of ggplot2 version 2.2.0 on Stacking bars suggest:
If you want to stack in the opposite order, try forcats::fct_rev()
library(ggplot2) # version 2.2.1 used
plot_df <- data.frame(group = rep(1:4, 6),
levels = factor(c(rep(1:5, each = 4), rep(1, 4))))
ggplot(plot_df, aes(group, fill = forcats::fct_rev(levels))) +
geom_bar(position = "fill")
This is the original plot:
ggplot(plot_df, aes(group, fill = levels)) +
geom_bar(position = "fill")
Or, using position_fill(reverse = TRUE) as suggested by alistaire in his comment:
ggplot(plot_df, aes(group, fill = levels)) +
geom_bar(position = position_fill(reverse = TRUE))
Note that the levels (colors) in the legend is not in the same order as in the stacked bars.
An alternative is to reorder the factor as such, assuming the factor is called "levels":
levels = ordered(levels, levels=c(5,4,3,2,1)).
for more info: http://www.cookbook-r.com/Manipulating_data/Changing_the_order_of_levels_of_a_factor/
Related
I'm trying to plot my data as a stacked bar with 3 levels ("catg"), but I want the categories on the X-axis to appeared in increasing order by their value of the "low" sub-categories,
here is reproductive example:
#creating df:
set.seed(33)
df<-data.frame(value=runif(12),
catg=factor(rep(c("high","medium","low")),
levels = c("high","medium","low")),
var_name=(c(rep("question1",3),rep("question2",3),rep("question3",3),rep("question4",3)))
#plotting
bar_dist<-ggplot(df,aes(x=var_name,
y=value,
fill=catg,
label=round(value,2)))
bar_dist+ geom_bar(stat = "identity",
position = "dodge",
width = 0.7)+
coord_flip() +
xlab("questions")+
ylab("y")+
geom_text(size = 4,position=position_dodge(width = 0.7))
And here is my current plot:
so in this case I should have question3 and then 4, 1, and finally 2.
every help will be appreciate,
One way could be :
df$var_name=factor(df$var_name,levels=rev(levels(reorder(df[df$catg=="low",]$var_name,df[df$catg=="low",]$value))))
It uses reorder() as suggested by Richard Telford to reorder the levels according to df$value after filtering df to keep only the "low".
levels() is used to extract the levels from the previous function.
rev() is used to reverse the levels.
factor() reassigns the levels to df$var_name
or :
df$var_name=factor(df$var_name,levels = df[with(df,order(value,decreasing = T)) ,][df[with(df,order(value,decreasing = T)) ,]$catg=="low",]$var_name)
It sorts df on df$value (by decreasing value), filters on df$catg for "low" and retrieves df$var_name which is used as levels in factor().
The same plotting function is then used:
A solution that doesn't modify the data frame, using fct_reorder2() from the forcats library:
library(forcats)
bar_dist <- ggplot(df,
aes(
x = fct_reorder2(var_name, catg, value),
y = value, fill = catg,
label = round(value, 2)))
bar_dist + geom_bar(stat = "identity",
position = "dodge",
width = 0.7) +
coord_flip() +
xlab("questions") +
ylab("y") +
geom_text(size = 4, position = position_dodge(width = 0.7))
I have a faceted ggplot that is all but done. I cannot seem to get the fill aesthetic to be descending for each group in the dodged plot and across facets. The idea is to look at the plot and quickly recognise the top three categories within each group on the y-axis - and that the colors will be order different for each group. Here is some code to get a representative graph.
library(tidyverse)
set.seed(123)
#using crossing from purrr
df <- crossing(
mean = 1:8,
cats = sample(letters[1:3], 8, T),
gender = c('Male', 'Female')) %>%
mutate(vary_x = sample(seq(1,3,.1),nrow(.), T))
df %>%
ggplot(aes(mean, vary_x, fill = cats))+
geom_bar(stat = 'identity',
position = 'dodge') +
facet_grid(.~gender) +
coord_flip()
Something like this maybe:
df %>%
ggplot(aes(mean, reorder(vary_x,mean), fill = cats))+
geom_bar(stat = 'identity',
position = 'dodge') +
facet_grid(.~gender) +
coord_flip()
This question already has answers here:
Order categorical data in a stacked bar plot with ggplot2
(3 answers)
Closed 5 years ago.
Im following the example given in this post for creating a grouped and stacked bar-chart: How to produce stacked bars within grouped barchart in R
From that post:
library(reshape2)
library(dplyr)
library(ggplot2)
test <- data.frame(person=c("A", "B", "C", "D", "E"),
value1=c(100,150,120,80,150),
value2=c(25,30,45,30,30) ,
value3=c(100,120,150,150,200))
melted <- melt(test, "person")
melted$cat <- ''
melted[melted$variable == 'value1',]$cat <- "first"
melted[melted$variable != 'value1',]$cat <- "second"
ggplot(melted, aes(x = cat, y = value, fill = variable)) +
geom_bar(stat = 'identity', position = 'stack') +
facet_grid(~ person)
As it is above, the plot orders value2 on top of value3.
What I am trying to do is change the order of the stacked portion,ie; I'd like to place value3 on top of value2.
Ive tried manually changing the order of the variable:
melted2 <- melted %>%
arrange(desc(variable))
ggplot(melted2, aes(x = cat, y = value, fill = variable)) +
geom_bar(stat = 'identity', position = 'stack') +
facet_grid(~ person)
But the plot output looks identical to the first one. Essentially, the reordering of the input data does not accomplish the task.
Thank you in advance!
This should work though it isn't clear to me exactly what order you'd like these in. But you can use levels to accomplish this:
melted$variable <- factor(melted$variable, levels = c("value1","value3","value2"))
ggplot(melted, aes(x = cat, y = value, fill = variable)) +
geom_bar(stat = 'identity', position = 'stack') +
facet_grid(~ person)
This approach that you use before only arranges the values in order of their value.
melted2 <- melted %>%
arrange(desc(variable))
Works better for continuous vectors. You actually needs to change the levels of the factor. You can check the levels using this:
levels(melted$variable)
[1] "value1" "value3" "value2"
melted$variable was already a factor but you just needed to override the default levels to what you wanted.
I want to create grouped bar plot while keeping order. If it was single column and not a grouped bar plot use of reorder function is obvious. But not sure how to use it on a melted data.frame.
Here is the detail explanation with code example:
Lets say we have following data.frame:
d.nfl <- data.frame(Team1=c("Vikings", "Chicago", "GreenBay", "Detroit"), Win=c(20, 13, 9, 12))
plotting a simple bar plot while flipping it.
ggplot(d.nfl, aes(x = Team1, y=Win)) + geom_bar(aes(fill=Team1), stat="identity") + coord_flip()
above plot will not have an order and if I want to order the plot by win I can do following:
d.nfl$orderedTeam <- reorder(d.nfl$Team1, d.nfl$Win)
ggplot(d.nfl, aes(x = orderedTeam, y=Win)) + geom_bar(aes(fill=orderedTeam), stat="identity") + coord_flip()
Now lets say we add another column (to original data frame)
d.nfl$points <- c(12, 3, 45, 5)
Team1 Win points
1 Vikings 20 12
2 Chicago 13 3
3 GreenBay 9 45
4 Detroit 12 5
to generate grouped bar plot, first we need to melt it:
library(reshape2)
> d.nfl.melt <- melt(d.nfl[,c('Team1','Win','points')],id.vars = 1)
> ggplot(d.nfl.melt,aes(x = Team1,y = value)) + geom_bar(aes(fill = variable),position = "dodge", stat="identity") + coord_flip()
above ggplot is unordered.
but how I do ordered group bar plot (ascending manner)
This is a non-issue.
The easiest way is to not discard your ordered team in the melt:
d.nfl.melt <- melt(d.nfl,id.vars = c("Team1", "orderedTeam"))
Alternatively, we can use reorder after melting and just only use the Win elements in computing the ordering:
d.nfl.melt$ordered_after_melting = reorder(
d.nfl.melt$Team1,
X = d.nfl.melt$value * (d.nfl.melt$variable == "Win")
)
Yet another idea is to take the levels from the original ordered column and apply them to a melted factor:
d.nfl.melt$copied_levels = factor(
d.nfl.melt$Team1,
levels = levels(d.nfl$orderedTeam)
)
All three methods give the same result. (I left out the coord_flips because they don't add anything to the question, but you can of course add them back in.)
gridExtra::grid.arrange(
ggplot(d.nfl.melt,aes(x = orderedTeam, y = value)) +
geom_bar(aes(fill = variable),position = "dodge", stat="identity"),
ggplot(d.nfl.melt,aes(x = ordered_after_melting, y = value)) +
geom_bar(aes(fill = variable),position = "dodge", stat="identity"),
ggplot(d.nfl.melt,aes(x = copied_levels, y = value)) +
geom_bar(aes(fill = variable),position = "dodge", stat="identity")
)
As to the easiest, I would recommend just keeping the orderedTeam variable around while melting. Your code seems to work hard to leave it out, it's quite easy to keep it in.
The challenge your question presents is how to reorder a factor Team1 based on a subset values in a melted column.
The comments to your question from #alistaire and #joran link to great answers.
The tl;dr answer is to just apply the ordering from your original, unmelted data.frame to the new one using levels().
library(reshape2)
#Picking up from your example code:
d.nfl.melt <- melt(d.nfl[,c('Team1','Win','points')],id.vars = 1)
levels(d.nfl.melt$Team1)
#Current order is alphabetical
#[1] "Chicago" "Detroit" "GreenBay" "Vikings"
#Reorder based on Wins (using the same order from your earlier, unmelted data.frame)
d.nfl.melt$Team1 <- factor(d.nfl.melt$Team1, levels = levels(d.nfl$orderedTeam)) #SOLUTION
levels(d.nfl.melt$Team1)
#New order is ascending by wins
#[1] "GreenBay" "Detroit" "Chicago" "Vikings"
ggplot(d.nfl.melt,aes(x = Team1,y = value)) +
geom_bar(aes(fill = variable),position = "dodge", stat="identity") + coord_flip()
So, I am doing several descending ordered barplots in R using ggplot. Each of these plots contains one bar named "others", which should always the last bar. How to realize this optimally? More generally: Is there an easy possibility to pick one bar of a bar plot and move it to the last position without manually changing all levels.
Many thanks in advance,
chris
The trick is to use factor, as follows
library(ggplot2) # for plots
# dummy data
dat <- data.frame(
letters = c("A","B","Other","X","Y","Z"),
probs = sample(runif(6)*10,6)
)
# not what we want
ggplot(dat, aes(letters, probs)) + geom_bar(stat = "identity")
# magic happens here
# factor, and push Other to end of levels using c(others, other)
dat$letters <- factor(
dat$letters,
levels = c(
levels(dat$letters)[!levels(dat$letters) == "Other"],
"Other")
)
ggplot(dat, aes(letters, probs)) + geom_bar(stat = "identity")
If you're using + coord_flip(), use levels = rev(c(...)) for intuitive ordering
dat$letters <- factor(
dat$letters,
levels = rev(c(
levels(dat$letters)[!levels(dat$letters) == "Other"],
"Other"))
)
ggplot(dat, aes(letters, probs)) + geom_bar(stat = "identity") + coord_flip()