【问题标题】:R: how to group by nested intervals?R:如何按嵌套间隔分组?
【发布时间】:2021-04-02 22:01:18
【问题描述】:

我有一个如下所示的数据框:

      d   b c  a
1  3400 100 3 -1
2  3400  50 3  1
3  3400 100 1 -1
4  3408  50 1  1
5  3412 100 3  1
6  3423  50 1  1
7  3434 100 1  1
8  3436 100 3  1
9  3438  50 3  1
10 3445  50 1  1
11 3454 100 3  1
12 3465 100 1  1

并且我想根据组从列 c 值 = 3 开始并且如果列 d 值在第一个组条目之前 + 30 则组结束的条件按列 a 和 b 进行分组(所以间隔长度= 30,但每个区间的起点可以在另一个区间)。然后我想统计每组的行数。

所以这个样本的预期输出应该是:

b    a   rowcount 
100 -1    2        ( starting at d = 3400)
50   1    3        ( starting at d = 3400)
100  1    3        (starting at d= 3412)
50   1    2        (starting at d= 3438)
100  1    2        (starting at d= 3454)

我试过了:

df<-df%>%
  group_by(b,a,first(c) == 3  & lead(d) - d < 30)
summarise(number = n())

但这并没有给我想要的输出。任何 cmet 都表示赞赏!

更新:新示例:

      d   b c a
1  3400 100 3 1
2  3400 100 3 1
3  3400 100 1 1
4  3408 100 1 1
5  3412 100 3 1
6  3434 100 3 1
7  3436 100 1 1
8  3438 100 3 1
9  3445 100 1 1
10 3443 100 3 1
11 3444 100 1 1
12 3463 100 3 1
13 3463 100 1 1
14 3463 100 3 1

您的代码作为输出给出:

      a     b count desc                    addition_info                    
  <dbl> <dbl> <int> <chr>                   <chr>                            
1     1   100     5 ( starting at d = 3400) There is 3 `c == 3` in this group
2     1   100     6 ( starting at d = 3434) There is 3 `c == 3` in this group
3     1   100     3 ( starting at d = 3463) There is 2 `c == 3` in this group

但是第三组是错误的,因为 d = 29 的差异,因此

      a     b count desc                    addition_info                    
  <dbl> <dbl> <int> <chr>                   <chr>                            
1     1   100     5 ( starting at d = 3400) There is 3 `c == 3` in this group
2     1   100     9 ( starting at d = 3434) There is 3 `c == 3` in this group

【问题讨论】:

  • 从第 1 行开始,c == 3 所以我开始了一个小组。查看具有相同 b (100) 但 d
  • 因为前两行 b(100) 行(第 1 行和第 3 行)的 a == -1 列和第 5 行的 a ==1 列,所以这将启动一个新组跨度>
  • 对于最后一个 a =1; b = 100starting at d = 3454 第二条记录有 d = 3645 所以 d 差异大于 30 并且仍然在同一组中?
  • 很抱歉这是我的错误,我已更正。感谢您的解决方案,是否有可能包含 d
  • 如果有c == 1d &gt; 30 的记录(或几条)那么会发生什么。它(或他们)会是它自己的组还是将包含在下一个c == 3 中?那些c != 3之后会发生什么

标签: r dplyr group-by conditional-statements


【解决方案1】:

稍微调整了您的示例,但我认为这在很大程度上就足够了。 (另见代码下方的注释)

df <- read.table(text = "     d   b c a
1  3400 100 3 1
2  3400 100 3 1
3  3400 100 1 1
4  3408 100 1 1
5  3412 100 3 1
6  3434 100 3 1
7  3436 100 1 1
8  3438 100 3 1
9  3445 100 1 1
10 3443 100 3 1
11 3444 100 1 1
12 3463 100 3 1
13 3463 100 1 1
14 3463 100 3 1
15 3465 100 3 1", header = T)

#added one row in df
> df
      d   b c a
1  3400 100 3 1
2  3400 100 3 1
3  3400 100 1 1
4  3408 100 1 1
5  3412 100 3 1
6  3434 100 3 1
7  3436 100 1 1
8  3438 100 3 1
9  3445 100 1 1
10 3443 100 3 1
11 3444 100 1 1
12 3463 100 3 1
13 3463 100 1 1
14 3463 100 3 1
15 3465 100 3 1

现在遵循这个策略

library(tidyverse)
library(data.table) # for rleid()


df %>% mutate(r = row_number()) %>%
  group_by(b, a) %>% mutate(grp_no = rleid(accumulate(d, ~ifelse(.y - .x > 30, .y, .x)))) %>%
  group_by(b, a, grp_no) %>%
  summarise(row_count = n(), r = first(r), d = first(d)) %>%
  arrange(r) %>%
  mutate(additional = paste("group starts at d =", d)) %>%
  select(-r, -d)

# A tibble: 3 x 5
# Groups:   b, a [1]
      b     a grp_no row_count additional              
  <int> <int>  <int>     <int> <chr>                   
1   100     1      1         5 group starts at d = 3400
2   100     1      2         9 group starts at d = 3434
3   100     1      3         1 group starts at d = 3465

对于第一个例子,它的输出是

# A tibble: 5 x 5
# Groups:   b, a [3]
      b     a grp_no row_count additional              
  <int> <int>  <int>     <int> <chr>                   
1   100    -1      1         2 group starts at d = 3400
2    50     1      1         3 group starts at d = 3400
3   100     1      1         3 group starts at d = 3412
4    50     1      2         2 group starts at d = 3438
5   100     1      2         2 group starts at d = 3454

注意:你也可以在上面的语法中使用dplyr::dense_rank而不是rleid,像这样

df %>% mutate(r = row_number()) %>%
  group_by(b, a) %>% 
  mutate(grp_no = dense_rank(accumulate(d, ~ifelse(.y - .x > 30, .y, .x)) )) %>%
  group_by(b, a, grp_no) %>%
  summarise(row_count = n(), r = first(r), d = first(d)) %>%
  arrange(r) %>%
  mutate(additional = paste("group starts at d =", d)) %>%
  select(-r, -d)

尾注:现在我不是你c==3 的逻辑怎么适合这个?如果你能澄清我可以再试一次

【讨论】:

  • 一个问题:如何获取第一个和最后一个 d 条目?我在 summarise 参数中使用 dlast= last(d) 进行了尝试,但我得到了与 first() 相同的 d 值
【解决方案2】:
library(dplyr, warn.conflicts = FALSE)
library(purrr)
options(scipen = 999)
data <- structure(list(d = c(3400L, 3400L, 3400L, 3408L, 3412L, 3423L, 
  3434L, 3436L, 3438L, 3445L, 3454L, 3645L), b = c(100L, 50L, 100L, 
    50L, 100L, 50L, 100L, 100L, 50L, 50L, 100L, 100L), c = c(3L, 
      3L, 1L, 1L, 3L, 1L, 1L, 3L, 3L, 1L, 3L, 1L), a = c(-1L, 1L, -1L, 
        1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)), row.names = c(NA, -12L), class = "data.frame")



# split data into group of a,b
new_data <- data %>% 
  group_by(a, b) %>%
  group_split()

# function group index - assuming that every c == 1 have a c == 3 before it
# then only need to sort by the d different with first record by less than 30
group_function <- function(df) {
  bin <- seq(from = min(df$d), to = max(df$d) + 30, by = 30)
  df <- df %>%
    mutate(d_group = cut(d, breaks = bin,
                         include.lowest = TRUE, right = FALSE)) %>%
    group_by(d_group)
  df$group_index <- group_indices(df)
  df %>% 
    group_by(a, b, group_index) %>%
    summarize(count = n(),
      desc = sprintf("( starting at d = %s)", first(d)),
      # I added the count of c==3 in the group just to show that sample data
      # is not follow the logic you mentioned
      addition_info = paste0("There is ",
                             sum(c == 3), " `c == 3` in this group"),
      .groups = "drop") %>%
    select(-group_index)
}
new_data %>%
  map_dfr(group_function)
#> # A tibble: 6 x 5
#>       a     b count desc                    addition_info                     
#>   <int> <int> <int> <chr>                   <chr>                             
#> 1    -1   100     2 ( starting at d = 3400) There is 1 `c == 3`` in this group
#> 2     1    50     3 ( starting at d = 3400) There is 1 `c == 3`` in this group
#> 3     1    50     2 ( starting at d = 3438) There is 1 `c == 3`` in this group
#> 4     1   100     3 ( starting at d = 3412) There is 2 `c == 3`` in this group
#> 5     1   100     1 ( starting at d = 3454) There is 1 `c == 3`` in this group
#> 6     1   100     1 ( starting at d = 3645) There is 0 `c == 3`` in this group

reprex package (v1.0.0) 于 2021-04-04 创建

更新:包括所描述的逻辑。

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 2010-09-16
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2019-06-28
    • 2018-10-12
    相关资源
    最近更新 更多