【问题标题】:Create a block column based on id and the value of another column in R根据 id 和 R 中另一列的值创建一个块列
【发布时间】:2014-10-15 01:58:09
【问题描述】:

鉴于以下前两列(id 和 time_diff),我想生成“块”列

test
   id time_diff   block
1   a        NA       1
2   a         1       1
3   a         1       1
4   a         1       1
5   a         3       1
6   a         3       1
7   b        NA       2
8   b        11       3
9   b         1       3
10  b         1       3
11  b         1       3
12  b        12       4
13  b         1       4
14  c        NA       5
15  c         4       5
16  c         7       5

数据已经按id和时间排序。在给定相同 id 的情况下,time_diff 是根据前一时间与该行的时间值的差值计算的。我想创建一个块 id,它是一个自动增量值,当遇到具有相同 id 的新 ID 或 >10 的 time_diff 时会增加。

如何在 R 中实现这一点?

【问题讨论】:

    标签: r


    【解决方案1】:

    将您的数据作为数据框导入,例如:

    df = read.table(text='
       id time_diff   block
    1   a        NA       1
    2   a         1       1
    3   a         1       1
    4   a         1       1
    5   a         3       1
    6   a         3       1
    7   b        NA       2
    8   b        11       3
    9   b         1       3
    10  b         1       3
    11  b         1       3
    12  b        12       4
    13  b         1       4
    14  c        NA       5
    15  c         4       5
    16  c         7       5')
    

    你可以做一个这样的单行来获得满足你的两个条件的事件:

    > new_col = as.vector(cumsum(
         na.exclude(
           c(F,diff(as.numeric(as.factor(df$id)))) |     # change of id OR
           df$time_diff > 10                             # time_diff greater than 10
         )
      ))
    > new_col
     [1] 0 0 0 0 0 1 2 2 2 2 3 3 4 4 4
    

    最后使用cbind 将此新列附加到您的数据框:

    > cbind(df, block = c(0,new_col))
       id time_diff block block
    1   a        NA     1     0
    2   a         1     1     0
    3   a         1     1     0
    4   a         1     1     0
    5   a         3     1     0
    6   a         3     1     0
    7   b        NA     2     1
    8   b        11     3     2
    9   b         1     3     2
    10  b         1     3     2
    11  b         1     3     2
    12  b        12     4     3
    13  b         1     4     3
    14  c        NA     5     4
    15  c         4     5     4
    16  c         7     5     4
    

    你会注意到你想要的 block 变量和我的变量之间存在偏移:纠正它很容易,可以在几个不同的步骤中完成,我将把它留给你 :)

    【讨论】:

    • 和akrun一样,我从你的回答中得到灵感。 | in cumsum 是我的教训。谢谢你。 +1
    【解决方案2】:

    @Jealie 方法的另一种变体是:

    with(test, cumsum(c(TRUE,id[-1]!=id[-nrow(test)])|time_diff>10))
    #[1] 1 1 1 1 1 1 2 3 3 3 3 4 4 5 5 5
    

    【讨论】:

    • upvoted :) 直接比较(id[-1]!=id[-nrow(test)])解决了处理NA的繁琐问题
    • @Jealie 也感谢 +1 的启发。
    • 我无法立即在cumsum 中找到|。现在我看到我的代码可以更短。谢谢你的课,像往常一样。 +1
    【解决方案3】:

    在向 Jealie 和 akrun 学习后,我想出了这个想法。

    mydf %>%
        mutate(group = cumsum(time_diff > 10 |!duplicated(id)))
    
    #   id time_diff block group
    #1   a        NA     1     1
    #2   a         1     1     1
    #3   a         1     1     1
    #4   a         1     1     1
    #5   a         3     1     1
    #6   a         3     1     1
    #7   b        NA     2     2
    #8   b        11     3     3
    #9   b         1     3     3
    #10  b         1     3     3
    #11  b         1     3     3
    #12  b        12     4     4
    #13  b         1     4     4
    #14  c        NA     5     5
    #15  c         4     5     5
    #16  c         7     5     5
    

    【讨论】:

    • @akrun 谢谢。你玩过 0.3 的新功能吗?正如 aosmith 建议的那样,我正在查看 github 页面。
    • 是的,我已经安装了dplyr 0.3。我找不到aosmith的评论。 renamecount等是新版本中的一些选项。
    • This 是建议的链接。你可能已经看过了。我今天早上在 R Bloggers 上找到了this post。后者有一些样本。我会尽快完成这些。
    • @akrun 好的。我会研究两者并赶上。
    【解决方案4】:

    这是一种使用dplyr的方法:

    require(dplyr)
    
    set.seed(999)
    test <- data.frame(
      id = rep(letters[1:4], each = 3),
      time_diff = sample(4:15)
    )
    
    
    test %>%
      mutate(
        b = as.integer(id) - lag(as.integer(id)),
        more10 = time_diff > 10,
        increment = pmax(b, more10, na.rm = TRUE),
        increment = ifelse(row_number() == 1, 1, increment),
        block = cumsum(increment)
      ) %>%
      select(id, time_diff, block)
    

    【讨论】:

      【解决方案5】:

      试试:

      > df
         id time_diff
      1   a        NA
      2   a         1
      3   a         1
      4   a         1
      5   a         3
      6   a         3
      7   b        NA
      8   b        11
      9   b         1
      10  b         1
      11  b         1
      12  b        12
      13  b         1
      14  c        NA
      15  c         4
      16  c         7
      
      block= c(1)
      for(i in 2:nrow(df))
          block[i] = ifelse(df$time_diff[i]>10 || df$id[i]!=df$id[i-1], 
                        block[i-1]+1,
                        block[i-1])
      df$block = block
      
      df
         id time_diff block
      1   a        NA     1
      2   a         1     1
      3   a         1     1
      4   a         1     1
      5   a         3     1
      6   a         3     1
      7   b        NA     2
      8   b        11     3
      9   b         1     3
      10  b         1     3
      11  b         1     3
      12  b        12     4
      13  b         1     4
      14  c        NA     5
      15  c         4     5
      16  c         7     5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        相关资源
        最近更新 更多