以相等间隔对数据帧中的组中的数据进行分箱的更好方法

时间:2019-01-16 14:26:25

标签: r dataframe binning

我有一个数据帧,其特征是具有许多不同的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函数并设法在数据框中对其进行了修改-甚至对于我作为新用户来说,我也知道它并不漂亮

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

最终,我希望间隔窗口和垃圾箱数量是任意的-如果我的跨度为5000小时,并且我想在1小时内进行垃圾箱采样。为此,我将breaks=seq(0,5000,1)用作bins

这也将应用于非常大的数据帧,因此在某种程度上需要计算速度。

dplyr解决方案将是一个不错的选择,因为我正在按组应用分箱。

我的猜测是labels = as.character(seq(1,5000,1))cut之间可能存在很好的交互,以生成所需的输出。

谢谢。

更新

经过测试,我发现即使我当前的实现也不像我说的那样:

split

我知道

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()

在bin序列中没有出现的地方,我应该在duration列中将其设为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 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 

2 个答案:

答案 0 :(得分:1)

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

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  

答案 1 :(得分:1)

我们可以进行以下更改:

  • test$cummulative_time可以简单地是cumulative_time
  • 可以分解出
  • breaks,然后如图所示在cut中使用
  • 第二个mutate可以更改为summarize,在这种情况下,不需要selectdistinct
  • 最好用匹配的group_by关闭任何ungroup
  • 添加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)) %>%
  ungroup %>%
  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