Rename ordered x-axis labels in faceted ggplot - r

I'm trying to rename faceted, ordered, x-axis tick marks in ggplot().
library(ggplot2)
library(dplyr)
set.seed(256)
myFun <- function(n = 5000) {
a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE))
paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE))
}
n <- 15
dat <- data.frame(category = sample(letters[1:2], n, replace = T),
name = myFun(n),
perc = sample(seq(0, 1, by = 0.01), n, replace = TRUE))
to_plot <-
dat %>%
group_by(category) %>%
arrange(category, desc(perc)) %>%
top_n(5, perc)
Plotting this gets me
to_plot %>%
ggplot(aes(x = name, y = perc)) +
geom_bar(stat = "identity") +
facet_wrap(~category, scales = "free_y") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
which is unordered and not what I want at all, so I do some ordering by adding a "dummy" column of row_number()
to_plot %>%
mutate(row_number = row_number()) %>%
ungroup() %>%
mutate(row_number = row_number %>% as.factor()) %>%
ggplot(aes(x = row_number, y = perc)) +
geom_bar(stat = "identity") +
facet_wrap(~category, scales = "free_y") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
This gets me close, but I still need to change the names on the x-axis so I add:
scale_x_discrete(name = "name", labels = str_wrap(to_plot %>% pull(name), 3))
but this only repeats the first facet group across both facets, even though the data in each plot is correct
I've also tried just ordering everything sequentially and allowing both axes to be free in the facet_wrap() fx, but that doesn't seem to work either:
new_plot <-
dat %>%
group_by(category) %>%
arrange(category, desc(perc)) %>%
ungroup() %>%
mutate(row_number = row_number() %>% as.factor())
new_plot %>%
ggplot(aes(x = row_number, y = perc)) +
geom_bar(stat = "identity") +
scale_x_discrete(name = "name", labels = new_plot %>% pull(name)) +
facet_wrap(~category, scales = "free") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
How can I label the x-axis tick-marks in multiple facet_wrap() plots independently of one another? I feel like I'm missing something pretty basic here, but I can't figure out what it is.

to_plot %>%
ggplot(aes(x = name %>% forcats::fct_reorder(-perc), y = perc)) +
geom_bar(stat = "identity") +
facet_wrap(~category, scales = "free") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Related

Placing data labels for stacked bar chart at top of bar

I have been attempting to add a label on top of each bar to represent the proportion that each ethnic group makes up in referrals.
For some reason I cannot get the labels to be placed at the top of each bar. How do I fix this?
My code below
freq <- df %>%
group_by(ethnicity) %>%
summarise(n = n()) %>%
mutate(f = round((n/sum(n)*100, 1))
df %>%
group_by(pathway) %>%
count(ethnicity) %>%
ggplot(aes(x = ethnicity, y = n , fill = pathway)) +
geom_bar(stat = "identity", position = "stack") +
geom_text(data = freq,
aes(x= ethnicity, y = f, label = f),
inherit.aes = FALSE) +
theme(legend.position = "bottom") +
scale_fill_manual(name = "",
values = c("light blue", "deepskyblue4"),
labels = "a", "b") +
xlab("") +
ylab("Number of Referrals") +
scale_y_continuous(breaks = seq(0, 2250, 250), expand = c(0,0)
Here is what it currently looks like
Since you are using the count as your y-axis position in geom_bar, you need to use the same thing in your geom_text to get the labels in the right place. Below is an example using mtcars dataset. Using vjust = -1 I put a little bit of space between the label and the bars to make it more legible and aesthetically pleasing.
library(tidyverse)
mtcars %>%
group_by(carb) %>%
summarise(n = n()) %>%
mutate(f = round(proportions(n) * 100, 1)) -> frq
mtcars %>%
group_by(gear) %>%
count(carb) -> df
df %>%
ggplot(aes(x = carb, y = n, fill = gear)) +
geom_bar(stat = "identity", position = "stack") +
geom_text(data = frq,
vjust = -1,
aes(x= carb, y = n, label = f),
inherit.aes = FALSE)
Created on 2022-10-31 by the reprex package (v2.0.1)

Order the stacked barplot by proportion or percent in R

I can order my plot based on count but not by proportion. I want to bars on x-axis to be arranged by proportion of "c". Here is my code
long<- data.frame(
Name = c("abc","abc","abc","gif","gif","gif","xyz","xyz","xyz"),
variable = c("a","b","c","a","b","c","c","b","a"),
value = c(4,6,NA,2,8,1,6,NA,NA))
long_totals <- long %>%
group_by(Name) %>%
summarise(Total = sum(value, na.rm = T))
p<-long %>%
mutate(variable = fct_relevel(variable, c("c", "b", "a"))) %>%
arrange(variable) %>%
mutate(Name = fct_inorder(Name))
p %>%
ggplot() +
aes(x = Name,
y = value,
fill = variable) +
geom_bar(position = "fill",
stat = "summary") +
geom_text(data = long_totals,
aes(y = 100,
x = Name,
label = Total),
size = 7,
position = position_fill(vjust = 1.02)) +
scale_y_continuous(labels = scales::percent_format())
Also, I am plotting total numbers using geom_text
Add the proportion c by group like this, when generating p:
... %>%
group_by(Name) %>%
mutate(prop_c = sum(value[variable=="c"], na.rm=T)/sum(value, na.rm=T))
Then plot, using reorder:
ggplot() +
geom_col(data= p,aes(x = reorder(Name,prop_c, decreasing=T),y = value,fill = variable),position = "fill") +
geom_text(data = long_totals, aes(y = 100,x = Name,label = Total),size = 7,position = position_fill(vjust = 1.02)) +
scale_y_continuous(labels = scales::percent_format())

Re-positioning axis labels in face_wrapped plots with free y axis in ggplot2

I have a face_wrapped plot with free y axis in ggplot2. I was wondering if it is possible to reposition some of the flipped x-axis label based on the red arrows shown in the picture below?
library(tidyverse)
data <- read_csv('https://raw.githubusercontent.com/rnorouzian/e/master/surv.csv')
names(data)[2:5] <- c("Representation", "Solidification", "Application", "Confidence")
data %>%
pivot_longer(cols = -id) %>%
mutate(name = name,
value = str_wrap(value, 20)) %>%
ggplot() +
geom_bar(aes(value, fill = name), show.legend = FALSE) +
facet_wrap(.~name, scales = 'free_y') +
coord_flip() +
labs(y = "Students", x = "") +
theme(axis.text.y = element_text(size=8))
We can reorder after creating the frequency with count
library(dplyr)
library(tidyr)
library(ggplot2)
data %>%
pivot_longer(cols = -id) %>%
mutate(name = name,
value = str_wrap(value, 20)) %>%
count(name, value) %>%
ggplot(aes(x = reorder(value, n), y = n, fill = name)) +
geom_bar(show.legend = FALSE, stat = 'identity') +
facet_wrap(.~name, scales = 'free_y') +
coord_flip() +
labs(y = "Students", x = "") +
theme(axis.text.y = element_text(size=8))
-output
Or if it is a custom order, then have to create the custom order vector and use that to change the 'value' to a factor with levels specified in that order
data1 <- data %>%
pivot_longer(cols = -id) %>%
mutate(name = name,
value = str_wrap(value, 20))
lvls <- unique(data1$value)[c(3, 10, 1, 5, 9, 4, 8, 7, 6, 2)]
data1 %>%
mutate(value = factor(value, levels = lvls)) %>% # // change here
ggplot() +
geom_bar(aes(value, fill = name), show.legend = FALSE) +
facet_wrap(.~name, scales = 'free_y') +
coord_flip() +
labs(y = "Students", x = "") +
theme(axis.text.y = element_text(size=8))
This code should get the exact picture you want, by reordering the factor after you have pivoted longer
data %>%
pivot_longer(cols = -id) %>%
mutate(name = name,
value = factor(str_wrap(value, 20))) %>%
mutate(value = forcats::fct_relevel(value, "Agree", after = 1)) %>%
mutate(value = forcats::fct_relevel(value, "The assignment\nhelped me solidify\nthe key concepts", after = Inf)) %>%
mutate(value = forcats::fct_relevel(value, "The assignment\nreflected the class\ninstructions", after = Inf)) %>%
mutate(value = forcats::fct_relevel(value, "The assignment\nhighly reflected the\nclass instructions", after = Inf)) %>%
mutate(value = forcats::fct_relevel(value, "The assignment\ngave me a great\nopportunity to apply\nwhat I learned", after = Inf)) %>%
ggplot() +
geom_bar(aes(value, fill = name), show.legend = FALSE) +
facet_wrap(.~name, scales = 'free_y') +
coord_flip() +
labs(y = "Students", x = "") +
theme(axis.text.y = element_text(size=8))

How to sort the double bar using ggplot in r?

I am learning r and I have problem with sorting the double bar in ascending or descending order and I want to set the legend just on the top of the plot with two color represent respectively with one row and two columns like for example:
The title Time
box color Breakfast box color Dinner
And the plot here
Here is my dataframe:
dat <- data.frame(
time = factor(c("Breakfast","Breakfast","Breakfast","Breakfast","Breakfast","Lunch","Lunch","Lunch","Lunch","Lunch","Lunch","Dinner","Dinner","Dinner","Dinner","Dinner","Dinner","Dinner"), levels=c("Breakfast","Lunch","Dinner")),
class = c("a","a","b","b","c","a","b","b","c","c","c","a","a","b","b","b","c","c"))
And here is my code to make change:
dat %>%
filter(time %in% c("Breakfast", "Dinner")) %>%
droplevels %>%
count(time, class) %>%
group_by(time) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(x = class, y = prop, fill = time, label = scales::percent(prop))) +
geom_col(position = 'dodge') +
geom_text(position = position_dodge(width = 0.9), vjust = 0.5, size = 3) +
scale_y_continuous(labels = scales::percent)+
coord_flip()
Any help would be appreciated.
Something like this should be close to what you are asking, feel free to ask more
Resources consulted during the answer: http://www.sthda.com/english/wiki/ggplot2-legend-easy-steps-to-change-the-position-and-the-appearance-of-a-graph-legend-in-r-software
Using part of the answer you can look further into https://ggplot2.tidyverse.org/reference/theme.html
library(tidyverse)
dat <- data.frame(
time = factor(c("Breakfast","Breakfast","Breakfast","Breakfast","Breakfast","Lunch","Lunch","Lunch","Lunch","Lunch","Lunch","Dinner","Dinner","Dinner","Dinner","Dinner","Dinner","Dinner"), levels=c("Breakfast","Lunch","Dinner")),
class = c("a","a","b","b","c","a","b","b","c","c","c","a","a","b","b","b","c","c"))
dat %>%
filter(time %in% c("Breakfast", "Dinner")) %>%
droplevels %>%
count(time, class) %>%
group_by(time) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(x = fct_reorder(class,prop), y = prop, fill = time, label = scales::percent(prop))) +
geom_col(position = 'dodge') +
geom_text(position = position_dodge(width = 0.9), vjust = 0.5, size = 3) +
scale_y_continuous(labels = scales::percent)+
coord_flip() +
labs(x = "class",fill = "Time") +
theme(legend.position = "top", legend.direction="vertical", legend.title=element_text(hjust = 0.5,face = "bold",size = 12))
Created on 2020-05-08 by the reprex package (v0.3.0)
To get the legend title above the legend key, requires a little additional adjustments to the theme and guides.
dat %>%
filter(time %in% c("Breakfast", "Dinner")) %>%
droplevels %>%
count(time, class) %>%
group_by(time) %>%
mutate(prop = n/sum(n)) %>%
ggplot(aes(x = class, y = prop, fill = time, label = scales::percent(prop))) +
geom_col(position = 'dodge') +
geom_text(position = position_dodge(width = 0.9), vjust = 0.5, size = 3) +
scale_y_continuous(labels = scales::percent)+
coord_flip() +
theme(legend.position="top", legend.direction="vertical", legend.title=element_text(hjust = 0.5))+
guides(fill = guide_legend(title = "Time", nrow = 1))

R, ggplot2, limit rows in a faceted barplot

Sup,
Consider the following lines:
data
df=data.frame(
prod=sample(1:30, 1000, replace=TRUE),
mat=sample(c('yes', 'no'), 1000, replace=TRUE),
fj=sample(c(1,2), 1000, replace = TRUE)
)
plot
df %>%
group_by(mat, prod, fj) %>%
summarise(n = n()) %>%
arrange(desc(n)) %>%
slice(1:5) %>%
ggplot(aes(x = reorder(prod, n), y = n)) +
geom_col(fill = RColorBrewer::brewer.pal(3, 'Dark2')[2], colour = "grey", alpha = 0.8) +
labs(x = "Prod", y = "Qnt") +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
facet_wrap(fj ~ mat, scale="free") +
theme_minimal()
which gives me
Now, if i drop fj variable, as in
df %>%
group_by(mat, prod) %>%
summarise(n = n()) %>%
arrange(desc(n)) %>%
slice(1:5) %>%
ggplot(aes(x = reorder(prod, n), y = n)) +
geom_col(fill = RColorBrewer::brewer.pal(3, 'Dark2')[2], colour = "grey", alpha = 0.8) +
labs(x = "Prod", y = "Qnt") +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
facet_wrap(~ mat, scale="free") +
theme_minimal()
slice(1:5) does it's job and i've got:
Question
why slice and reorder doesn't seems to work properly when there's 3+ variables and what should i do to limit the first plot to 5 lines each?
When you call summarize you loose one level of grouping. In this case, you lost fj, so when you slice it's not included in the group divisions.
If you first ungroup then group_by mat and fj, I think you'll end up with what you are looking for.
df %>%
group_by(mat, prod, fj) %>%
summarise(n = n()) %>%
ungroup()%>%
group_by(mat, fj) %>%
arrange(desc(n)) %>%
slice(1:5) %>%
ggplot(aes(x = reorder(prod, n), y = n)) +
geom_col(fill = RColorBrewer::brewer.pal(3, 'Dark2')[2], colour = "grey", alpha = 0.8) +
labs(x = "Prod", y = "Qnt") +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
facet_wrap(fj ~ mat, scale="free") +
theme_minimal()
This leaves the problem of reordering the prod variable within each facet. It doesn't work in the example above because you are ordering by the entire data frame, and some of the values of Prod are repeated in several of the facets. As discussed in this blog post by #drsimonj you need to create an order variable and plot based on that. This follows/blatently copies the method outlined in the blog post.
df %>%
group_by(mat, prod, fj) %>%
summarise(n = n()) %>%
group_by(mat, fj) %>%
arrange(desc(n)) %>%
slice(1:5) %>%
ungroup() %>%
arrange(fj,mat, n) %>% # arrange the entire table by the facets first, then by the n value
mutate(row.order = row_number()) %>% # create dummy variable
ggplot(aes(x = row.order, y = n)) + # plot by the dummy variable
geom_col(fill = RColorBrewer::brewer.pal(3, 'Dark2')[2], colour = "grey", alpha = 0.8, position = "dodge") +
labs(x = "Prod", y = "Qnt") +
scale_y_continuous(labels = scales::comma) +
scale_x_continuous( # add back in the Prod values
breaks = df2$row.order,
labels = df2$prod
)+
coord_flip() +
facet_wrap(fj ~ mat, scales = "free") +
theme_minimal()

Resources