代码之家  ›  专栏  ›  技术社区  ›  bziggy

r data.table仅在每组至少有一个递增的obs时调整最小和最大年份

  •  0
  • bziggy  · 技术社区  · 4 年前

    我有一个数据集,其中包含id、位置、开始年份、结束年份、年龄1和年龄2。对于定义为id、位置、年龄1和年龄2的每个组,我想创建新的开始和结束年份。例如,我可能有三个关于中国的条目,涵盖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数量。我想我需要某种循环。

    有没有更有效的方法来实现这一点?

    2 回复  |  直到 4 年前
        1
  •  1
  •   r2evans    4 年前

    data.table

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

    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(...) (所有分组变量),这将给出所有可能的组合,而不仅仅是数据中观察到的组合。因此,the FUN 可以在零数据的情况下调用action。在这种情况下,它确实发出了警告。人们可以用以下方式抑制这种情况 function(a) suppressWarnings(min(a)) 而不是仅仅 min .

        2
  •  1
  •   akrun    4 年前

    我们可以用 dplyr 。按“id”分组后,取“start”和 lag 对于“开始”,应用 rleid 获取运行长度id并创建“newstart”、“newend”作为 min max 关于“开始”

    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]