I would like to reorder the levels of a factor in one column, but within groups defined by a grouping column.
Simple example data set:
df <- structure(list(a_factor = structure(1:6, .Label = c("a", "b",
"c", "d", "e", "f"), class = "factor"), group = structure(c(1L,
1L, 1L, 2L, 2L, 2L), .Label = c("group1", "group2"), class = "factor"),
value = 1:6), class = "data.frame", row.names = c(NA, -6L
))
> df
a_factor group value
1 a group1 1
2 b group1 2
3 c group1 3
4 d group2 4
5 e group2 5
6 f group2 6
More precisely, how do I reorder the factor levels, e.g. descending by value
where df$group == "group1"
, but ascending by value
where df$group == "group2"
, preferably in dplyr?
An expected output might be:
> df
a_factor group value
1 c group1 3
2 b group1 2
3 a group1 1
4 d group2 4
5 e group2 5
6 f group2 6
Although, the question is more generally about how to tackle this in dplyr.
We can negate based on group value then order:
df %>%
arrange(case_when(
group == "group1" ~ -value,
group == "group2" ~ value))
# a_factor group value
# 1 c group1 3
# 2 b group1 2
# 3 a group1 1
# 4 d group2 4
# 5 e group2 5
# 6 f group2 6
The following is a base R solution.
sp <- split(df$value, df$group)
sp <- lapply(seq_along(sp), function(i) sort(sp[[i]], decreasing = i == 1))
df$a_factor <- factor(df$a_factor, levels = df$a_factor[unlist(sp)])
df$a_factor
#[1] a b c d e f
#Levels: c b a d e f
df[order(df$a_factor), ]
# a_factor group value
#3 c group1 3
#2 b group1 2
#1 a group1 1
#4 d group2 4
#5 e group2 5
#6 f group2 6
To reorder the factor levels you can use forcats
(part of the tidyverse
), and do something like this...
library(forcats)
df2 <- df %>% mutate(a_factor = fct_reorder(a_factor,
value*(-1 + 2 * (group=="group1"))))
levels(df2$a_factor)
[1] "f" "e" "d" "a" "b" "c"
This does not rearrange the dataframe itself...
df2
a_factor group value
1 a group1 1
2 b group1 2
3 c group1 3
4 d group2 4
5 e group2 5
6 f group2 6
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With