【问题标题】:Group Data in R for consecutive rows在 R 中为连续行分组数据
【发布时间】:2015-09-11 15:53:25
【问题描述】:

如果在 R 中没有快速的 1-3 衬里,我肯定会使用 linux sort 和使用 groupby 的简短 python 程序,所以不要向后弯腰试图让一些疯狂的工作.这是输入数据框:

df_in <- data.frame(
  ID = c(1,1,1,1,1,2,2,2,2,2),
  weight = c(150,150,151,150,150,170,170,170,171,171),
  start_day = c(1,4,7,10,11,5,10,15,20,25),
  end_day = c(4,7,10,11,30,10,15,20,25,30)
)
   ID weight start_day end_day
1   1    150         1       4
2   1    150         4       7
3   1    151         7      10
4   1    150        10      11
5   1    150        11      30
6   2    170         5      10
7   2    170        10      15
8   2    170        15      20
9   2    171        20      25
10  2    171        25      30

我想通过IDweight 进行一些基本聚合,但前提是该组位于df_in 的连续行中。具体来说,期望的输出是

df_desired_out <- data.frame(
  ID = c(1,1,1,2,2),
  weight = c(150,151,150,170,171),
  min_day = c(1,7,10,5,20),
  max_day = c(7,10,30,20,30)
)
  ID weight min_day max_day
1  1    150       1       7
2  1    151       7      10
3  1    150      10      30
4  2    170       5      20
5  2    171      20      30

这个question 似乎与我想要的非常接近,但由于某种原因我在调整它时遇到了很多麻烦。

【问题讨论】:

    标签: r


    【解决方案1】:

    dplyr 中,我会通过为连续行创建另一个分组变量来做到这一点。这就是代码cumsum(c(1, diff(weight) != 0) 在下面的代码块中所做的事情。 here 也是一个例子。

    组创建可以在group_by 内完成,然后您可以相应地按组进行任何总结。

    library(dplyr)
    
    df_in %>%
        group_by(ID, group_weight = cumsum(c(1, diff(weight) != 0)), weight) %>%
        summarise(start_day = min(start_day), end_day = max(end_day))
    
    Source: local data frame [5 x 5]
    Groups: ID, group_weight [?]
    
         ID group_weight weight start_day end_day
      (dbl)        (dbl)  (dbl)     (dbl)   (dbl)
    1     1            1    150         1       7
    2     1            2    151         7      10
    3     1            3    150        10      30
    4     2            4    170         5      20
    5     2            5    171        20      30
    

    这种方法确实会在数据集中为您留下额外的分组变量,如果需要,可以在取消分组后使用select(-group_weight) 将其删除。

    【讨论】:

      【解决方案2】:

      首先我们结合IDweight。快速而肮脏的方法是使用粘贴:

      df_in$id_weight <- paste(df_in$id, df_in$weight, sep='_')
      df_in
         ID weight start_day end_day id_weight
      1   1    150         1       4     1_150
      2   1    150         4       7     1_150
      3   1    151         7      10     1_151
      4   1    150        10      11     1_150
      5   1    150        11      30     1_150
      6   2    170         5      10     2_170
      7   2    170        10      15     2_170
      8   2    170        15      20     2_170
      9   2    171        20      25     2_171
      10  2    171        25      30     2_171
      

      更安全的方法是使用interactiongroup_indices: Combine values in 4 columns to a single unique value

      我们可以使用rle 连续分组。

      rlel <- rle(df_in$id_weight)$lengths
      df_in$group <- unlist(lapply(1:length(rlel), function(i) rep(i, rlel[i])))
      df_in
         ID weight start_day end_day id_weight group
      1   1    150         1       4     1_150     1
      2   1    150         4       7     1_150     1
      3   1    151         7      10     1_151     2
      4   1    150        10      11     1_150     3
      5   1    150        11      30     1_150     3
      6   2    170         5      10     2_170     4
      7   2    170        10      15     2_170     4
      8   2    170        15      20     2_170     4
      9   2    171        20      25     2_171     5
      10  2    171        25      30     2_171     5
      

      现在有了方便的组号,我们可以按组进行汇总。

      df_in %>% 
        group_by(group) %>% 
        summarize(id_weight = id_weight[1], 
                  start_day = min(start_day), 
                  end_day = max(end_day))
      # A tibble: 5 x 4
        group id_weight start_day end_day
        <int> <chr>         <dbl>   <dbl>
      1     1 1_150             1       7
      2     2 1_151             7      10
      3     3 1_150            10      30
      4     4 2_170             5      20
      5     5 2_171            20      30
      

      【讨论】:

        【解决方案3】:
        with(df_in, {
          aggregate(day, list('ID'=ID, 'weight'=weight), 
                    function(x) c('min_day' = min(x), 'max_day' = max(x)))
        })
        

        生产:

          ID weight x.min_day x.max_day
        1  1    150         1         5
        2  1    151         3         3
        3  2    170         1         3
        4  2    171         4         5
        

        【讨论】:

        • 谢谢,但看起来这会产生与问题中包含的不正确 data.table 解决方案相同的输出。我希望它只对原始数据框中连续的行进行分组。所需的输出在问题中,ID=1 有 3 行。
        • @user673173 我认为您应该更清楚地编辑您的问题描述。我不相信 aosmith 使用 cumsum(c(1, diff(weight) != 0) 的解决方案会得到你想要的,尽管它似乎在这种情况下有效。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-18
        • 1970-01-01
        • 2021-12-09
        • 1970-01-01
        相关资源
        最近更新 更多