【问题标题】:Comparing value of a certain row with all previous rows in data.table将某一行的值与 data.table 中的所有先前行进行比较
【发布时间】:2020-06-13 02:26:06
【问题描述】:

我有一个数据集,其中包含涉及某类产品的公司。 数据集如下所示:

df <- data.table(year=c(1979,1979,1980,1980,1980,1981,1981,1982,1982,1982,1982),
                 category = c("A","A","B","C","A","D","C","F","F","A","B"))

我想创建一个新变量如下: 如果一家公司进入了一个以前没有参与过的新类别以前几年(不是同一年),那么该条目将被标记为“NEW”,否则将被标记为“OLD ”。

因此,期望的结果将是:

    year   category   Newness
 1: 1979        A     NEW
 2: 1979        A     NEW
 3: 1980        B     NEW
 4: 1980        C     NEW
 5: 1980        A     OLD
 6: 1981        D     NEW
 7: 1981        C     OLD
 8: 1982        F     NEW
 9: 1982        F     NEW
10: 1982        A     OLD
11: 1982        B     OLD

我倾向于使用 data.table,因为我有超过 150 万个观察值,并且希望能够通过按公司 ID 分组来复制解决方案。

任何帮助将不胜感激,并提前感谢您。

【问题讨论】:

    标签: r group-by data.table grouping


    【解决方案1】:

    不是一个答案,但由于效率是一个问题,我想发布不同方法之间的比较。这是在我正在研究的专利数据库上运行的。

    > Ronak <- function()
    +   df[, Newness := c("NEW", "OLD")[(match(year, unique(year)) > 1) + 1], category]
    > B._Christian1 <- function()
    +   df[, Newness := fifelse(year == year[1L], "NEW", "OLD"), category]
    > B._Christian2 <- function()
    +   df[, Newness := c("NEW", "OLD")[match(year, year[1L], 2)], category]
    > Onyambu <- function()
    +   df[,Newness := ifelse(duplicated(.SD)==duplicated(category),"New","Old")]
    > chinsoon12 <- function()
    +   df[, Newness := "OLD"][unique(df, by="category"), on=.(year, category),
    +                                     Newness := "NEW"]
    > 
    > microbenchmark::microbenchmark(times=3L,
    +                                Ronak(), B._Christian1(), B._Christian2(), Onyambu(), chinsoon12())
    Unit: milliseconds
                expr       min        lq      mean    median        uq       max neval
             Ronak()  482.6191  482.7456  484.3963  482.8720  485.2849  487.6977     3
     B._Christian1()  240.3175  242.9452  243.9646  245.5729  245.7881  246.0033     3
     B._Christian2()  274.8113  278.3835  279.7271  281.9557  282.1850  282.4142     3
           Onyambu() 2374.6428 2377.0848 2379.3771 2379.5267 2381.7442 2383.9617     3
        chinsoon12()  200.6551  200.8337  202.5799  201.0123  203.5423  206.0723     3
    

    再次感谢大家。

    【讨论】:

      【解决方案2】:

      另一个data.table 选项:

      df[, Newness := "OLD"][
          unique(df, by="category"), on=.(year, category), Newness := "NEW"]
      

      计时码:

      library(data.table)
      set.seed(0L)
      nr <- 1.5e6
      df <- data.table(year=sample(1970:2019, nr, TRUE), category=sample(1e4, nr, TRUE))
      setkey(df, year, category)
      
      mtd0 <- function()
          df[, Newness := c("NEW", "OLD")[(match(year, unique(year)) > 1) + 1], category]
      
      mtd1 <- function() 
          df[, Newness := ifelse(duplicated(.SD)==duplicated(category),"New","Old")]
      
      mtd2 <- function()
          df[, Newness := "OLD"][
              unique(df, by="category"), on=.(year, category), Newness := "NEW"]
      
      microbenchmark::microbenchmark(times=3L,
          mtd0(), mtd1(), mtd2())
      

      时间安排:

      Unit: milliseconds
         expr      min       lq      mean   median       uq      max neval
       mtd0() 154.6129 167.5908 182.70500 180.5687 196.7511 212.9334     3
       mtd1() 343.3772 375.0303 395.08653 406.6835 420.9412 435.1989     3
       mtd2()  41.4178  42.0520  45.40527  42.6862  47.3990  52.1118     3
      

      【讨论】:

      • 我觉得你的回答不对。由于您生成的数据集中的第一年是 1970 年,我们预计变量 Newness 的所有实例在 1970 年都是 New(没有前一年!)。如果您使用代码生成的输出将 1970 年的变量 Newness 制成表格,您会发现某些实例采用值 OLD(这不应该是这种情况,因为 1970 年没有旧类别!)。例如,mtd2()[year==1970, .N, Newness] 提供 775 个新实例和 29103 个旧实例!此外,您的所有输出都不相同。
      • 如果你在可重现的数据集上应用你的代码,你会意外得到预期的输出;但是,这是因为每个类别第一次出现在可重复数据中实际上与公司进入该类别的第一年相吻合;并且不能保证整个数据集都是这种情况。
      • 啊,我明白你的意思了。数据需要排序
      • @chinsoon12 无论如何我可以限制在某个时间段内吗?例如,我不会查看整个数据集,而是查找过去 5 年的匹配项?
      • @chinsoon12。感谢您的回答。我在stackoverflow.com/questions/62926480/… 上将其作为一个单独的问题提出
      【解决方案3】:

      您可以通过以下方式解决您的问题:

      # Method 1:
      setDT(df, key = "year")[, Newness := fifelse(year == year[1L], "NEW", "OLD"), category]  
      
      # Method 2
      setDT(df, key = "year")[, Newness := c("NEW", "OLD")[match(year, year[1L], 2)], category]
      
      #      year category Newness
      # 1:   1979        A     NEW
      # 2:   1979        A     NEW
      # 3:   1980        B     NEW
      # 4:   1980        C     NEW
      # 5:   1980        A     OLD
      # 6:   1981        D     NEW
      # 7:   1981        C     OLD
      # 8:   1982        F     NEW
      # 9:   1982        F     NEW
      # 10:  1982        A     OLD
      # 11:  1982        B     OLD
      

      【讨论】:

      • 无论如何我可以限制在某个时间段内吗?例如,我不会查看整个数据集,而是查找过去 5 年的匹配项?
      【解决方案4】:

      您可以在基础 R 中使用 duplicated + ifelse:

      transform(df,Newness = ifelse(duplicated(category)==duplicated(df),"New","Old"))
          year category Newness
       1: 1979        A     New
       2: 1979        A     New
       3: 1980        B     New
       4: 1980        C     New
       5: 1980        A     Old
       6: 1981        D     New
       7: 1981        C     Old
       8: 1982        F     New
       9: 1982        F     New
      10: 1982        A     Old
      11: 1982        B     Old
      

      在 data.table 中你会做:

      library(data.table)
      df[,Newness := ifelse(duplicated(.SD)==duplicated(category),"New","Old")]
      df
          year category Newness
       1: 1979        A     New
       2: 1979        A     New
       3: 1980        B     New
       4: 1980        C     New
       5: 1980        A     Old
       6: 1981        D     New
       7: 1981        C     Old
       8: 1982        F     New
       9: 1982        F     New
      10: 1982        A     Old
      11: 1982        B     Old
      

      【讨论】:

      • 我觉得你的回答不对。您获得了预期的输出,因为每个类别第一次出现在可重复数据中实际上与公司进入该类别的第一年一致。不能保证整个数据集都是这种情况。如果您更改行的顺序(例如 set.seed(2); df
      • @Onyambu 我在stackoverflow.com/questions/62926480/… 提出了一个单独的相关问题,如果您对此有任何想法,我将不胜感激。
      【解决方案5】:

      我们可以将每个category 的第一年分配为"NEW"。

      library(data.table)
      df[, Newness := c("NEW", "OLD")[(match(year, unique(year)) > 1) + 1], category]
      df
      
      #    year category Newness
      # 1: 1979        A     NEW
      # 2: 1979        A     NEW
      # 3: 1980        B     NEW
      # 4: 1980        C     NEW
      # 5: 1980        A     OLD
      # 6: 1981        D     NEW
      # 7: 1981        C     OLD
      # 8: 1982        F     NEW
      # 9: 1982        F     NEW
      #10: 1982        A     OLD
      #11: 1982        B     OLD
      

      类似地,dplyr 可以写成:

      library(dplyr)
      df %>%
        group_by(category) %>%
        mutate(Newness =  c("NEW", "OLD")[(match(year, unique(year)) > 1) + 1])
      

      【讨论】:

      • 我觉得你的回答不对。您获得了预期的输出,因为每个类别第一次出现在可重复数据中实际上与公司进入该类别的第一年一致。不能保证整个数据集都是这种情况。如果您更改行的顺序(例如set.seed(2);df &lt;- df[sample(nrow(df))]),您将不再获得预期的输出。
      • @B.ChristianKamgang 如果数据未排序,您可以随时order。
      • 是的,但是只要没有指定数据是有序的,我认为你不应该假设它是,如果你这样做了,那么我认为你应该明确指定有必要获得正确的结果。
      • @RonakShah 无论如何我可以限制在某个时间段内吗?例如,我不会查看整个数据集,而是查找过去 5 年的匹配项?
      • 我不认为这是一个简单/小的修复。也许,把它当作一个新问题来问?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-23
      • 2019-12-31
      • 1970-01-01
      • 1970-01-01
      • 2022-06-15
      • 1970-01-01
      相关资源
      最近更新 更多