【问题标题】:r data.table adjust min and max years only if each set has at least one incrementing obsr data.table 仅当每组至少有一个递增的 obs 时才调整最小和最大年份
【发布时间】:2020-10-13 04:14:48
【问题描述】:

我有一个包含 id、位置、开始年份、结束年份、age1 和 age2 的数据集。对于定义为 id、location、age1 和 age2 的每个组,我想创建新的开始和结束年份。例如,我可能有 3 个 china 条目,涵盖 0 - 4 岁。一个是 2000 - 2000,另一个是 2001 - 2001,最后一个是 2005-2005。由于前两个条目中的年份递增 1,因此我希望它们对应的 newstart 和 newend 为 2000-2001。第三个条目将具有 newstart==2005 和 newend==2005,因为这不是连续年份的一部分。

我拥有的数据表类似于以下,除了它有数千个条目许多组合:

    id    location   start   end   age1   age2
    1     brazil     2000    2000  0      4
    1     brazil     2001    2001  0      4
    1     brazil     2002    2002  0      4
    2     argentina  1990    1991  1      1
    2     argentina  1991    1991  2      2
    2     argentina  1992    1992  2      2
    2     argentina  1993    1993  2      2
    3     belize     2001    2001  0.5    1
    3     belize     2005    2005  1      2

我想更改数据表,使其如下所示

    id    location   start   end   age1   age2  newstart   newend
    1     brazil     2000    2000  0      4     2000       2002
    1     brazil     2001    2001  0      4     2000       2002
    1     brazil     2002    2002  0      4     2000       2002
    2     argentina  1990    1991  1      1     1991       1991
    2     argentina  1991    1991  2      2     1991       1993
    2     argentina  1992    1992  2      2     1991       1993
    2     argentina  1993    1993  2      2     1991       1993
    3     belize     2001    2001  0.5    1     2001       2001
    3     belize     2005    2005  1      2     2005       2005

我尝试使用滞后创建一个变量来跟踪前一年和本年度的差异,然后计算这两年之间的差异。然后我通过放置最小开始和最大结束来创建 newstart 和 newend。我发现这只有在连续年份有一组 2 时才有效。如果我有一个更大的集合,这将不起作用,因为它无法跟踪每个分组的年份增加 1 的 obs 数量。我相信我需要某种类型的循环。

有没有更有效的方法来做到这一点?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    数据表

    你标记了,所以我的第一个建议是:

    library(data.table)
    dat[, contiguous := rleid(c(TRUE, diff(start) == 1)), by = .(id)]
    dat[, c("newstart", "newend") := .(min(start), max(end)), by = .(id, contiguous)]
    dat[, contiguous := NULL]
    dat
    #    id  location start  end age1 age2 newstart newend
    # 1:  1    brazil  2000 2000  0.0    4     2000   2002
    # 2:  1    brazil  2001 2001  0.0    4     2000   2002
    # 3:  1    brazil  2002 2002  0.0    4     2000   2002
    # 4:  2 argentina  1990 1991  1.0    1     1990   1993
    # 5:  2 argentina  1991 1991  2.0    2     1990   1993
    # 6:  2 argentina  1992 1992  2.0    2     1990   1993
    # 7:  2 argentina  1993 1993  2.0    2     1990   1993
    # 8:  3    belize  2001 2001  0.5    1     2001   2001
    # 9:  3    belize  2005 2005  1.0    2     2005   2005
    

    基础 R

    如果你真的只是指data.frame,那么

    dat <- transform(dat, contiguous = ave(start, id, FUN = function(a) cumsum(c(TRUE, diff(a) != 1))))
    dat <- transform(dat,
      newstart = ave(start, id, contiguous, FUN = min),
      newend   = ave(end  , id, contiguous, FUN = max)
    )
    # Warning in FUN(X[[i]], ...) :
    #   no non-missing arguments to min; returning Inf
    # Warning in FUN(X[[i]], ...) :
    #   no non-missing arguments to min; returning Inf
    # Warning in FUN(X[[i]], ...) :
    #   no non-missing arguments to max; returning -Inf
    # Warning in FUN(X[[i]], ...) :
    #   no non-missing arguments to max; returning -Inf
    
    dat
    #   id  location start  end age1 age2 newstart newend contiguous
    # 1  1    brazil  2000 2000  0.0    4     2000   2002          1
    # 2  1    brazil  2001 2001  0.0    4     2000   2002          1
    # 3  1    brazil  2002 2002  0.0    4     2000   2002          1
    # 4  2 argentina  1990 1991  1.0    1     1990   1993          1
    # 5  2 argentina  1991 1991  2.0    2     1990   1993          1
    # 6  2 argentina  1992 1992  2.0    2     1990   1993          1
    # 7  2 argentina  1993 1993  2.0    2     1990   1993          1
    # 8  3    belize  2001 2001  0.5    1     2001   2001          1
    # 9  3    belize  2005 2005  1.0    2     2005   2005          2
    dat$contiguous <- NULL
    

    我刚刚了解到ave 的有趣点:它使用interaction(...)(所有分组变量),它将给出所有可能的组合,而不仅仅是在数据中观察到的组合。因此,FUNction 可能会以零数据调用。在这种情况下,它确实发出了警告。人们可以用function(a) suppressWarnings(min(a)) 而不是min 来抑制这一点。

    【讨论】:

    • 这只有在每个条目的年份递增 1 时才有效。我只想要递增 1 的年份的最小和最大年份...即 2000、20001、2002。如果年份是't 在一个组中递增,然后我希望 newstart 和 newend 将原始年份保存在表中。
    • 已修复,我认为符合要求。
    • min/max代替first/last更好吗?
    【解决方案2】:

    我们可以使用 dplyr。按'id'分组后,取'start'和'start'的lag之差,应用rleid得到run-length-id'并创建'newstart','newend'为“开始”的minmax

    library(dplyr)
    library(data.table)
    df1 %>% 
       group_by(id) %>%
       group_by(grp =  rleid(replace_na(start - lag(start), 1)),
         .add = TRUE) %>%
       mutate(newstart = min(start), newend = max(end))
    

    -输出

    # A tibble: 9 x 9
    # Groups:   id, grp [4]
    #     id location  start   end  age1  age2   grp newstart newend
    #  <int> <chr>     <int> <int> <dbl> <int> <int>    <int>  <int>
    #1     1 brazil     2000  2000   0       4     1     2000   2002
    #2     1 brazil     2001  2001   0       4     1     2000   2002
    #3     1 brazil     2002  2002   0       4     1     2000   2002
    #4     2 argentina  1990  1991   1       1     1     1990   1993
    #5     2 argentina  1991  1991   2       2     1     1990   1993
    #6     2 argentina  1992  1992   2       2     1     1990   1993
    #7     2 argentina  1993  1993   2       2     1     1990   1993
    #8     3 belize     2001  2001   0.5     1     1     2001   2001
    #9     3 belize     2005  2005   1       2     2     2005   2005
    

    data.table

    library(data.table)
    setDT(df1)[, grp := rleid(replace_na(start - shift(start), 1))
             ][, c('newstart', 'newend') := .(min(start), max(end)), .(id, grp)][, grp := NULL]
    

    【讨论】:

      猜你喜欢
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-27
      • 2016-09-10
      • 1970-01-01
      • 2021-06-01
      • 1970-01-01
      • 2023-04-10
      相关资源
      最近更新 更多