Sort/arrange within group for only chosen groups

Viewed 43

I would like to sort/arrange data by group. That's easy enough. However, I only want to sort values within specific groups, not all groups.

I found one possible instance of a similar question at the link. But I found it to be confusing due to the framing of the question by the OP. Arrange values within a specific group

Sample data:

df <- data.frame(var = c("apple", "banana", "eggplant", "carrot", "dill", "fava", "garlic"), 
                 grp = c("A", "A", "B", "B", "B", "C", "C"), 
                 val = c(4, 2, 1, 3, 7, 6, 2))
df
#        var grp val
# 1    apple   A   4
# 2   banana   A   2
# 3   carrot   B   3
# 4     dill   B   7
# 5 eggplant   B   1
# 6     fava   C   6
# 7   garlic   C   2

Desired output:

#        var grp val
# 1    apple   A   4
# 2   banana   A   2
# 3 eggplant   B   1
# 4   carrot   B   3
# 5     dill   B   7
# 6   garlic   C   2
# 7     fava   C   6

Partial solution:

library(dplyr)
df %>% 
  group_by(grp) %>% 
  arrange(val, .by_group = T)

This of course sorts for all groups. How do I get it to sort for only the groups I would like sorted, which are "B" and "C"? I would like a tidyverse solution but feel free to post a base solution as well.

2 Answers

We can change the sign to the elements in 'val' that correspond to "A" group so that it is ordered in the opposite direction compared to the 'val' elements in other group

library(dplyr)
df %>%
      arrange(grp, val * c(1, -1)[(grp == 'A') + 1])

-output

        var grp val
1    apple   A   4
2   banana   A   2
3 eggplant   B   1
4   carrot   B   3
5     dill   B   7
6   garlic   C   2
7     fava   C   6

Or if the values for 'A' should be kept as such, then mltiply by 0 so that each value is same for 'A'

df %>%
      arrange(grp, val * c(1, 0)[(grp == 'A') + 1])
           var grp val
1    apple   A   4
2   banana   A   2
3 eggplant   B   1
4   carrot   B   3
5     dill   B   7
6   garlic   C   2
7     fava   C   6

NOTE: This is done without any group_by attribute


If we want to use the OP's way, i.e. using group_by

df %>% 
   group_by(grp)  %>%
    arrange(case_when(grp == 'A' ~ -1 * val, TRUE ~ val), 
         .by_group = TRUE) %>%
    ungroup

-ouptutu

# A tibble: 7 x 3
  var      grp     val
  <chr>    <chr> <dbl>
1 apple    A         4
2 banana   A         2
3 eggplant B         1
4 carrot   B         3
5 dill     B         7
6 garlic   C         2
7 fava     C         6

If the values in 'val' for grp 'A' are showed in descending order because of coincidence, then create a sequence column before doing the grouping and then use that for modifying

df %>% 
    mutate(rn = row_number()) %>%
    group_by(grp)  %>%
    arrange(case_when(grp == 'A' ~ as.numeric(rn), TRUE ~ val), 
         .by_group = TRUE) %>%
    ungroup %>% 
    dplyr::select(-rn)

-output

# A tibble: 7 x 3
  var      grp     val
  <chr>    <chr> <dbl>
1 apple    A         4
2 banana   A         2
3 eggplant B         1
4 carrot   B         3
5 dill     B         7
6 garlic   C         2
7 fava     C         6

Or using base R

df[with(df, order(grp, c(1, 0)[(grp == 'A') + 1] * val)),]
       var grp val
1    apple   A   4
2   banana   A   2
3 eggplant   B   1
4   carrot   B   3
5     dill   B   7
7   garlic   C   2
6     fava   C   6

You can filter the groups you want to arrange, sort them and bind to the remaining data.

library(dplyr)

order_groups <- c('B', 'C')

df %>%
  filter(grp %in% order_groups) %>%
  arrange(grp, val) %>%
  bind_rows(df %>%
             filter(!grp %in% order_groups)) %>%
  arrange(grp)

#.      var grp val
#1    apple   A   4
#2   banana   A   2
#3 eggplant   B   1
#4   carrot   B   3
#5     dill   B   7
#6   garlic   C   2
#7     fava   C   6
Related