Использование dplyr: в группах выберите первое значение, соответствующее условию - PullRequest
1 голос
/ 17 января 2020

Мне нужна помощь в получении решения, которое отсканирует назад и получит первое значение, соответствующее условию. У меня есть данные, похожие на:

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

Для последнего члена каждой группы, определенной в порядке времени на 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

Ответы [ 2 ]

3 голосов
/ 17 января 2020

Использование 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),]
0 голосов
/ 17 января 2020

Мы можем изменить значение x на id, получив первое число меньше 5, используя which. Последний replace должен присвоить NA всем значениям в previous.less.5, кроме последнего.

library(dplyr)

df %>%
  #Data is already sorted by `id` and `time.var` but if your still need use
  #arrange(id, time.var) %>%
  group_by(id) %>%
  mutate(rev_x = c(NA, rev(x)[-1]), previous.less.5 = rev_x[which(rev_x < 5)[1]], 
         previous.less.5 = replace(previous.less.5, row_number() != n(), NA)) %>%
  select(-rev_x)

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

Это также должно обрабатывать регистр и возвращать NA, если в id нет значения меньше 5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...