Условно изменяющиеся значения путем группировки переменных в R; условие, основанное на различном времени между метками времени в двух кадрах данных

#r #if-statement #conditional-statements #difftime

Вопрос:

Я пытаюсь исправить некоторые ошибочные записи в наборе данных условным способом. Мне нужно сделать это по группам, и условие основано на разнице между 2 временными метками в 2 разных наборах данных.

Вот несколько примеров типов данных, с которыми я работаю:-

 df1<-structure(list(UserID = c("AAA", "AAA", "AAA", "BBB", "BBB", 
                               "BBB", "BBB", "CCC", "CCC", "CCC", "CCC", "CCC", "DDD", "DDD", 
                               "DDD", "DDD", "DDD", "DDD"), Value = c("Group1", "Group1", "Group2", 
                                                                      "Group3", "Group3", "Group1", "Group2", "Group4", "Group5", "Group5", 
                                                                      "Group5", "Group5", "Group1", "Group2", "Group2", "Group2", "Group2", 
                                                                      "Group2"), Time = structure(c(1577840400, 1577844000, 1577847600, 
                                                                                                    1577966400, 1577970000, 1577973600, 1577977200, 1577977200, 1577980800, 
                                                                                                    1577984400, 1577988000, 1577991600, 1578193200, 1578196800, 1578200400, 
                                                                                                    1578204000, 1578207600, 1578211200), class = c("POSIXct", "POSIXt"
                                                                                                    ), tzone = "UTC")), row.names = c(NA, -18L), class = "data.frame")


df2<-structure(list(UserID = c("AAA", "AAA", "AAA", "BBB", "BBB", 
                               "BBB", "BBB", "CCC", "CCC", "DDD", "DDD"), StartTime = structure(c(1577839980, 
                                                                                                  1577840460, 1577843820, 1577966580, 1577970180, 1577973360, 1577975160, 
                                                                                                  1577977920, 1577978940, 1578193200, 1578193920), class = c("POSIXct", 
                                                                                                                                                             "POSIXt"), tzone = "UTC"), EndTime = structure(c(1577840460, 
                                                                                                                                                                                                              1577843820, 1577846640, 1577970180, 1577973360, 1577975160, 1577978580, 
                                                                                                                                                                                                              1577978940, 1577980680, 1578193920, 1578196620), class = c("POSIXct", 
                                                                                                                                                                                                                                                                         "POSIXt"), tzone = "UTC")), row.names = c(NA, -11L), class = "data.frame")
 

Оба набора данных выглядят следующим образом:-

 print(df1)
   UserID  Value                Time
1     AAA Group1 2020-01-01 01:00:00
2     AAA Group1 2020-01-01 02:00:00
3     AAA Group2 2020-01-01 03:00:00
4     BBB Group3 2020-01-02 12:00:00
5     BBB Group3 2020-01-02 13:00:00
6     BBB Group1 2020-01-02 14:00:00
7     BBB Group2 2020-01-02 15:00:00
8     CCC Group4 2020-01-02 15:00:00
9     CCC Group5 2020-01-02 16:00:00
10    CCC Group5 2020-01-02 17:00:00
11    CCC Group5 2020-01-02 18:00:00
12    CCC Group5 2020-01-02 19:00:00
13    DDD Group1 2020-01-05 03:00:00
14    DDD Group2 2020-01-05 04:00:00
15    DDD Group2 2020-01-05 05:00:00
16    DDD Group2 2020-01-05 06:00:00
17    DDD Group2 2020-01-05 07:00:00
18    DDD Group2 2020-01-05 08:00:00

 print(df2)
   UserID           StartTime             EndTime
1     AAA 2020-01-01 00:53:00 2020-01-01 01:01:00
2     AAA 2020-01-01 01:01:00 2020-01-01 01:57:00
3     AAA 2020-01-01 01:57:00 2020-01-01 02:44:00
4     BBB 2020-01-02 12:03:00 2020-01-02 13:03:00
5     BBB 2020-01-02 13:03:00 2020-01-02 13:56:00
6     BBB 2020-01-02 13:56:00 2020-01-02 14:26:00
7     BBB 2020-01-02 14:26:00 2020-01-02 15:23:00
8     CCC 2020-01-02 15:12:00 2020-01-02 15:29:00
9     CCC 2020-01-02 15:29:00 2020-01-02 15:58:00
10    DDD 2020-01-05 03:00:00 2020-01-05 03:12:00
11    DDD 2020-01-05 03:12:00 2020-01-05 03:57:00
 

Столбец метки времени в df1 округляется до каждого часа, в то время как в df2 есть начальная метка времени и конечная метка времени (обе детализированы и округлены до минуты). В df1 есть некоторые записи, которые неверны, поскольку они не отображаются в df2 в соответствующее время.

Например, UserID последняя EndTime метка времени CCC в df2-2020-01-02 15:58:00, но в df1 CCC отображается по адресу 2020-01-02 17:00:00, 2020-01-02 18:00:00 amp; 2020-01-02 19:00:00; аналогичный пример с UserID DDD.

Что я хочу сделать

Если UserID у a есть запись в df1 с df1$Time отметкой времени >=60 минут, чем их последняя > df2$EndTime отметка времени в df2, я хочу, чтобы запись в поле df1$Value была изменена на «NoGroup».

Вот наглядный пример желаемого результата:-

    UserID   Value                Time
1     AAA  Group1 2020-01-01 01:00:00
2     AAA  Group1 2020-01-01 02:00:00
3     AAA  Group2 2020-01-01 03:00:00
4     BBB  Group3 2020-01-02 12:00:00
5     BBB  Group3 2020-01-02 13:00:00
6     BBB  Group1 2020-01-02 14:00:00
7     BBB  Group2 2020-01-02 15:00:00
8     CCC  Group4 2020-01-02 15:00:00
9     CCC  Group5 2020-01-02 16:00:00
10    CCC NoGroup 2020-01-02 17:00:00
11    CCC NoGroup 2020-01-02 18:00:00
12    CCC NoGroup 2020-01-02 19:00:00
13    DDD  Group1 2020-01-05 03:00:00
14    DDD  Group2 2020-01-05 04:00:00
15    DDD NoGroup 2020-01-05 05:00:00
16    DDD NoGroup 2020-01-05 06:00:00
17    DDD NoGroup 2020-01-05 07:00:00
18    DDD NoGroup 2020-01-05 08:00:00
 

Любые указатели ценятся, как всегда 🙂

Ответ №1:

с помощью dplyr :

 df1 %>%
  left_join(df2 %>% group_by(UserID) %>% filter(EndTime == max(EndTime)), by = "UserID") %>%
  mutate(Value = if_else(Time-EndTime >= 60, "NoGroup", Value)) %>%
  select(-c(4,5))
 

Сначала вы join последний EndTimes из каждого UserID из df2 них df1 , затем вы проверяете, не прошло ли Time больше 60 минут после этого EndTime , и Value соответственно меняете. Наконец, вы удаляете столбцы, которые были добавлены во время join

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

1. Это здорово, большое вам за это спасибо, работает блестяще!

Ответ №2:

 df1<-structure(list(UserID = c("AAA", "AAA", "AAA", "BBB", "BBB", 
                               "BBB", "BBB", "CCC", "CCC", "CCC", "CCC", "CCC", "DDD", "DDD", 
                               "DDD", "DDD", "DDD", "DDD"), Value = c("Group1", "Group1", "Group2", 
                                                                      "Group3", "Group3", "Group1", "Group2", "Group4", "Group5", "Group5", 
                                                                      "Group5", "Group5", "Group1", "Group2", "Group2", "Group2", "Group2", 
                                                                      "Group2"), Time = structure(c(1577840400, 1577844000, 1577847600, 
                                                                                                    1577966400, 1577970000, 1577973600, 1577977200, 1577977200, 1577980800, 
                                                                                                    1577984400, 1577988000, 1577991600, 1578193200, 1578196800, 1578200400, 
                                                                                                    1578204000, 1578207600, 1578211200), class = c("POSIXct", "POSIXt"
                                                                                                    ), tzone = "UTC")), row.names = c(NA, -18L), class = "data.frame")


df2<-structure(list(UserID = c("AAA", "AAA", "AAA", "BBB", "BBB", 
                               "BBB", "BBB", "CCC", "CCC", "DDD", "DDD"), StartTime = structure(c(1577839980, 
                                                                                                  1577840460, 1577843820, 1577966580, 1577970180, 1577973360, 1577975160, 
                                                                                                  1577977920, 1577978940, 1578193200, 1578193920), class = c("POSIXct", 
                                                                                                                                                             "POSIXt"), tzone = "UTC"), EndTime = structure(c(1577840460, 
                                                                                                                                                                                                              1577843820, 1577846640, 1577970180, 1577973360, 1577975160, 1577978580, 
                                                                                                                                                                                                              1577978940, 1577980680, 1578193920, 1578196620), class = c("POSIXct", 
                                                                                                                                                                                                                                                                         "POSIXt"), tzone = "UTC")), row.names = c(NA, -11L), class = "data.frame")


library(tidyverse)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union

no_groups <-
  df1 %>%
  as_tibble() %>%
  left_join(df2 %>% as_tibble()) %>%
  group_by(UserID) %>%
  mutate(
    last_end = max(EndTime)
  ) %>%
  mutate(
    no_group =  all((last_end - Time) <=  minutes(60))
  ) %>%
  distinct(UserID, no_group)
#> Joining, by = "UserID"
no_groups
#> # A tibble: 4 x 2
#> # Groups:   UserID [4]
#>   UserID no_group
#>   <chr>  <lgl>   
#> 1 AAA    FALSE   
#> 2 BBB    FALSE   
#> 3 CCC    TRUE    
#> 4 DDD    TRUE

df1 %>%
  as_tibble() %>%
  left_join(no_groups) %>%
  mutate(Value = ifelse(no_group, "NoGroup", Value)) %>%
  select(-no_group)
#> Joining, by = "UserID"
#> # A tibble: 18 x 3
#>    UserID Value   Time               
#>    <chr>  <chr>   <dttm>             
#>  1 AAA    Group1  2020-01-01 01:00:00
#>  2 AAA    Group1  2020-01-01 02:00:00
#>  3 AAA    Group2  2020-01-01 03:00:00
#>  4 BBB    Group3  2020-01-02 12:00:00
#>  5 BBB    Group3  2020-01-02 13:00:00
#>  6 BBB    Group1  2020-01-02 14:00:00
#>  7 BBB    Group2  2020-01-02 15:00:00
#>  8 CCC    NoGroup 2020-01-02 15:00:00
#>  9 CCC    NoGroup 2020-01-02 16:00:00
#> 10 CCC    NoGroup 2020-01-02 17:00:00
#> 11 CCC    NoGroup 2020-01-02 18:00:00
#> 12 CCC    NoGroup 2020-01-02 19:00:00
#> 13 DDD    NoGroup 2020-01-05 03:00:00
#> 14 DDD    NoGroup 2020-01-05 04:00:00
#> 15 DDD    NoGroup 2020-01-05 05:00:00
#> 16 DDD    NoGroup 2020-01-05 06:00:00
#> 17 DDD    NoGroup 2020-01-05 07:00:00
#> 18 DDD    NoGroup 2020-01-05 08:00:00
 

Создано 2021-09-17 пакетом reprex (v2.0.0)