Удалите все строки после последнего вхождения значения в группу

#r #dplyr

Вопрос:

У меня есть большой набор данных (500 000 строк) с идентификатором и днями недели (1-7), и я хочу удалить все строки после окончательного вхождения числа 7 дня недели для всех идентификаторов.

Вот упрощенный пример.

Набор данных:

 df <- structure(list(A = structure(c(1L, 1L, 1L, 1L,1L,1L,1L,1L,1L,1L,1L,1L,1L, 2L, 2L, 2L, 2L, 2L,2L,2L,2L,2L, 3L, 3L, 3L,3L,3L,3L), 
                                   .Label = c("a", "b", "c"), class = "factor"),
                     B = c(1L, 7L, 1L, 1L, 2L, 7L, 1L, 2L, 3L, 1L, 2L, 7L,1L, 7L, 1L, 1L, 2L, 7L, 1L, 2L, 3L, 1L, 2L, 7L,1L, 1L, 2L, 7L)), 
                .Names = c("ID","Weekday"), 
                class = "data.frame", 
                row.names = c(NA, -28L))
 

Этот код отлично работает с этим упрощенным набором данных:

 df.out<-df %>% 
  group_by(ID)%>%
  dplyr::slice(1:max(which(Weekday == "7")))
 

Но когда я пробую это на своем большом наборе данных, я получаю следующую ошибку:

 Error in 1:max(which(weekday == "7")) : result would be too long a vector
In addition: Warning message:
In max(which(weekday == "7")) :
  no non-missing arguments to max; returning -Inf
 

Я пробовал использовать день недели как числовой, множитель и целое число, но безуспешно.

Похоже, я не могу воспроизвести эту ошибку в упрощенном наборе данных, подходящем для этой платформы, поэтому я не уверен, как ее продемонстрировать дальше, но буду признателен за любую помощь в том, как обойти эту ошибку!

Я использовал аналогичный код для большого набора данных, чтобы удалить все строки до первого появления без каких-либо проблем. Например.

 df.out<-df %>% 
 group_by(ID)%>%
 dplyr::slice(which.max(weekday == "1") : n())
 

Комментарии:

1. Попробуйте этот пример, проблема which в том, что у него нет совпадения: max(which(mtcars$cyl == 11))

Ответ №1:

Ошибка может быть воспроизведена, если у вас есть ID ошибка, в которой нет 7.

 library(dplyr)

df <- rbind(df, data.frame(ID = 'd', Weekday = 1))

df %>% 
    group_by(ID)%>%
    slice(1:max(which(Weekday == "7")))
 

Ошибка в 1:макс(который(День недели == «7»)) : результат будет слишком длинным вектором

Решением было бы проверить, есть ли у вас хотя бы одно вхождение из 7.

 df %>% 
  group_by(ID)%>%
  slice(if(any(Weekday == 7)) 1:max(which(Weekday == 7)) else 0) %>%
  ungroup

#   ID    Weekday
#   <fct>   <dbl>
# 1 a           1
# 2 a           7
# 3 a           1
# 4 a           1
# 5 a           2
# 6 a           7
# 7 a           1
# 8 a           2
# 9 a           3
#10 a           1
# … with 13 more rows
 

Комментарии:

1. В slice случае, если вы вернете 0, он не выберет ни одной строки. (См mtcars %>% slice(0) .). Так что это отбросит те ID , в которых нет 7.

2. Извините, я не хотел удалять свой комментарий. Но спасибо, в этом есть смысл. Если бы я хотел, чтобы он нашел последнее вхождение 6, если нет 7, сработало бы это? df %>% group_by(ИДЕНТИФИКАТОР)%>>% срез(если(любой(Будний день == 7 )) 1:макс(какой(Будний день == 7)) еще если(любой(Будний день == 6 )) 1:макс(какой(Будний день == 6)))%>>>% разгруппировать

3. Да, и за этим следует, else 0 если 6 нет.

4. Отлично. Огромное спасибо!

Ответ №2:

Мы также можем сделать

 library(dplyr)
df %>% 
  group_by(ID)%>%
  slice(seq_len(max(c(0, which(Weekday == 7)[-1])))) %>%
  ungroup
 

-выход

 # A tibble: 23 x 2
   ID    Weekday
   <fct>   <dbl>
 1 a           1
 2 a           7
 3 a           1
 4 a           1
 5 a           2
 6 a           7
 7 a           1
 8 a           2
 9 a           3
10 a           1
# … with 13 more rows
 

данные

 df <- structure(list(ID = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 
3L, 3L, 3L, 3L, 4L), .Label = c("a", "b", "c", "d"), class = "factor"), 
    Weekday = c(1, 7, 1, 1, 2, 7, 1, 2, 3, 1, 2, 7, 1, 7, 1, 
    1, 2, 7, 1, 2, 3, 1, 2, 7, 1, 1, 2, 7, 1)), row.names = c(NA, 
-29L), class = "data.frame")
 

Ответ №3:

Вы также можете использовать следующее решение:

 library(dplyr)

df %>%
  group_by(ID) %>% 
  filter(between(row_number(), 1, which(Weekday == 7)[sum(Weekday == 7)]))

# A tibble: 23 x 2
# Groups:   ID [3]
   ID    Weekday
   <fct>   <int>
 1 a           1
 2 a           7
 3 a           1
 4 a           1
 5 a           2
 6 a           7
 7 a           1
 8 a           2
 9 a           3
10 a           1
# ... with 13 more rows