Как упорядочить связи в данных так, чтобы ранее наблюдаемое значение появилось первым - PullRequest
0 голосов
/ 25 августа 2018
ex <- structure(list(group = c(1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 
2, 2, 2, 2, 2), timestamp = structure(c(1504975114, 1504975115, 
1504975116, 1504975116, 1504975121, 1504975121, 1504975121, 1504975121, 
1504963482, 1504963486, 1504963486, 1504964343, 1504964343, 1504964394, 
1504964394, 1504964394, 1504964394), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), subgroup = c(36L, 36L, 36L, 35L, 36L, 35L, 
35L, 36L, 43L, 43L, 14L, 14L, 14L, 14L, 14L, 43L, 43L), A = c(1L, 
49L, 1L, 74L, 12L, 61L, 5L, 5L, 1L, 30L, 30L, 18L, 19L, 32L, 
40L, 32L, 40L), B = c(1L, 1L, 0L, 1L, 1L, 1L, 0L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L)), .Names = c("group", "timestamp", 
"subgroup", "A", "B"), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -17L))

У меня есть данные, как указано выше.Я хочу отсортировать данные в пределах group по отметке времени, но также обратите внимание на то, как обрабатываются связи в отметке времени.Точнее говоря, если два наблюдения имеют одну и ту же временную метку, я бы хотел, чтобы первым было это наблюдение, которое имеет тот же subgroup id, что и значение из предыдущей временной метки.Таким образом, желаемый результат будет выглядеть следующим образом:

    # A tibble: 17 x 5
    group timestamp           subgroup     A     B
    <dbl> <dttm>                 <int> <int> <int>
 1  1.00 2017-09-09 16:38:34       36     1     1
 2  1.00 2017-09-09 16:38:35       36    49     1
 3  1.00 2017-09-09 16:38:36       36     1     0
 4  1.00 2017-09-09 16:38:36       35    74     1
 5  1.00 2017-09-09 16:38:41       35    61     1
 6  1.00 2017-09-09 16:38:41       35     5     0
 7  1.00 2017-09-09 16:38:41       36    12     1
 8  1.00 2017-09-09 16:38:41       36     5     1
 9  2.00 2017-09-09 13:24:42       43     1     1
10  2.00 2017-09-09 13:24:46       43    30     1
11  2.00 2017-09-09 13:24:46       14    30     1
12  2.00 2017-09-09 13:39:03       14    18     1
13  2.00 2017-09-09 13:39:03       14    19     1
14  2.00 2017-09-09 13:39:54       14    32     1
15  2.00 2017-09-09 13:39:54       14    40     1
16  2.00 2017-09-09 13:39:54       43    32     1
17  2.00 2017-09-09 13:39:54       43    40     1

Как я могу это сделать?

Ответы [ 4 ]

0 голосов
/ 28 августа 2018

Вот идея использования tidyverse:

library(tidyverse)
ex %>%
  group_by(group) %>%
  mutate(order = map2(
    split_ <- split(subgroup,timestamp),
    accumulate(split_, ~intersect(c(rev(.x),.y),.y)),
    match) %>% unlist) %>%
  arrange(group,timestamp,order) 

# # A tibble: 17 x 6
# # Groups:   group [2]
#    group           timestamp subgroup     A     B order
#    <dbl>              <dttm>    <int> <int> <int> <int>
#  1     1 2017-09-09 16:38:34       36     1     1     1
#  2     1 2017-09-09 16:38:35       36    49     1     1
#  3     1 2017-09-09 16:38:36       36     1     0     1
#  4     1 2017-09-09 16:38:36       35    74     1     2
#  5     1 2017-09-09 16:38:41       35    61     1     1
#  6     1 2017-09-09 16:38:41       35     5     0     1
#  7     1 2017-09-09 16:38:41       36    12     1     2
#  8     1 2017-09-09 16:38:41       36     5     1     2
#  9     2 2017-09-09 13:24:42       43     1     1     1
# 10     2 2017-09-09 13:24:46       43    30     1     1
# 11     2 2017-09-09 13:24:46       14    30     1     2
# 12     2 2017-09-09 13:39:03       14    18     1     1
# 13     2 2017-09-09 13:39:03       14    19     1     1
# 14     2 2017-09-09 13:39:54       14    32     1     1
# 15     2 2017-09-09 13:39:54       14    40     1     1
# 16     2 2017-09-09 13:39:54       43    32     1     2
# 17     2 2017-09-09 13:39:54       43    40     1     2

Я сделал предположение, что метки времени сортируются перед рукой, если нет, сортируют как первый шаг с ex %>% arrange(group, timestamp) %>% ....

Вы можете добавить %>% select(-order) %>% ungroup, чтобы получить точно желаемый результат (я оставил это так, чтобы было легче понять).


1012 * пояснения * Давайте оставим только группу 1, чтобы проиллюстрировать, что происходит внутри вызова mutate: ex1 <- filter(ex, group==1) Для каждой временной отметки мы составляем список подгрупп: split_ <- split(ex1$subgroup,ex1$timestamp) # $`2017-09-09 16:38:34` # [1] 36 # # $`2017-09-09 16:38:35` # [1] 36 # # $`2017-09-09 16:38:36` # [1] 36 35 # # $`2017-09-09 16:38:41` # [1] 36 35 35 36 Порядок последнего элемента должен быть изменен, 35 должен предшествовать 36, потому что он используется последним в 3-м элементе. Поскольку intersect сохраняет порядок элементов в 1-м аргументе, я могу получить правильный порядок для последнего элемента следующим образом: intersect(c(rev(split_[[3]]), split_[[4]]), split_[[4]]) # [1] 35 36 Чтобы применить это преобразование ко всем элементам, я использую purrr::accumulate, так как мне всегда нужен последний вычисленный порядок для вычисления следующего: acc_ <- accumulate(split_, ~intersect(c(rev(.x),.y),.y)) # [[1]] # [1] 36 # # [[2]] # [1] 36 # # [[3]] # [1] 36 35 # # [[4]] # [1] 35 36 Если я использую split_ и acc_ с match, я могу получить порядок, который должны быть у этих элементов в нашем выводе map2(split_ , acc_, match) # $`2017-09-09 16:38:34` # [1] 1 # # $`2017-09-09 16:38:35` # [1] 1 # # $`2017-09-09 16:38:36` # [1] 1 2 # # $`2017-09-09 16:38:41` # [1] 2 1 1 2 Затем я могу unlist получить столбец order_ и отсортировать по order_, чтобы получить желаемый результат.

0 голосов
/ 26 августа 2018

Это дает желаемый результат. Интересно, есть ли более простой способ сделать это

library(dplyr) 

ex1<-ex %>% 
  mutate(timestamp=as.POSIXct(as.character(timestamp))) %>% 
  arrange(group,timestamp) %>% 
  group_by(group,timestamp) %>% 
  mutate(order=0,subgroup_lag=0)  # initialising variable to use in the for loop

ex1$group_id<-  group_indices(ex,group,timestamp) # creating a group_index, this will be used for splitting the dataset into multiple datasets 

ex_list<-split(ex1,ex1$group_id) # split by group_index
# this will create a list with datasets that have the same Group, and timestamp

for (i in 2: length(ex_list)){ # for each dataframe in the list ex_list
  if (nrow(as.data.frame(ex_list[[i]]))>1){ 

     ex_list[[i]]$subgroup_lag<-ex_list[[i-1]][nrow(ex_list[[i-1]]),]$subgroup #if there are multiple rows in a dataframe, obtain the subgroup value from the previous row
     #calling that value to be compared subgroup_lag 
     if(ex_list[[i]]$subgroup_lag>0){

       ex_list[[i]]$order <- ifelse(ex_list[[i]]$subgroup_lag == ex_list[[i]]$subgroup,1,0 ) #identify rows that have the same subgroup id as the subgroup_lag value, then mark order as 1
       # this is to sort easily in the next step
     }

  }
  ex_list[[i]] <-  ex_list[[i]] %>%
    arrange(desc(order)) # sort by order
}

df<-do.call(rbind,ex_list) # bind rows into a dataframe
df 

вывод как ниже

group timestamp           subgroup     A     B
   <dbl> <dttm>                 <int> <int> <int>
 1    1. 2017-09-09 16:38:34       36     1     1
 2    1. 2017-09-09 16:38:35       36    49     1
 3    1. 2017-09-09 16:38:36       36     1     0
 4    1. 2017-09-09 16:38:36       35    74     1
 5    1. 2017-09-09 16:38:41       35    61     1
 6    1. 2017-09-09 16:38:41       35     5     0
 7    1. 2017-09-09 16:38:41       36    12     1
 8    1. 2017-09-09 16:38:41       36     5     1
 9    2. 2017-09-09 13:24:42       43     1     1
10    2. 2017-09-09 13:24:46       43    30     1
11    2. 2017-09-09 13:24:46       14    30     1
12    2. 2017-09-09 13:39:03       14    18     1
13    2. 2017-09-09 13:39:03       14    19     1
14    2. 2017-09-09 13:39:54       14    32     1
15    2. 2017-09-09 13:39:54       14    40     1
16    2. 2017-09-09 13:39:54       43    32     1
17    2. 2017-09-09 13:39:54       43    40     1
0 голосов
/ 27 августа 2018

Векторизованное решение. Но, боюсь, это не более эффективно, чем цикл for

vector_f<- function() { 
  ex$id<-seq_along(ex$group)

  ex1<-ex %>% 
    mutate(timestamp=as.POSIXct(as.character(timestamp))) 
  ex1$group_id<-  as.numeric(group_indices(ex1,group,timestamp))

  df_list<- list()
  for (i in 2:max(ex1$group_id)){ 
    df_list[[i]]<- ex1 %>%
      filter(group_id %in% c(i-1,i,i+1)) %>% 
      arrange(group,timestamp) %>% 
      group_by(group,timestamp) %>% 
      mutate(subgroup_1=last(subgroup)) %>% 
      ungroup() %>% 
      mutate(temp= lag(subgroup_1,n=1)) %>% 
      group_by(group,timestamp) %>% 
      mutate(subgroup_lag= first(temp,n=1)) %>% 
      mutate(order =ifelse(subgroup_lag == subgroup,1,0 ) ) %>% 
      arrange(group,timestamp,desc(order)) %>% 
      ungroup() %>% 
      filter(group_id %in% c(i))
   }
  df_list[[1]]<- ex1 %>%
    filter(group_id ==1 ) %>%
    mutate(subgroup_1=0,order=0,temp=0,subgroup_lag=0) %>% 
  ungroup()

df<-do.call(rbind,df_list)
  print(df)
}

Когда я выполняю сравнение с использованием предоставленного вами набора данных, цикл for показывает гораздо лучшие результаты> его, потому что даже когда мы векторизируем, мы вынуждены разбивать набор данных на группы для сравнения. Так что, честно говоря, это скорее решение dplyr, со всеми потерями векторизации

> microbenchmark(vector_f(), for_f(), times=100)
Unit: milliseconds
       expr      min       lq     mean   median       uq       max neval
 vector_f() 58.03299 66.40527 73.79760 70.92226 78.58620 115.33876   100
    for_f() 12.64291 13.80850 16.32043 16.10607 17.63527  27.66872   100

Я также запустил коды, создав фиктивный набор данных из 8,5 миллионов строк, и вот системное время. Я предлагаю удалить столбцы, которые вам не нужны, а затем добавить их обратно после завершения этой обработки

system.time(vector_f())
   user  system elapsed 
 244.47    3.40  248.12 

system.time(for_f())
   user  system elapsed 
 218.61    0.71  219.50 

Использование решения на основе data.table может быть быстрее

0 голосов
/ 25 августа 2018

Это код, который обычно работал бы.

library(dplyr)        
ex %>% 
arrange(group,timestamp,subgroup)

Но при этом получается такой вывод

 group timestamp           subgroup     A     B
   <dbl> <dttm>                 <int> <int> <int>
 1    1. 2017-09-09 16:38:34       36     1     1
 2    1. 2017-09-09 16:38:35       36    49     1
 3    1. 2017-09-09 16:38:36       36     1     0
 4    1. 2017-09-09 16:38:36       35    74     1
 5    1. 2017-09-09 16:38:41       36    12     1
 6    1. 2017-09-09 16:38:41       35    61     1
 7    1. 2017-09-09 16:38:41       35     5     0
 8    1. 2017-09-09 16:38:41       36     5     1
 9    2. 2017-09-09 13:24:42       43     1     1
10    2. 2017-09-09 13:24:46       43    30     1

причина в том, что объект даты и времени также хранит доли секунд, поэтомухотя кажется, что временные метки строк 5 и 6 совпадают, они не совпадают.Вы можете сделать быстрый as.numeric(ex$timestamp), чтобы проверить это.

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

library(dplyr) 
library(lubridate) # to use round_date
ex %>% 
   arrange(group,round_date(timestamp, ".5s"),subgroup)

 group timestamp           subgroup     A     B
   <dbl> <dttm>                 <int> <int> <int>
 1    1. 2017-09-09 16:38:34       36     1     1
 2    1. 2017-09-09 16:38:35       36    49     1
 3    1. 2017-09-09 16:38:36       35    74     1
 4    1. 2017-09-09 16:38:36       36     1     0
 5    1. 2017-09-09 16:38:41       35    61     1
 6    1. 2017-09-09 16:38:41       35     5     0
 7    1. 2017-09-09 16:38:41       36    12     1
 8    1. 2017-09-09 16:38:41       36     5     1
 9    2. 2017-09-09 13:24:42       43     1     1
10    2. 2017-09-09 13:24:46       14    30     1
11    2. 2017-09-09 13:24:46       43    30     1
12    2. 2017-09-09 13:39:03       14    18     1
13    2. 2017-09-09 13:39:03       14    19     1
14    2. 2017-09-09 13:39:54       14    32     1
15    2. 2017-09-09 13:39:54       14    40     1
16    2. 2017-09-09 13:39:54       43    32     1
17    2. 2017-09-09 13:39:54       43    40     1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...