使用 dplyr 过滤窗口:找到匹配行,并保留后续 N 行

Ale*_*lex 5 r dplyr

我有一个数据框,我想过滤掉符合某些条件的行,以及它后面的 N 行。例如,考虑一个包含小时和分钟列(表示每行的时间戳)的数据框。假设我想要第 0 小时和第 6 小时之后的前两条记录。有可能以一种好的方式做到这一点吗?

set.seed(3)
df <- 
    data.frame(hour = 0:11, minutes = runif(12, 0, 59), count = rpois(12, 3)) %>%
    arrange(hour, minutes)
Run Code Online (Sandbox Code Playgroud)

产生

> df
   hour   minutes count
1     0  9.914450     3
2     1 47.643468     3
3     2 22.711599     5
4     3 19.336325     5
5     4 35.523940     1
6     5 35.659249     4
7     6  7.353373     5
8     7 17.381455     2
9     8 34.078985     2
10    9 37.227777     0
11   10 30.208938     1
12   11 29.796411     1
Run Code Online (Sandbox Code Playgroud)

普通过滤器返回两行:

> df %>%
+     filter(hour%%6 == 0)
  hour  minutes count
1    0 9.914450     3
2    6 7.353373     5
Run Code Online (Sandbox Code Playgroud)

然而,答案应该是:

  hour   minutes count
1    0  9.914450     3
2    1 47.643468     3
3    6  7.353373     5
4    7 17.381455     2
Run Code Online (Sandbox Code Playgroud)

在这种情况下,可以对用于过滤的列使用模算术,但在一般情况下这可能是不可能的。

下面提供了原始示例,在这里我想要每小时的前两条记录。在这种情况下,Akrun 的答案很好,并且利用了数据中的组结构。例如

library(dplyr)
set.seed(0)
df <- 
    data.frame(hour = rep(0:11, 3), minutes = runif(36, 0, 59), count = rpois(36, 3)) %>%
    arrange(hour, minutes)
Run Code Online (Sandbox Code Playgroud)

好像:

   hour    minutes count
1     0  7.4077507     2
2     0 10.4168484     3
3     0 52.9051348     4
4     1 15.6650111     4
5     1 15.7660195     5
6     1 40.5343480     4
7     2 21.9553101     1
8     2 22.6621194     4
9     2 22.7807315     2
10    3  0.7900297     3
11    3 33.7983484     4
12    3 45.4206438     3
...
Run Code Online (Sandbox Code Playgroud)

一个人可以做

df %>% mutate(is_even_hour = ifelse(hour %% 2 == 0, 1, 0)) %>%
    filter(is_even_hour == 1) %>%
    group_by(hour, is_even_hour) %>%
    filter(row_number() <= 2) %>%
    ungroup %>%
    select(-is_even_hour)
Run Code Online (Sandbox Code Playgroud)

这使

hour   minutes count
   <int>     <dbl> <int>
1      0  7.407751     2
2      0 10.416848     3
3      2 21.955310     1
4      2 22.662119     4
5      4 22.560889     2
6      4 29.364255     5
7      6 20.080591     2
8      6 53.004991     3
9      8 35.374384     4
10     8 38.987070     3
11    10  3.645390     4
12    10 10.986838     5
Run Code Online (Sandbox Code Playgroud)

akr*_*run 2

按“小时”分组后,我们可以一步完成此filter操作

df %>%
     group_by(hour) %>%
     filter(!hour%%2 & row_number() <3)
#     hour   minutes count
#    <int>     <dbl> <int>
#1      0  7.407751     2
#2      0 10.416848     3
#3      2 21.955310     1
#4      2 22.662119     4
#5      4 22.560889     2
#6      4 29.364255     5
#7      6 20.080591     2
#8      6 53.004991     3
#9      8 35.374384     4
#10     8 38.987070     3
#11    10  3.645390     4
#12    10 10.986838     5
Run Code Online (Sandbox Code Playgroud)

对于更新的帖子

i1 <- df %>% 
          filter(hour%%6 == 0) %>%
          .$hour %>% 
          rep(., each =2)+ 0:1 %>% 
          match(., df$hour) 
df[i1,]
#   hour   minutes count
#1    0  9.914450     3
#2    1 47.643468     3
#7    6  7.353373     5
#8    7 17.381455     2
Run Code Online (Sandbox Code Playgroud)

或者这可以通过以下方式以紧凑的方式完成data.table

library(data.table)
setDT(df)[df[, rep(which(!hour%%6), each = 2) + 0:1 ]]
#   hour   minutes count
#1:    0  9.914450     3
#2:    1 47.643468     3
#3:    6  7.353373     5
#4:    7 17.381455     2
Run Code Online (Sandbox Code Playgroud)