【问题标题】:Rename sequence of elements if it occurs more than once (R, dplyr)如果多次出现,则重命名元素序列(R,dplyr)
【发布时间】:2020-05-17 09:08:18
【问题描述】:

我有一个数据集,df

 ID         Date
 A          9/9/2019 5:00:01
 A          9/9/2019 5:00:02
 A          9/9/2019 5:00:03
 B          9/9/2019 6:00:01
 B          9/9/2019 6:00:03
 B          9/9/2019 6:00:04
 A          9/9/2019 6:00:05
 A          9/9/2019 6:00:06
 A          9/9/2019 6:00:07
 c          9/9/2019 6:00:08
 c          9/9/2019 6:00:09
 A          9/9/2019 6:00:10
 A          9/9/2019 6:00:11

我愿意

 ID         Date
 A          9/9/2019 5:00:01
 A          9/9/2019 5:00:02
 A          9/9/2019 5:00:03
 B          9/9/2019 6:00:01
 B          9/9/2019 6:00:03
 B          9/9/2019 6:00:04
 list1      9/9/2019 6:00:05
 list1      9/9/2019 6:00:06
 list1      9/9/2019 6:00:07
 c          9/9/2019 6:00:08
 c          9/9/2019 6:00:09
 list2      9/9/2019 6:00:10
 list2      9/9/2019 6:00:11

如果一个元素序列在我的数据集中出现多次,我想重命名它

输出:

structure(list(ID = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 
1L, 3L, 3L, 1L, 1L), .Label = c("A", "B", "c"), class =   "factor"), 
Date = structure(1:13, .Label = c("9/9/2019 12:00:00 AM", 
"9/9/2019 12:00:01 AM", "9/9/2019 12:00:02 AM", "9/9/2019    12:00:03 AM", 
"9/9/2019 12:00:04 AM", "9/9/2019 12:00:05 AM", "9/9/2019   12:00:06 AM", 
"9/9/2019 12:00:07 AM", "9/9/2019 12:00:08 AM", "9/9/2019 12:00:09 AM", 
"9/9/2019 12:00:11 AM", "9/9/2019 12:00:12 AM", "9/9/2019 12:00:13 AM"
), class = "factor")), class = "data.frame", row.names = c(NA, 
-13L))

这是我尝试过的:

c(letters, do.call(paste0, expand.grid(letters, 1:1000)))
setDT(df[, grp := rleid(ID)][ItemSubject == "", 
ItemSubject := nm1[.GRP], grp][, grp := NULL][]

我之前使用过这个命令,但不确定是否将这个特定命令集成到代码中。

【问题讨论】:

    标签: r list loops data.table


    【解决方案1】:

    我觉得下面的做法比较干净:

    library(data.table)
    setDT(DT)
    
    DT[ , run_id := rleid(ID)]
    DT[DT[ , .SD[1L], by = run_id][duplicated(ID), ID := paste0('list', .I)],
       on = 'run_id', ID := i.ID][]
    #         ID                    Date run_id
    #     <fctr>                  <fctr>  <int>
    #  1:      A    9/9/2019 12:00:00 AM      1
    #  2:      A    9/9/2019 12:00:01 AM      1
    #  3:      A    9/9/2019 12:00:02 AM      1
    #  4:      B 9/9/2019    12:00:03 AM      2
    #  5:      B    9/9/2019 12:00:04 AM      2
    #  6:      B    9/9/2019 12:00:05 AM      2
    #  7:  list1  9/9/2019   12:00:06 AM      3
    #  8:  list1    9/9/2019 12:00:07 AM      3
    #  9:  list1    9/9/2019 12:00:08 AM      3
    # 10:      c    9/9/2019 12:00:09 AM      4
    # 11:      c    9/9/2019 12:00:11 AM      4
    # 12:  list2    9/9/2019 12:00:12 AM      5
    # 13:  list2    9/9/2019 12:00:13 AM      5
    

    数据基本上是跨行排序的——A 第 1-3 行系列与A 第 7-9 行系列不同。

    这暗示rleid 必须发挥作用。 rleid 为每个 series 生成一个我认为符合您对数据集的结构的 ID。

    现在,我们可以解决您的问题。正如您所描述的,每当run_id 增加但ID 重复时,我们希望替换ID 列。

    我采取的方法是尝试使用duplicated 来检测重复。问题是在原始表中,有A 的重复(例如行 2 & 3) 在我们所说的意义上不是重复的。我们希望首先将表每run_id 减少为一行,这样重复就意味着重复run_ids。这就是DT[ , .SD[1L], by = run_id] 部分的作用。请注意,严格来说,我们可以使用DT[ , .(ID = ID[1L]), by = run_id],因为我们不需要Date 列。

    现在,我们可以使用duplicated(ID) 来识别重复的行;由于第二个参数(通常称为j)在过滤器duplicated(ID) 发生后评估,因此只剩下两行,因此.I 与您想要的计数器完美对齐替换。

    一旦完成,我们可以通过加入和覆盖(又名update-on-join)替换原始表中的目标ID值。

    【讨论】:

    • 哇这工作!我现在将在我的大型数据集上尝试这个。这本质上是一个循环吗?
    • @tanishahudson 在某种程度上,一切都是一个循环? 说真的,我会专注于在如何考虑数据结构方面要带走什么,这里有一些重要的想法:(1)时间系列/有序数据 (2) 聚合 (3) 唯一性 (4) 连接
    【解决方案2】:

    在base R,我们可以用rle做到这一点

    df1$ID <- inverse.rle(within.list(rle(as.character(df1$ID)),  {
               i1 <- duplicated(values)
               values[i1] <- paste0("list", seq_len(sum(i1)))
                }))
    df1
    #      ID                    Date
    #1      A    9/9/2019 12:00:00 AM
    #2      A    9/9/2019 12:00:01 AM
    #3      A    9/9/2019 12:00:02 AM
    #4      B 9/9/2019    12:00:03 AM
    #5      B    9/9/2019 12:00:04 AM
    #6      B    9/9/2019 12:00:05 AM
    #7  list1  9/9/2019   12:00:06 AM
    #8  list1    9/9/2019 12:00:07 AM
    #9  list1    9/9/2019 12:00:08 AM
    #10     c    9/9/2019 12:00:09 AM
    #11     c    9/9/2019 12:00:11 AM
    #12 list2    9/9/2019 12:00:12 AM
    #13 list2    9/9/2019 12:00:13 AM
    

    【讨论】:

      【解决方案3】:

      这是另一个data.table 选项:

      DT[, nid := rleid(ID)][, 
          ri := rleid(nid), ID][
              ri > 1L, ID := paste0("list", ri - 1L), ID]
      

      输出:

             ID                    Date nid ri
       1:     A    9/9/2019 12:00:00 AM   0  1
       2:     A    9/9/2019 12:00:01 AM   0  1
       3:     A    9/9/2019 12:00:02 AM   0  1
       4:     B 9/9/2019    12:00:03 AM   1  1
       5:     B    9/9/2019 12:00:04 AM   1  1
       6:     B    9/9/2019 12:00:05 AM   1  1
       7: list1  9/9/2019   12:00:06 AM   2  2
       8: list1    9/9/2019 12:00:07 AM   2  2
       9: list1    9/9/2019 12:00:08 AM   2  2
      10:     c    9/9/2019 12:00:09 AM   3  1
      11:     c    9/9/2019 12:00:11 AM   3  1
      12: list2    9/9/2019 12:00:12 AM   4  3
      13: list2    9/9/2019 12:00:13 AM   4  3
      

      【讨论】:

        猜你喜欢
        • 2023-01-27
        • 1970-01-01
        • 2021-05-15
        • 1970-01-01
        • 2016-06-18
        • 2020-01-26
        • 1970-01-01
        • 2020-09-17
        • 2021-12-30
        相关资源
        最近更新 更多