Выберите первую строку в каждом непрерывном прогоне по группе

#r #dplyr #group-by #sequence

#r #dplyr #групповое-по #последовательность

Вопрос:

У меня есть данные, которые сгруппированы по «идентификатору». Каждый «идентификатор» имеет разные лекарства на разные даты. В каждом последовательном прогоне «наркотика» я хотел бы сохранить только первый ряд. Это должно быть сделано по группам, то есть внутри каждого «идентификатора». В данных показаны два примера:

 ID        date    drug  
 1  01/01/2020       A # first row in run 1 of 'A' for ID 1: keep 
 1  07/01/2020       A # 2nd row in run 1 of 'A' for ID 1: drop
 1  09/01/2020       B
 1  15/01/2020       A
 2  01/02/2020       C 
 2  13/02/2020       D
 2  17/02/2020       C # first row in run 2 of 'C' of ID 2: keep 
 2  18/03/2020       C # 2nd row in run 2 of 'C' of ID 2: drop 
 2  19/03/2020       E
 

Желаемый результат:

 ID     date             drug  
1      01/01/2020        A
1      09/01/2020        B
1      15/01/2020        A
2      01/02/2020        C
2      13/02/2020        D
2      17/02/2020        C
2      19/03/2020        E
 

Я пробовал следующее, но я не могу заставить его работать, так как он удалит те лекарства, которые относятся к той же группе, но появятся позже, например, он упадет 15/01/2020, 17/02/2020 и 18/03/2020, поскольку требуется только первое наблюдение по группам.

 df_selection <- df %>%   
  group_by(ID) %>% 
  arrange(ID,date) %>% 
  group_by(ID, drug) %>% 
  slice(1L) %>% 
  arrange(ID,date)
 

Я перепробовал много комбинаций, но не могу заставить ее работать. Я был бы очень признателен за помощь!


Дополнительный пример для демонстрации случая, когда последний «препарат» в одном «идентификаторе» совпадает с первым в следующем «идентификаторе», здесь препарат «B»:

 ID       date drug
 1 01/01/2020    A
 1 07/01/2020    A
 1 09/01/2020    B # first row in a run of 'B' for ID 1: keep 
 1 15/01/2020    B # 2nd row in a run of 'B' for ID 1: drop 
 2 01/02/2020    B # first row in a run of 'B' for ID 2: keep 
 2 13/02/2020    B # 2nd: drop
 2 17/02/2020    B # 3rd: drop
 2 18/03/2020    E
 2 19/03/2020    E
 

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

1. Что, если лекарство A является последним obs для ID1, а также первым obs для ID2? Сохранить это? Некоторые ответы до сих пор основывались на условии, что ID2 начинается с другого лекарства, чем заканчивается ID1.

2. @JonSpring Действительно, я полагаю, мы должны рассмотреть запуски в пределах ‘id’. Это не указано в вопросе, но код может это предположить.

Ответ №1:

Используя data.table :

 setDT(df)[rowid(rleid(drug)) == 1]
#    ID       date drug
# 1:  1 01/01/2020    A
# 2:  1 09/01/2020    B
# 3:  1 15/01/2020    A
# 4:  2 01/02/2020    C
# 5:  2 13/02/2020    D
# 6:  2 17/02/2020    C
# 7:  2 19/03/2020    E
 

Если прогоны ‘drug’ следует учитывать в каждом ‘ID’, который нам нужен…

 df[rowid(rleid(ID, drug)) == 1]
 

…для обработки таких случаев, как:

    ID       date drug
1:  1 01/01/2020    A
2:  1 07/01/2020    A
3:  1 09/01/2020    B
4:  1 15/01/2020    B # This 'B' belongs to 2nd run in ID 1 
5:  2 01/02/2020    B # This 'B' belongs to 1st run in ID 2
6:  2 13/02/2020    B
7:  2 17/02/2020    B
8:  2 18/03/2020    E
9:  2 19/03/2020    E
 

Ответ №2:

 df %>% filter(drug != lag(drug, default = ""))
 

Или, если вы хотите сохранить первое появление препарата для одного идентификатора, даже если он совпадает с последним препаратом для предыдущего идентификатора (например, допустим, первым препаратом ID2 был A, и поэтому мы хотели сохранить его.):

 df %>%
  filter(drug != lag(drug, default = "") |
           ID != lag(ID, default = 0))
 

Ответ №3:

Используя base R с rle

 subset(df, with(rle(drug), !duplicated(rep(seq_along(values), lengths))))
 

Ответ №4:

Надеюсь, этот код сработает для ваших общих случаев

 > subset(df, sequence(rle(drug)$lengths) == 1)
  ID       date drug
1  1 01/01/2020    A
3  1 09/01/2020    B
4  1 15/01/2020    A
5  2 01/02/2020    C
6  2 13/02/2020    D
7  2 17/02/2020    C
9  2 19/03/2020    E