Ordering factors in each facet of ggplot by y-axis value
I've found dplyr doesn't work super well with group_by()
when dealing with different factor levels in each of the groups. So one work around is thinking of creating a new factor that's unique for each animal-letter combination and ordering that. First, we create an interaction variable with animal+letter and determine the proper order for each of the letters for the animals
new_order <- my_df %>%
group_by(animals) %>%
do(data_frame(al=levels(reorder(interaction(.$animals, .$letters, drop=TRUE), .$numbers)))) %>%
pull(al)
Now we create the interaction variable in the data we want to plot, use this new ordering, and finally change the labels so they look like just the letters again
my_df %>%
mutate(al=factor(interaction(animals, letters), levels=new_order)) %>%
ggplot(aes(x = al, y = numbers)) +
geom_point() + facet_wrap(~animals, ncol = 1, scales = 'free_x') +
scale_x_discrete(breaks= new_order, labels=gsub("^.*\\.", "", new_order))
ggplot facet different Y axis order based on value
The functions reorder_within
and scale_*_reordered
from the tidytext package might come in handy.
reorder_within
recodes the values into a factor with strings in the form of "VARIABLE___WITHIN". This factor is ordered by the values in each group of WITHIN.scale_*_reordered
removes the "___WITHIN" suffix when plotting the axis labels.
Add scales = "free_y"
in facet_wrap
to make it work as expected.
Here is an example with generated data:
library(tidyverse)
# Generate data
df <- expand.grid(
year = 2019:2021,
group = paste("Group", toupper(letters[1:8]))
)
set.seed(123)
df$value <- rnorm(nrow(df), mean = 10, sd = 2)
df %>%
mutate(group = tidytext::reorder_within(group, value, within = year)) %>%
ggplot(aes(value, group)) +
geom_point() +
tidytext::scale_y_reordered() +
facet_wrap(vars(year), scales = "free_y")
Change order of factors within ggplot facets
How about a revolting hack?
This combines your subsets into a new dataframe
. It then adds a column concatenating the set number with the variable in order to make each variable unique. The problem is, this affects your x axis names, as they are the concatenated names. To get around this I followed this hack R: Reorder facet_wrapped x-axis with free_x in ggplot2 to remove the axis names and use geom_text
instead.
mtcars_melt <- melt(mtcars)
# Subset data
set1 <- subset(mtcars_melt, variable == "mpg" | variable == "wt" | variable == "qsec")
set2 <- subset(mtcars_melt, variable == "gear" | variable == "cyl" | variable == "mpg")
set3 <- subset(mtcars_melt, variable == "drat" | variable == "vs" | variable == "mpg")
# Order factors
set1$variable <- factor(set1$variable, levels = c("mpg", "wt", "qsec"))
set2$variable <- factor(set2$variable, levels = c("mpg", "gear", "cyl"))
set3$variable <- factor(set3$variable, levels = c("vs", "mpg", "drat"))
names(set1)[2]<-"set1"
names(set2)[2]<-"set2"
names(set3)[2]<-"set3"
mset1<-melt(set1)
mset2<-melt(set2)
mset3<-melt(set3)
library(data.table)
new<-as.data.frame(rbindlist(list(mset1,mset2,mset3)))
names(new)[2]<-'setno'
new$lvl <- with(new,paste(variable,setno,sep="."))
new$lvl<-as.factor(new$lvl)
new$lvl<-factor(new$lvl,levels=c("mpg.set1","wt.set1","qsec.set1","mpg.set2","gear.set2","cyl.set2", "vs.set3" , "mpg.set3","drat.set3" ))
plot1 <- ggplot(new, aes(x = lvl, y = value)) + ylim(0, 35) + geom_boxplot()+
facet_wrap(~setno,scales="free_x")+
ylim(-2,NA)+
geom_text(aes(y=-1,label=variable),angle=45,size=5,hjust=1)+
theme(axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.line.x = element_blank(),
axis.ticks.x = element_blank())+
geom_hline(aes(yintercept=0))
ggplot: facets with their own order of factors on x axis
Edit after comment
You should slightly modify the reorder_within
function, by setting max
instead of mean
like this:
dat<-data.frame(Pal=rep(c("A","B","C","D"),times=c(20,40,60,30)),
Rol=c(rep("aa",15),rep("bb",5),rep("aa",40),rep("cc",60),rep("aa",30)),
Cel=rep(c("home","tree","hat","ball","arm","leg","beer","stick","pen","rope"),times=c(15,13,12,12,13,12,15,18,17,23)),
Value=c(runif(n=20,min = 7000, max = 100000),runif(n=40,min = 100, max = 100000),runif(n=60,min = 1000, max = 1000000),runif(n=30,min = 100000, max = 10000000)),
Col=rep(c("red","black"),each=5,times=15),
Effect=c(rep(c("length","height"),times=c(15,5)),rep(c("weight","length","age of youngest individual found miles from the closest coastline"),times=c(10,7,23)),rep(c("pressure","speed","rate","length"),times=c(10,3,7,40)),rep(c("length","rate","O2","fecundity"),times=c(3,4,7,16)))
)
library(ggplot2)
library(forcats)
reorder_within <- function(x, by, within, fun = max, sep = "___", ...) {
new_x <- paste(x, within, sep = sep)
stats::reorder(new_x, by, FUN = fun)
}
scale_x_reordered <- function(..., sep = "___") {
reg <- paste0(sep, ".+$")
ggplot2::scale_x_discrete(labels = function(x) gsub(reg, "", x), ...)
}
highlights<-data.frame(Pal=c("A","B","C","D"))
highlights$Pal<-factor(highlights$Pal,levels=c("A","B","C","D"))
ggplot() +
geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=1, ymax=1000000000), fill=c("yellow","blue","red","green"), alpha=0.05) +
geom_point(data = dat, aes(x=reorder_within(Effect, Value, Pal), y=Value, shape=Rol, col=Col)) +
scale_color_manual(breaks=unique(dat$Col), values=as.character(unique(dat$Col))) +
labs(x="",y="Activity") + facet_grid(.~Pal, scales = "free_x")+
scale_y_log10(limits=c(1,1000000000),breaks = c(1,10,100,1000,10000,100000,1000000,10000000,100000000,100000000,1000000000)) +
scale_x_reordered() +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1),
panel.background = element_rect(fill = "white", colour = "white"),
strip.background = element_rect(fill = "white", colour = "black"),
legend.key = element_rect(fill = "white"))
Created on 2022-07-20 by the reprex package (v2.0.1)
Old answer
You can use the functions reorder_within
and scale_x_reordered
from this GitHub like this:
dat<-data.frame(Pal=rep(c("A","B","C","D"),each=5),
Rol=c("aa","aa","aa","aa","bb","aa","aa","aa","aa","aa","cc","cc","cc","cc","cc","aa","aa","aa","aa","aa"),
Cel=rep(c("home","tree","hat","ball","pen","rope"),times=c(5,3,2,5,2,3)),
Value=c(7701.1,59897.3,59897.3,59897.3,744438.1,1226.4,1454.6,1454.6,1454.6,1454.6,56600,92400,5010000,7010000,15740000,28.5,34.2,39.9,48.5,57),
Col=c("black","red","black","black","red","red","red","black","black","black","red","red","red","red","red","red","black","black","black","black"),
Effect=c("length","length","length","length","height","weight","length","length","age of youngest individual found miles from the closest coastline","age of youngest individual found miles from the closest coastline","pressure","speed","rate","rate","length","length","rate","rate","O2","fecundity")
)
library(ggplot2)
library(forcats)
reorder_within <- function(x, by, within, fun = mean, sep = "___", ...) {
new_x <- paste(x, within, sep = sep)
stats::reorder(new_x, by, FUN = fun)
}
scale_x_reordered <- function(..., sep = "___") {
reg <- paste0(sep, ".+$")
ggplot2::scale_x_discrete(labels = function(x) gsub(reg, "", x), ...)
}
highlights<-data.frame(Pal=c("A","B","C","D"))
highlights$Pal<-factor(highlights$Pal,levels=c("A","B","C","D"))
ggplot() +
geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=1, ymax=1000000000), fill=c("yellow","blue","red","green"), alpha=0.05) +
geom_point(data = dat, aes(x=reorder_within(Effect, Value, Pal), y=Value, shape=Rol, col=Col)) +
scale_color_manual(breaks=unique(dat$Col), values=as.character(unique(dat$Col))) +
labs(x="",y="Activity") + facet_grid(.~Pal, scales = "free_x")+
scale_y_log10(limits=c(1,1000000000),breaks = c(1,10,100,1000,10000,100000,1000000,10000000,100000000,100000000,1000000000)) +
scale_x_reordered() +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1),
panel.background = element_rect(fill = "white", colour = "white"),
strip.background = element_rect(fill = "white", colour = "black"),
legend.key = element_rect(fill = "white"))
Created on 2022-07-20 by the reprex package (v2.0.1)
ordering y axis with facet
You can set your factor on the group column using the currently ordered values as the levels (reversed to get the ordering you want). This will lock in the ordering.
library(dplyr)
dat =data.frame(parent = c("J","J","F","F"),group= c("A(4)","C(3)","A(4)","D(5)"),value=c(1,2,3,4),
count = c(4,3,4,5))
dat <- dat %>% arrange(parent,-count )
dat$group <- factor(dat$group, levels = rev(unique(dat$group)))
ggplot(dat, aes(x = group, y= value))+
geom_bar(stat ="identity",position = "dodge")+
coord_flip()+
facet_wrap(~parent, scale = "free_y")
R / Tidyverse: Ordering factors within group with duplicate labels and plotting using facet_wrap
tidytext::reorder_within()
does something similar, and in combination with tidytext::scale_y_reordered()
helps with tidying the output to look like your goal.
library(tidytext)
dummy_data %>%
mutate(y_var = reorder_within(y_var, x_var, group_var)) %>%
ggplot() +
geom_point(aes(x = x_var, y = y_var, color = group_var), size = 5) +
scale_y_reordered() +
facet_wrap(~group_var, scales = 'free', dir = 'v')
R ggplot2 facet wrap dot plot reorder each
This could be achieved via reorder_within
+ scale_y_reordered
from the tidytext
package like so:
- Reorder you y axis variable by values within facets via
reorder_within
- Use
scale_y_reordered
- Set the scales free for your y-axis too.
library(ggplot2)
library(dplyr)
library(tidyr)
library(tidytext)
mtcars2 <- as_tibble(mtcars, rownames = 'car')
mtcars_long_numeric_with_mpg <- mtcars2 %>%
select(car, mpg, disp, hp, drat, wt, qsec) %>%
pivot_longer(names_to = 'names', values_to = 'values', 2:7) %>%
mutate(car = tidytext::reorder_within(car, values, names))
ggplot(mtcars_long_numeric_with_mpg, aes(x = values, y = reorder(car, values))) +
geom_point() +
tidytext::scale_y_reordered() +
facet_wrap(~names, scales = 'free')+
theme(text = element_text(size=6))
How to achieve a facet specific ordering of axis entries in ggplot geom_point?
One option would be to indeed use factor levels as you described, but use a prefix that seperates the levels for the different facets. Later, you can then use the labels
argument of the scale to remove the prefix. Example below:
set.seed(1)
m1 = c('1A','1B','1C','1D')
m2 = c('2A','2B','2C')
s = c(rep('s1',4),rep('s2',3))
d = data.frame(m = c(m1,m2),
s=factor(s),
v=sample(1:10, replace=T,7),
stringsAsFactors = FALSE)
d$m <- factor(d$m, levels = c("1A", "1C", "1B", "1D", "2B", "2A", "2C"))
library(ggplot2)
ggplot(d, aes(x=m, y=v)) +
geom_point(size=2) +
coord_flip() +
facet_grid( s~. , scales='free', space='free') +
scale_x_discrete(
labels = function(x){substr(x, 2, nchar(x))}
)
Varying factor order in each facet of ggplot2
Unfortunately factors can only have one set of levels. The only way i've found to do this is actually to create two separate data.frames from your data and re-level the factor in each. For example
data <- data.frame(
x = c(LETTERS[1:10],LETTERS[1:3],LETTERS[11:17]),
y = rnorm(n=20,10,2),
type= c(rep("J",10),rep("K",10))
)
data$type <- as.factor(data$type)
J<-subset(data, type=="J")
J$x <- reorder(J$x, J$y, max)
K<-subset(data, type=="K")
K$x <- reorder(K$x, K$y, max)
Now we can plot them with
ggplot(mapping = aes(x=y, y=x, xend=0, yend=x)) +
geom_segment(data=J, colour="grey50") +
geom_point(data=J, size=3, aes(colour=type)) +
geom_segment(data=K, colour="grey50") +
geom_point(data=K, size=3, aes(colour=type)) +
theme_bw() +
theme(panel.grid.major.y = element_blank()) +
facet_grid(type ~ ., scales="free_y", space="free_y")
which results in
Related Topics
Why Does "Hello" > 0 Return True
Changing Names in a List of Dataframes
Install R Packages in Azure Ml
Uri Routing for Shinydashboard Using Shiny.Router
Data.Frames in R: Name Autocompletion
Multiplying Combinations of a List of Lists in R
Importing Multiple .CSV Files with Variable Column Types into R
How to Unlock Environment in R
Follow-Up: Generalizing a Data.Frame Subsetting Function 2
R/Ggplot Cumulative Sum in Histogram
Shiny: How to Stop Processing Invalidatelater() After Data Was Abtained or at the Given Time
Use Hooks to Format Table in Output
R: Finding the Intersect of Two Lines
Format a Vector of Rows in Italic and Red Font in R Dt (Datatable)
How to Get Last Data for Each Id/Date