【问题标题】:Sort/arrange character column with condition使用条件对字符列进行排序/排列
【发布时间】:2021-11-09 06:53:17
【问题描述】:

我正在尝试使用dplyrarrange 根据条件对字符串进行排序。我想在一列上arrange,但如果第二列等于一个值,则按升序排列,如果第二列等于另一个值,则按降序排列。

我发现了几个类似的问题(其中一个是我自己以前的问题之一),但它们并不直接适用。它们都在数字列上排序,它们不适用于字符列。

R - Conditionally sort multiple columns as ascending or descending by group

Sort/arrange within group for only chosen groups

这里是示例数据:

df <- mtcars %>% as_tibble(rownames = "model") %>% select(model, mpg, cyl) %>% slice(1:8) %>% mutate(cond = c(rep("A", 4), rep("B", 4)))

如果 cond == "A",我想按升序对 "model" 进行排序。如果 cond == "B",我想按降序对 "model" 进行排序。我想要一个不依赖于bind_rows 或类似解决方案的解决方案。

可能是以下形式:

library(dplyr)
df %>% arrange(ifelse(cond == "A", model, desc(model)))

所需的解决方案如下所示:

## A tibble: 8 x 8
#  model              mpg   cyl cond 
#  <chr>              <dbl> <dbl> <chr>
#1 Datsun 710         22.8     4 A    
#2 Hornet 4 Drive     21.4     6 A    
#3 Mazda RX4          21       6 A    
#4 Mazda RX4 Wag      21       6 A    
#5 Valiant            18.1     6 B    
#6 Merc 240D          24.4     4 B    
#7 Hornet Sportabout  18.7     8 B    
#8 Duster 360         14.3     8 B    

【问题讨论】:

    标签: r sorting dplyr


    【解决方案1】:

    这很混乱,但它确实有效。

    mtcars %>% 
      as_tibble(rownames = "model") %>% 
      select(model, mpg, cyl) %>% 
      slice(1:8) %>% 
      mutate(cond = c(rep("A", 4), rep("B", 4))) %>%
      group_by(cond) %>%
      arrange(model, .by_group = TRUE) %>%
      add_rownames(., var = "rowna") %>%
      mutate(rowna = as.numeric(rowna)) %>%
      rowwise() %>%
      mutate(idx = ifelse(cond == "A", rowna, 8 - rowna)) %>%
      group_by(cond) %>%
      arrange(idx, .by_group = TRUE) %>%
      select(-idx, -rowna)
    
      model               mpg   cyl cond 
      <chr>             <dbl> <dbl> <chr>
    1 Datsun 710         22.8     4 A    
    2 Hornet 4 Drive     21.4     6 A    
    3 Mazda RX4          21       6 A    
    4 Mazda RX4 Wag      21       6 A    
    5 Valiant            18.1     6 B    
    6 Merc 240D          24.4     4 B    
    7 Hornet Sportabout  18.7     8 B    
    8 Duster 360         14.3     8 B 
    

    【讨论】:

      【解决方案2】:

      我们可能会使用group_modifygroup_by

      library(dplyr)
      df %>% 
          group_by(cond) %>%
          group_modify(~.x %>% 
                    arrange(if(.y == 'A') model else desc(model)) ) %>%
          ungroup %>%
          select(names(df))
      

      -输出

      # A tibble: 8 x 4
        model               mpg   cyl cond 
        <chr>             <dbl> <dbl> <chr>
      1 Datsun 710         22.8     4 A    
      2 Hornet 4 Drive     21.4     6 A    
      3 Mazda RX4          21       6 A    
      4 Mazda RX4 Wag      21       6 A    
      5 Valiant            18.1     6 B    
      6 Merc 240D          24.4     4 B    
      7 Hornet Sportabout  18.7     8 B    
      8 Duster 360         14.3     8 B   
      

      也可以单独subset 'model' sort 的值并在factor 中指定为levels 以得到arranged 相应

      df %>%  
         arrange(factor(model, levels = c(sort(model[cond == 'A']), 
                 sort(model[cond != "A"], decreasing = TRUE))))
      

      -输出

      # A tibble: 8 x 4
        model               mpg   cyl cond 
        <chr>             <dbl> <dbl> <chr>
      1 Datsun 710         22.8     4 A    
      2 Hornet 4 Drive     21.4     6 A    
      3 Mazda RX4          21       6 A    
      4 Mazda RX4 Wag      21       6 A    
      5 Valiant            18.1     6 B    
      6 Merc 240D          24.4     4 B    
      7 Hornet Sportabout  18.7     8 B    
      8 Duster 360         14.3     8 B    
      

      【讨论】:

        【解决方案3】:

        我发现通常情况下,基本 R 解决方案要简单得多:

        split(df, df$cond) <- lapply(split(df, df$cond), function(d) {
            d[order(d$model, decreasing = all(d$cond == "B")),]
        })
        

        这会按条件拆分数据集,然后根据条件是“A”还是“B”对每个拆分进行排序,然后将拆分重新插入到各自的位置。

        【讨论】:

          【解决方案4】:

          这可以通过dplyr::arrangeifelse 轻松完成。诀窍在于 {dplyr} 的 desc 函数具有不同的输出,其类型与其输入不同。对于这些情况,我们必须改用base::rev

          library(dplyr)
          
          df %>% 
            arrange(cond, ifelse(cond == "B", rev(model), model))
          
          #> # A tibble: 8 x 4
          #>   model               mpg   cyl cond 
          #>   <chr>             <dbl> <dbl> <chr>
          #> 1 Datsun 710         22.8     4 A    
          #> 2 Hornet 4 Drive     21.4     6 A    
          #> 3 Mazda RX4          21       6 A    
          #> 4 Mazda RX4 Wag      21       6 A    
          #> 5 Valiant            18.1     6 B    
          #> 6 Hornet Sportabout  18.7     8 B    
          #> 7 Merc 240D          24.4     4 B    
          #> 8 Duster 360         14.3     8 B
          

          reprex package (v0.3.0) 于 2021-09-18 创建

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-26
            • 1970-01-01
            • 2013-05-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多