【问题标题】:reorder rows by group based on another index value in r根据 r 中的另一个索引值按组重新排序行
【发布时间】:2020-12-14 17:58:20
【问题描述】:

Base R 中是否有一种方法可以按组根据列值对数据进行重新排序? 例如,如果我有一个数据集 m:

name  v1   v2   v3
a1     4    1    2
a2     1    3    2
a3     1    5    2
b1     1    2    2
b2     1    4    2
b3     1    3    2
c1     6    1    2
c2     5    1    2
c3     4    1    2

并且我只有每个组都有另一个索引向量,这意味着如果我的数据有 3 个组 a、b、c,则此索引向量只有 3 行: 2 3 1

现在我想根据这个索引向量(升序,1,2,3)对我的数据集重新排序,所以组的顺序应该是 c、a、b,但组内的顺序没有变化,如下所示:

name  v1   v2   v3
c1     6    1    2
c2     5    1    2
c3     4    1    2
a1     4    1    2
a2     1    3    2
a3     1    5    2
b1     1    2    2
b2     1    4    2
b3     1    3    2

在 Base R 中有没有办法做到这一点?谢谢!

【问题讨论】:

    标签: r grouping string-matching


    【解决方案1】:

    假设你有一个命名的索引向量v

    v <- c(a=2, b=3, c=1)
    

    在基础 R 中,您可以使用 match 按以下方式创建一个临时的 id 列,然后 order 您的结果。

    ds1$id <- v[match(substr(ds1$name, 1, 1), names(v))]
    ds1[order(ds1$id), -5]
    #   name v1 v2 v3
    # 7   c1  6  1  2
    # 8   c2  5  1  2
    # 9   c3  4  1  2
    # 1   a1  4  1  2
    # 2   a2  1  3  2
    # 3   a3  1  5  2
    # 4   b1  1  2  2
    # 5   b2  1  4  2
    # 6   b3  1  3  2
    

    如果没有"name" 列,您可以将sequences 中的行nsamplerbind 重新组合在一起。

    ds2 <- ds1[2:4]  ## generate data w/o "name" column
    
    n <- 3
    do.call(rbind, sample(split(ds2, rep(seq(nrow(ds2)/n), each=n))))
    #     v1 v2 v3
    # 3.7  6  1  2
    # 3.8  5  1  2
    # 3.9  4  1  2
    # 1.1  4  1  2
    # 1.2  1  3  2
    # 1.3  1  5  2
    # 2.4  1  2  2
    # 2.5  1  4  2
    # 2.6  1  3  2
    

    除以n 有余数,就像n=4 的情况一样,您会收到警告,多余的行将分配给其他组。不知道这是否适合您?


    数据

    ds1 <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
    "c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
    3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
    2L, 2L, 2L), id = c(2, 2, 2, 3, 3, 3, 1, 1, 1)), row.names = c(NA, 
    -9L), class = "data.frame")
    

    【讨论】:

    • 我会赞成,因为这个解决方案更实用,并且只使用base-R +1 中的向量,我使用了一个数据框,它与你的非常相似。
    • 您好,感谢您的帮助。如果我没有第一列 a、b、c 怎么办,并且我想更改行的顺序但只更改组之间的顺序,例如每三行一起移动。有没有办法做到这一点?
    【解决方案2】:

    仅使用 base R 您需要一个关键数据框来存储您的向量:

    #Code
    keys <- data.frame(name=c('a','b','c'),order=c(2,3,1),stringsAsFactors = F)
    #Add
    df$Index <- keys[match(substring(df$name,1,1),keys$name),"order"]
    df <- df[order(df$Index),]
    df$Index <- NULL
    

    输出:

    df
      name v1 v2 v3
    7   c1  6  1  2
    8   c2  5  1  2
    9   c3  4  1  2
    1   a1  4  1  2
    2   a2  1  3  2
    3   a3  1  5  2
    4   b1  1  2  2
    5   b2  1  4  2
    6   b3  1  3  2
    

    使用的一些数据:

    #Data
    df <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
    "c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
    3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
    2L, 2L, 2L)), class = "data.frame", row.names = c(NA, -9L))
    

    【讨论】:

      【解决方案3】:

      一个选项是在factor 转换的“名称”列上使用arrange,删除数字并在自定义顺序中指定levels 或“c”、“a”、“b”

      library(dplyr)
      df1 %>% 
         arrange(factor(sub("\\d+", "", name), levels = c("c", "a", "b")))
      

      -输出

      #   name v1 v2 v3
      #1   c1  6  1  2
      #2   c2  5  1  2
      #3   c3  4  1  2
      #4   a1  4  1  2
      #5   a2  1  3  2
      #6   a3  1  5  2
      #7   b1  1  2  2
      #8   b2  1  4  2
      #9   b3  1  3  2
      

      或仅在base R 中使用相同的语法

      df1[order(with(df1, factor(sub("\\d+", "", name), levels = c('c', 'a', 'b')))), ]
      #   name v1 v2 v3
      #7   c1  6  1  2
      #8   c2  5  1  2
      #9   c3  4  1  2
      #1   a1  4  1  2
      #2   a2  1  3  2
      #3   a3  1  5  2
      #4   b1  1  2  2
      #5   b2  1  4  2
      #6   b3  1  3  2
      

      数据

      df1 <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
      "c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
      3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
      2L, 2L, 2L)), class = "data.frame", row.names = c(NA, -9L))
      

      【讨论】:

        【解决方案4】:

        顺序必须基于任何感兴趣的列。如果要根据变量 v1 排序,那么:

        dad <- data.frame(names = paste0(rep(letters[1:3],rep(3,3)),1:3),v1=rnorm(9),v2=rnorm(9))
        dad[order(dad$v1),]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-28
          • 1970-01-01
          • 2014-10-27
          • 1970-01-01
          • 2020-08-04
          • 2013-04-09
          • 2023-03-30
          相关资源
          最近更新 更多