首页 > 解决方案 > 使用 dplyr:在组内,选择第一个满足条件的值

问题描述

我需要帮助获得一个解决方案,该解决方案将及时向后扫描并获得满足条件的第一个值。我有类似的数据:

set.seed(42)

df <- data.frame(
  id = sample(LETTERS[1:3], 20, replace = TRUE),
  time.var = sample(1:20, 20, replace = TRUE),
  x = sample(c(1:10), 20, replace = TRUE)
  )

df <- df[order(df$id, df$time.var),]

 id time.var  x
  A        5  2
  A       14  8
  A       19  7
  A       20  1
  B        1  1
  B        2  5
  B        9 10
  B       11 10
  B       13  6
  B       15  4
  B       19  3
  C        1  7
  C        3  5
  C        8  9
  C        8  4
  C       17  7
  C       17  4
  C       17  8
  C       19  4
  C       19 10

对于按时间顺序定义的每个组的最后一个成员 by time.var,我想x通过按时间降序扫描来获得小于 5 的第一个值。

我试过了:

test <- df %>% 
        group_by(id) %>% 
        arrange(id, time.var) %>% 
        mutate(less.5 = which.max(x[x < 5]) )

我可以使用什么策略来获得这种类型的输出:

 id time.var  x  previous.less.5
  A        5  2
  A       14  8
  A       19  7
  A       20  1      2
  B        1  1
  B        2  5
  B        9 10
  B       11 10
  B       13  6
  B       15  4
  B       19  3      4
  C        1  7
  C        3  5
  C        8  9
  C        8  4
  C       17  7
  C       17  4
  C       17  8
  C       19  4
  C       19 10      4

标签: rdataframedplyr

解决方案


使用library(dplyr)

df %>% 
  arrange(id, time.var) %>% 
  group_by(id) %>% 
  mutate(previous.less.5 = tail(c(x[c((x[-n()] < 5), FALSE)]),1)) %>% 
  group_by(id) %>% 
  mutate(previous.less.5 = if_else(row_number() == n(), previous.less.5, NULL))

或者

df %>%
  arrange(id, time.var) %>% 
  group_by(id) %>%   
  slice(1:(n()-1)) %>% 
  filter(x < 5) %>% 
  slice(n()) %>% 
  select(-time.var) %>% 
  right_join(df, ., by="id", suffix =c("",".y")) %>% 
  group_by(id) %>% 
  mutate(previous.less.5 = if_else(row_number() == n(), x.y, NULL)) %>%
  select(-x.y)

给予:

#> # A tibble: 20 x 4
#> # Groups:   id [3]
#>    id    time.var     x previous.less.5
#>    <fct>    <int> <int>           <int>
#>  1 A            3    10              NA
#>  2 A            4     8              NA
#>  3 A            4     6              NA
#>  4 A            5     2              NA
#>  5 A            5     8              NA
#>  6 A            5     7              NA
#>  7 A           11     6              NA
#>  8 A           13     3              NA
#>  9 A           15     2               3
#> 10 B            2     1              NA
#> 11 B            4     3              NA
#> 12 B            4     6              NA
#> 13 B            8     5              NA
#> 14 B            8     4              NA
#> 15 B           20     7               4
#> 16 C            1     2              NA
#> 17 C            2    10              NA
#> 18 C           10     6              NA
#> 19 C           13     2              NA
#> 20 C           18     5               2

更新:

如果有一个组的记录少于 5(或最后一条记录少于 5),则以下工作:

df %>% 
  arrange(id, time.var) %>% 
  group_by(id) %>% 
  mutate(previous.less.5 = if_else(row_number() == n(), 
                                   max(tail(c( x[ c( x[-n()] < 5, FALSE) ] ), 1)), 
                                   NULL)) %>% 
  mutate(previous.less.5 = replace(previous.less.5, is.infinite(previous.less.5), NA))

数据:

set.seed(42) # I am getting different data than what you've shown with this seed

df <- data.frame(
  id = sample(LETTERS[1:3], 20, replace = TRUE),
  time.var = sample(1:20, 20, replace = TRUE),
  x = sample(c(1:10), 20, replace = TRUE)
)

df <- df[order(df$id, df$time.var),]

推荐阅读