【问题标题】:Identification of new values cumulatively by groups in data.table in r按 r 中 data.table 中的组累积识别新值
【发布时间】:2019-02-24 16:07:16
【问题描述】:

如何创建一个新列,通过Year + Month 的唯一梳组累积识别Letter 列中出现的新值?

数据样本。

require(data.table)
dt <- data.table(Letter = c(LETTERS[c(5, 1:2, 1:2, 1:4, 3:6)]),
                 Year = 2018,
                 Month = c(rep(5,5), rep(6,4), rep(7,4)))

打印。

    Letter Year Month
 1:      E 2018     5
 2:      A 2018     5
 3:      B 2018     5
 4:      A 2018     5
 5:      B 2018     5
 6:      A 2018     6
 7:      B 2018     6
 8:      C 2018     6
 9:      D 2018     6
10:      C 2018     7
11:      D 2018     7
12:      E 2018     7
13:      F 2018     7

我想要得到的结果:

    Letter Year Month   New
 1:      E 2018     5  TRUE
 2:      A 2018     5  TRUE
 3:      B 2018     5  TRUE
 4:      A 2018     5  TRUE
 5:      B 2018     5  TRUE
 6:      A 2018     6 FALSE
 7:      B 2018     6 FALSE
 8:      C 2018     6  TRUE
 9:      D 2018     6  TRUE
10:      C 2018     7 FALSE
11:      D 2018     7 FALSE
12:      E 2018     7 FALSE
13:      F 2018     7  TRUE

详细问题:

  1. Group1(“E”、“A”、“B”、“A”、“B”)默认为 TRUE,没有可比性。
  2. group2 中的哪些字母(“A”、“B”、“C”、“D”)在 group1 中不重复。
  3. 那么,第 3 组(“C”、“D”、“E”、“F”)中的哪些字母在第 1 组和第 2 组(“E”、“A”、“B”、“A”)中不重复、“B”、“A”、“B”、“C”、“D”)。

【问题讨论】:

    标签: r dataframe dplyr data.table tidyverse


    【解决方案1】:

    简单地说:

     # dt[,new := ifelse(Letter %in% dt$Letter[dt$Month<Month],F,T), by="Month"][]
    
     #   Letter Year Month   new
     #1:      E 2018     5  TRUE
     #2:      A 2018     5  TRUE
     #3:      B 2018     5  TRUE
     #4:      A 2018     5  TRUE
     #5:      B 2018     5  TRUE
     #6:      A 2018     6 FALSE
     #7:      B 2018     6 FALSE
     #8:      C 2018     6  TRUE
     #9:      D 2018     6  TRUE
    #10:      C 2018     7 FALSE
    #11:      D 2018     7 FALSE
    #12:      E 2018     7 FALSE
    #13:      F 2018     7  TRUE
    

    使用 David A. 的非常有效的 cmets,更快、更简洁的版本:(推荐

    dt[, new := !(Letter %in% dt$Letter[dt$Month<Month]), by=Month][]
    

    【讨论】:

    • @Andre 答案很简单!谢谢!
    • @David 感谢您的加入。
    • 如果有多年,仅按月分组和测试不平等是不够的,对吧?不过,您可以创建一个新的 YearMonth := paste(Year, Month) 变量并使用它。
    • 我不知道当你有不同的年份时逻辑是什么。我宁愿按年分开。使用我的代码和 rbind。
    • @Andre BTW 用 data.table 的原生 %chin% 替换 %in% 使其更加高效。
    【解决方案2】:

    另一种可能的方法:

    dupes <- c()
    dt[, New := {
        x <- !Letter %chin% dupes
        dupes <- c(dupes, unique(Letter[x]))
        x
    }, by=.(Year, Month)]
    

    以下时间供参考:

    如果字母是整数:

    library(microbenchmark)
    microbenchmark(mtd0=dt0[, New := !(Letter %in% dt0$Letter[dt0$Month<Month]), by=Month],
        mtd1={
            dt1[, v := FALSE]
            dt1[unique(dt1, by="Letter"), on=.(Letter, Year, Month), v := TRUE]
        },
        mtd2={
            dupes <- c()
            dt2[, New := {
                x <- !Letter %in% dupes
                dupes <- c(dupes, unique(Letter[x]))
                x
            }, by=.(Year, Month)]        
        },
        times=3L)
    

    整数定时输出:

    Unit: milliseconds
     expr       min       lq      mean    median        uq      max neval
     mtd0 1293.3100 1318.775 1331.7129 1344.2398 1350.9143 1357.589     3
     mtd1  377.1534  391.178  402.4423  405.2026  415.0868  424.971     3
     mtd2 2015.2115 2020.926 2023.7209 2026.6400 2027.9756 2029.311     3
    

    如果字母是一个字符:

    microbenchmark(mtd0=dt0[, New := !(Letter %chin% dt0$Letter[dt0$Month<Month]), by=Month],
        mtd1={
            dt1[, v := FALSE]
            dt1[unique(dt1, by="Letter"), on=.(Letter, Year, Month), v := TRUE]
        },
        mtd2={
            dupes <- c()
            dt2[, New := {
                x <- !Letter %chin% dupes
                dupes <- c(dupes, unique(Letter[x]))
                x
            }, by=.(Year, Month)]        
        },
        times=3L)
    

    定时输出:

    Unit: milliseconds
     expr       min        lq      mean    median        uq       max neval
     mtd0 1658.5806 1689.8941 1765.9329 1721.2076 1819.6090 1918.0105     3
     mtd1  849.2361  851.1807  852.8632  853.1253  854.6768  856.2283     3
     mtd2  420.1013  426.0941  433.9202  432.0869  440.8296  449.5723     3
    

    检查:

    > identical(dt2$New, dt1$v)
    [1] TRUE
    > identical(dt0$New, dt1$v)
    [1] FALSE
    

    数据:

    set.seed(0L)
    nr <- 1e7
    dt <- unique(data.table(Letter=sample(nr/1e2, nr, replace=TRUE),
        Year=sample(2014:2018, nr, replace=TRUE),
        Month=sample(1:12, nr, replace=TRUE)))
    setorder(dt, Year, Month)#[, Letter := as.character(Letter)]
    dt0 <- copy(dt)
    dt1 <- copy(dt)
    dt2 <- copy(dt)
    
    #for seed=0L, dt has about 4.8mio rows
    

    【讨论】:

    • 仅供参考,您在 DT[...] 之外的欺骗对象不会被修改。这不是问题,但有一些可能感兴趣的替代方案:它是否仅存在于 DT[...] 内部,例如 dt[, New := { if (.GRP == 1L) dupes &lt;- c(); x &lt;- !Letter %chin% dupes; dupes &lt;- c(dupes, unique(Letter[x])); x }, by=.(Year, Month)] 或使用 &lt;&lt;- 修改外部对象。
    • 谢谢,弗兰克。是的,如果需要unique设置,那么可以使用&lt;&lt;-来存储骗子,那么不需要再次调用unique
    • @chinsoon12 谢谢!很棒的方法!尤其是在字符串的效率方面。
    【解决方案3】:

    初始化为 FALSE;然后将每个字母加入到第一个年月并更新为 TRUE:

    dt[, v := FALSE]
    dt[unique(dt, by="Letter"), on=.(Letter, Year, Month), v := TRUE][]
    
        Letter Year Month     v
     1:      E 2018     5  TRUE
     2:      A 2018     5  TRUE
     3:      B 2018     5  TRUE
     4:      A 2018     5  TRUE
     5:      B 2018     5  TRUE
     6:      A 2018     6 FALSE
     7:      B 2018     6 FALSE
     8:      C 2018     6  TRUE
     9:      D 2018     6  TRUE
    10:      C 2018     7 FALSE
    11:      D 2018     7 FALSE
    12:      E 2018     7 FALSE
    13:      F 2018     7  TRUE
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-05
      • 2016-02-19
      • 2021-09-07
      • 1970-01-01
      • 2013-06-19
      • 2018-08-07
      相关资源
      最近更新 更多