We could write a function to apply on the geom
call to sort this type of issue as it's quite common and the way out is verbose and awkward. I took a shot at it below, it should work with all geom_*()
, stat_*()
, and ggplot()
calls.
One cool thing about it is that for once the different operator precedence of +
and %>%
works for us and not against.
setup
df <- data.frame("Swelling" = c("Swelling 1","Swelling 2", "Swelling 3","Swelling 3", "Swelling 2","Swelling 1","Swelling 3","Swelling 1","Swelling 1", "Swelling 3","Swelling 1", "Swelling 2", "Swelling 1","Swelling 2", "Swelling 3","Swelling 3", "Swelling 2","Swelling 1","Swelling 3","Swelling 1","Swelling 1", "Swelling 3","Swelling 1", "Swelling 2","Swelling 3","Swelling 1", "Swelling 2" ),
"Genotype" = c("Genotype 1","Genotype 2", "Genotype 3", "Genotype 1","Genotype 2", "Genotype 3","Genotype 1","Genotype 2", "Genotype 3","Genotype 1","Genotype 2", "Genotype 3", "Genotype 1","Genotype 2", "Genotype 3","Genotype 1","Genotype 2", "Genotype 3","Genotype 1","Genotype 2", "Genotype 3", "Genotype 1","Genotype 2", "Genotype 3","Genotype 1","Genotype 2", "Genotype 3"),
"Freq" = c("1","1", "1","1", "1","1","1","1","1", "1","1", "1","1","1", "1","1", "1","1","1","1","1", "1","1", "1","1","1", "1"))
library(plyr)
df2<-plyr::ddply(df,.(Swelling), function(x) with(x, data.frame(100*round(table(Genotype)/length(Genotype),2))))
function
arrange_gg <- function(gg, var, by, focus = TRUE){
fun <-
eval(eval(substitute(quote(function(x) {
lvls <- x %>%
dplyr::filter(focus) %>%
dplyr::arrange(by) %>%
dplyr::pull(var) %>%
as.character() %>%
unique()
x <- dplyr::mutate_at(x, dplyr::vars(var), ~factor(., union(lvls, levels(.))))
})))[-4])
data <- gg$data
if(is.function(data)){
gg$data <- purrr::compose(fun,data)
} else if(ggplot2:::is.waive(data)){
gg$data <- fun
} else if(is.data.frame(data)) {
gg$data <- fun(data)
} else {
stop("unexpected class")
}
gg
}
Former request
library(ggplot2)
ggplot(df2, aes(x = Genotype, y = Freq, fill = Swelling)) +
geom_bar(position = "fill",stat = "identity") %>%
arrange_gg(Genotype, desc(Freq), Swelling=="Swelling 1") %>%
arrange_gg(Swelling, desc(Freq)) +
scale_y_continuous(labels = scales::percent)
Additional request
Your additional request to get Swelling 1
at the bottom can be done in different ways yielding the same result here but not in the general case.
We can sort by the condition Swelling !="Swelling 1"
. FALSE
is put before TRUE
when sorting, and first levels are on top. so it will not reorder all values, just put Swelling 1
at the bottom.
ggplot(df2, aes(x = Genotype, y = Freq, fill = Swelling)) +
geom_bar(position = "fill",stat = "identity") %>%
arrange_gg(Genotype, desc(Freq), Swelling =="Swelling 1") %>%
arrange_gg(Swelling, desc(Freq), Swelling !="Swelling 1") +
scale_y_continuous(labels = scales::percent)
This will revert the existing order as defined alphabetically IF by
is a character col, and the factor order otherwise. No need for a focus
argument here.
ggplot(df2, aes(x = Genotype, y = Freq, fill = Swelling)) +
geom_bar(position = "fill",stat = "identity") %>%
arrange_gg(Genotype, desc(Freq), Swelling=="Swelling 1") %>%
arrange_gg(Swelling, desc(Swelling)) +
scale_y_continuous(labels = scales::percent)
To be sure to reverse the alphabetical order, add as.character
ggplot(df2, aes(x = Genotype, y = Freq, fill = Swelling)) +
geom_bar(position = "fill",stat = "identity") %>%
arrange_gg(Genotype, desc(Freq), Swelling=="Swelling 1") %>%
arrange_gg(Swelling, desc(as.character(Swelling))) +
scale_y_continuous(labels = scales::percent)
Apply on ggplot call
You can also apply it on the ggplot object to make these changes permanent
through the chain.
ggplot(df2, aes(x = Genotype, y = Freq, fill = Swelling)) %>%
arrange_gg(Genotype, desc(Freq), Swelling =="Swelling 1") %>%
arrange_gg(Swelling, desc(Freq), Swelling !="Swelling 1") +
geom_bar(position = "fill",stat = "identity") +
scale_y_continuous(labels = scales::percent)