按相等间隔将数据框中的一组数据装箱的更好方法

Better way of binning data in a group in a data frame by equal intervals

我有一个数据框,其特征在于许多不同的 ID。对于每个 ID 都有多个事件,这些事件的特征是事件之间的累积持续时间(小时)和该事件的持续时间(秒)。所以,它看起来像:

Id <- c(1,1,1,1,1,1,2,2,2,2,2)
cumulative_time<-c(0,3.58,8.88,11.19,21.86,29.54,0,5,14,19,23)
duration<-c(188,124,706,53,669,1506.2,335,349,395,385,175)
test = data.frame(Id,cumulative_time,duration)

> test
   Id cummulative_time duration
1   1             0.00    188.0
2   1             3.58    124.0
3   1             8.88    706.0
4   1            11.19     53.0
5   1            21.86    669.0
6   1            29.54   1506.2
7   2             0.00    335.0
8   2             5.00    349.0
9   2            14.00    395.0
10  2            19.00    385.0
11  2            23.00    175.0

我想按 ID 分组,然后按每 10 小时的累积量抽样重组组,并在这 10 小时内按 10 小时间隔内发生的持续时间求和。我想要的垃圾箱数量应该是从 0 到 30 小时。因此将是 3 个箱子。

我查看了 cut 函数并设法在数据框中对其进行了破解 - 即使我作为新的 r 用户也知道它并不漂亮

test_cut = test %>% 
  mutate(bin_durations = cut(test$cummulative_time,breaks = c(0,10,20,30),labels = c("10","20","30"),include.lowest = TRUE)) %>% 
  group_by(Id,bin_durations) %>% 
  mutate(total_duration = sum(duration)) %>% 
  select(Id,bin_durations,total_duration) %>% 
  distinct()

给出输出:

test_cut 
  Id time_bins duration
1  1        10   1018.0
2  1        20     53.0
3  1        30   2175.2
4  2        10    684.0
5  2        20    780.0
6  2        30    175.0

最终我希望间隔 window 和 bin 的数量是任意的——如果我有 5000 小时的跨度并且我想在 1 小时的样本中进行 bin。为此,我会使用 breaks=seq(0,5000,1) 作为 bins 我会说 labels = as.character(seq(1,5000,1))

这也适用于非常大的数据框,因此计算速度有点需要。

dplyr 解决方案会很棒,因为我正在按组应用分箱。

我的猜测是 cutsplit 之间有一个很好的交互来生成所需的输出。

提前致谢。

更新

经过测试,我发现即使是我目前的实现也不是我想要的,就像我说的那样:

n=3
test_cut = test %>% 
  mutate(bin_durations = cut(test$cumulative_time,breaks=seq(0,30,n),labels = as.character(seq(n,30,n)),include.lowest = TRUE)) %>% 
  group_by(Id,bin_durations) %>% 
  mutate(total_duration = sum(duration)) %>% 
  select(Id,bin_durations,total_duration) %>% 
  distinct()

我明白了

test_cut
# A tibble: 11 x 3
# Groups:   Id, bin_durations [11]
      Id bin_durations total_duration
   <dbl> <fct>                  <dbl>
 1     1 3                       188 
 2     1 6                       124 
 3     1 9                       706 
 4     1 12                       53 
 5     1 24                      669 
 6     1 30                     1506.
 7     2 3                       335 
 8     2 6                       349 
 9     2 15                      395 
10     2 21                      385 
11     2 24                      175 

在 bin 序列中没有出现的地方,我应该在持续时间列中得到 0。而不是遗漏。

因此,它应该看起来像:

test_cut
# A tibble: 11 x 3
# Groups:   Id, bin_durations [11]
      Id bin_durations total_duration
   <dbl> <fct>                  <dbl>
 1     1 3                       188 
 2     1 6                       124 
 3     1 9                       706 
 4     1 12                       53 
 5     1 15                        0 
 6     1 18                        0
 7     1 21                        0    
 8     1 24                      669
 9     1 27                        0 
10     1 30                     1506.
11     2 3                       335 
12     2 6                       349
13     2 9                         0
14     2 12                        0  
15     2 15                      395
16     2 18                        0 
17     2 21                      385 
18     2 24                      175
19     2 27                        0
20     2 30                        0 

这是一个整数除法的想法(%/%)

library(tidyverse)

test %>% 
 group_by(Id, grp = cumulative_time %/% 10) %>% 
 summarise(toatal_duration = sum(duration))

这给出了,

# A tibble: 6 x 3
# Groups:   Id [?]
     Id   grp toatal_duration
  <dbl> <dbl>           <dbl>
1     1     0           1018 
2     1     1             53 
3     1     2           2175.
4     2     0            684 
5     2     1            780 
6     2     2            175 

为了解决您更新后的问题,我们可以使用 complete 来添加缺失的行。因此,对于同一个示例,在 3 小时内分箱,

test %>%
     group_by(Id, grp = cumulative_time %/% 3) %>%
     summarise(toatal_duration = sum(duration)) %>%
     ungroup() %>%
     complete(Id, grp = seq(min(grp), max(grp)), fill = list(toatal_duration = 0))

这给出了,

     # A tibble: 20 x 3
      Id   grp toatal_duration
   <dbl> <dbl>           <dbl>
 1     1     0            188 
 2     1     1            124 
 3     1     2            706 
 4     1     3             53 
 5     1     4              0 
 6     1     5              0 
 7     1     6              0 
 8     1     7            669 
 9     1     8              0 
10     1     9           1506.
11     2     0            335 
12     2     1            349 
13     2     2              0 
14     2     3              0 
15     2     4            395 
16     2     5              0 
17     2     6            385 
18     2     7            175 
19     2     8              0 
20     2     9              0  

我们可以进行以下更改:

  • test$cummulative_time 可以简单地 cumulative_time
  • breaks 可以分解出来然后用在 cut 中,如图所示
  • 第二个 mutate 可以更改为 summarize,在这种情况下,不需要 selectdistinct
  • 用匹配的 ungroup 关闭任何 group_by 总是一个好主意,或者在 summarize 的情况下我们可以使用 .groups = "drop")
  • 添加 complete 为不存在的级别插入 0

实施这些更改我们有:

library(dplyr)
library(tidyr)

breaks <- seq(0, 40, 10)
test %>% 
  mutate(bin_durations = cut(cumulative_time, breaks = breaks,
   labels = breaks[-1], include.lowest = TRUE)) %>% 
  group_by(Id,bin_durations) %>% 
  summarize(total_duration = sum(duration), .groups = "drop") %>%
  complete(Id, bin_durations, fill = list(total_duration = 0))

给予:

# A tibble: 8 x 3
     Id bin_durations total_duration
  <dbl> <fct>                  <dbl>
1     1 10                     1018 
2     1 20                       53 
3     1 30                     2175.
4     1 40                        0 
5     2 10                      684 
6     2 20                      780 
7     2 30                      175 
8     2 40                        0