【问题标题】:get index by some variable in long format通过长格式的某个变量获取索引
【发布时间】:2019-11-25 19:33:40
【问题描述】:

我有一些长格式的数据,

library(data.table)
dat <- data.table(id=1:12, group=rep(1:2, each=6), time=c(rep(9:8, each=3),rep(6:7, each=3)), measure=1:3)
> dat
    id group time measure
 1:  1     1    9       1
 2:  2     1    9       2
 3:  3     1    9       3
 4:  4     1    8       1
 5:  5     1    8       2
 6:  6     1    8       3
 7:  7     2    6       1
 8:  8     2    6       2
 9:  9     2    6       3
10: 10     2    7       1
11: 11     2    7       2
12: 12     2    7       3

我想创建一个新变量,在每个group 中给出time 的索引。也就是说,期望的输出是

> res
    id group time measure index
 1:  1     1    9       1     2
 2:  2     1    9       2     2
 3:  3     1    9       3     2
 4:  4     1    8       1     1
 5:  5     1    8       2     1
 6:  6     1    8       3     1
 7:  7     2    6       1     1
 8:  8     2    6       2     1
 9:  9     2    6       3     1
10: 10     2    7       1     2
11: 11     2    7       2     2
12: 12     2    7       3     2

如果每个组内每次只有一行(即没有measure 变量),我会按照以下方式做一些事情

dat[order(group,time), .(index=seq_len(.N)), by=.(group)]

但在这种情况下,我不知所措。

【问题讨论】:

    标签: r indexing data.table


    【解决方案1】:

    OP 与他/她的尝试非常接近。这是使用rleid 的另一个选项:

    DT1[order(group, time), index := rleid(time), group]
    

    计时码:

    library(data.table)
    set.seed(0L)
    nr <- 1e6
    ng <- nr/10
    nt <- nr/2
    DT <- data.table(group=sample(ng, nr, TRUE), time=sample(nt, nr, TRUE))
    DT0 <- copy(DT)
    DT1 <- copy(DT)
    
    mtd0 <- function() DT0[, index := frank(time, ties.method = "dense"), group]
    
    mtd1 <- function() DT1[order(group, time), index := rleid(time), group]
    
    bench::mark(mtd0(), mtd1(), check=FALSE)
    identical(DT1$index, DT0$index)
    #[1] TRUE
    

    时间安排:

    # A tibble: 2 x 13
      expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result              memory            time    gc          
      <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>              <list>            <list>  <list>      
    1 mtd0()          42s      42s    0.0238    1.57GB    0.691     1    29        42s <df[,2] [1,000,000~ <df[,3] [300,811~ <bch:t~ <tibble [1 ~
    2 mtd1()        398ms    404ms    2.47     28.29MB    3.71      2     3      809ms <df[,2] [1,000,000~ <df[,3] [494 x 3~ <bch:t~ <tibble [2 ~
    

    另一个比较:

    mtd2 <- function() DT2[, g := .GRP, keyby=.(group, time)][, g2 := g - first(g) + 1L, by=group]
    
    bench::mark(#mtd0(), 
        mtd1(), mtd2(), check=FALSE)
    

    时间安排:

    # A tibble: 2 x 13
      expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result               memory          time    gc           
      <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>               <list>          <list>  <list>       
    1 mtd1()        370ms    372ms      2.69    24.5MB     4.03     2     3      745ms <df[,3] [1,000,000 ~ <df[,3] [101 x~ <bch:t~ <tibble [2 x~
    2 mtd2()        464ms    469ms      2.13    23.7MB     1.07     2     1      937ms <df[,4] [1,000,000 ~ <df[,3] [16 x ~ <bch:t~ <tibble [2 x~
    

    【讨论】:

    • 如果对表格进行排序没问题,这是我认为我在 Matt 或 Arun 的回答中看到的另一种方式:DT3[, g := .GRP, keyby=.(group, time)][, g2 := g - first(g) + 1L, by=group][]; fsetequal(DT1, DT3[, .(group, time, index = g2)])stackoverflow.com/a/35372767
    • 你回到你的电脑!
    【解决方案2】:

    让我们使用 dplyr 包中的dense_rank

    dat[,index:=dplyr::dense_rank(time),group]
    
        id group time measure     index
     1:  1     1    9       1         2
     2:  2     1    9       2         2
     3:  3     1    9       3         2
     4:  4     1    8       1         1
     5:  5     1    8       2         1
     6:  6     1    8       3         1
     7:  7     2    6       1         1
     8:  8     2    6       2         1
     9:  9     2    6       3         1
    10: 10     2    7       1         2
    11: 11     2    7       2         2
    12: 12     2    7       3         2
    

    【讨论】:

    • 或者使用 frank from data.tableties.method = "dense": dat[, index := frank(time, ties.method = "dense"), by=group][]
    • 太好了,谢谢马库斯!我宁愿留在 data.table 中。想让它成为答案吗?
    【解决方案3】:

    除了现有答案之外,另一种方法是使用 base R 中的 split()order() 。因此,此方法也可用于data.frame() 作为输入数据类型,但需要将data.table() 更改为data.frame()

    以下代码与您的dat 一起作为data.table()

    dt <- Reduce(rbind,Map(function(v) data.table(v,index = with(v,cumsum(c(1,diff(sort(time))))[order(time)])),split(dat,dat$group)))
    

    屈服:

    > dt
        id group time measure index
     1:  1     1    9       1     2
     2:  2     1    9       2     2
     3:  3     1    9       3     2
     4:  4     1    8       1     1
     5:  5     1    8       2     1
     6:  6     1    8       3     1
     7:  7     2    6       1     1
     8:  8     2    6       2     1
     9:  9     2    6       3     1
    10: 10     2    7       1     2
    11: 11     2    7       2     2
    12: 12     2    7       3     2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-28
      • 1970-01-01
      • 2022-07-16
      • 1970-01-01
      • 1970-01-01
      • 2023-01-02
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多