Присвоить идентификатор группы последовательности концептуальных уникальных значений во временных рядах - PullRequest
6 голосов
/ 22 января 2020

Я имею дело с большой серией времени с одним столбцом, содержащим четыре разных датчика, и одним столбцом, содержащим измеренные значения. Мне нужно назначить идентификатор для измерений, которые принадлежат к одному и тому же времени. Проблема в том, что время измерений немного отличается для каждого устройства, поэтому я не могу просто сгруппировать их по метке времени. В кадре данных, упорядоченном по времени, измерения, которые должны быть сгруппированы, могут быть идентифицированы последовательностями уникальных идентификаторов устройств. Проблема здесь в том, что в одно время 4 устройства записывают значение, а в другое время 3 устройства записывают значение. Мои данные выглядят так:

       timestamp                  device   measurement
    1  2019-08-27 07:29:20.671313 sdr_03   49.868820
    2  2019-08-27 07:29:20.932043 sdr_02   54.160831
    3  2019-08-27 07:29:21.839312 sdr_03   48.974476
    4  2019-08-27 07:29:21.850454 sdr_02   50.808674
    5  2019-08-27 08:57:01.990833 sdr_03   50.533058
    6  2019-08-27 08:57:02.022798 sdr_04   51.143322
    7  2019-08-27 09:16:56.454308 sdr_02   57.447151
    8  2019-08-27 09:16:56.482433 sdr_04   50.012745
    9  2019-08-27 09:16:56.761776 sdr_01   71.500305
    10 2019-08-27 09:16:57.305510 sdr_02   56.851177
    11 2019-08-27 09:16:57.333628 sdr_04   60.390141
    12 2019-08-27 09:16:57.612972 sdr_01   73.470345

, которые вы можете воспроизвести с помощью этого:

my_data<-data.frame(timestamp = c("2019-08-27 07:29:20.671313","2019-08-27 07:29:20.932043","2019-08-27 07:29:21.839312",
                                       "2019-08-27 07:29:21.850454", "2019-08-27 08:57:01.990833","2019-08-27 08:57:02.022798",
                                       "2019-08-27 09:16:56.454308", "2019-08-27 09:16:56.482433", "2019-08-27 09:16:56.761776",
                                       "2019-08-27 09:16:57.305510" ,"2019-08-27 09:16:57.333628", "2019-08-27 09:16:57.612972"),
               device=c("sdr_03", "sdr_02", "sdr_03", "sdr_02", "sdr_03" ,"sdr_04", "sdr_02", "sdr_04" ,"sdr_01", "sdr_02" ,"sdr_04",
                        "sdr_01"),
               measurement=c(49.868820, 54.160831, 48.974476, 50.808674, 50.533058, 51.143322,57.447151,50.012745, 71.500305,56.851177,
                             60.390141, 73.470345)
               )

Мне нужно присвоить одно и то же значение последовательным строкам, пока ни один из элементов в снова появляются предыдущие строки столбца устройства

             timestamp        device   measurement match_id
1  2019-08-27 07:29:20.671313 sdr_03   49.868820        1
2  2019-08-27 07:29:20.932043 sdr_02   54.160831        1
3  2019-08-27 07:29:21.839312 sdr_03   48.974476        2
4  2019-08-27 07:29:21.850454 sdr_02   50.808674        2
5  2019-08-27 08:57:01.990833 sdr_03   50.533058        3
6  2019-08-27 08:57:02.022798 sdr_04   51.143322        3
7  2019-08-27 09:16:56.454308 sdr_02   57.447151        3
8  2019-08-27 09:16:56.482433 sdr_04   50.012745        4
9  2019-08-27 09:16:56.761776 sdr_01   71.500305        4
10 2019-08-27 09:16:57.305510 sdr_02   56.851177        4
11 2019-08-27 09:16:57.333628 sdr_04   60.390141        5
12 2019-08-27 09:16:57.612972 sdr_01   73.470345        5

, который вы можете получить:

my_data<-data.frame(timestamp = c("2019-08-27 07:29:20.671313","2019-08-27 07:29:20.932043","2019-08-27 07:29:21.839312",
                                   "2019-08-27 07:29:21.850454", "2019-08-27 08:57:01.990833","2019-08-27 08:57:02.022798",
                                   "2019-08-27 09:16:56.454308", "2019-08-27 09:16:56.482433", "2019-08-27 09:16:56.761776",
                                   "2019-08-27 09:16:57.305510" ,"2019-08-27 09:16:57.333628", "2019-08-27 09:16:57.612972"),
           device=c("sdr_03", "sdr_02", "sdr_03", "sdr_02", "sdr_03" ,"sdr_04", "sdr_02", "sdr_04" ,"sdr_01", "sdr_02" ,"sdr_04",
                    "sdr_01"),
           measurement=c(49.868820, 54.160831, 48.974476, 50.808674, 50.533058, 51.143322,57.447151,50.012745, 71.500305,56.851177,
                         60.390141, 73.470345),match_id=c(1,1,2,2,3,3,3,4,4,4,5,5) )

Я искал ответы в течение трех дней. Любая помощь очень ценится.

Решение dplyr Аллана Камерона приводит к идентификаторам совпадений, которые появляются позже в кадре данных - см. Строки 1,2,6,9. За один раз может быть записано менее 4 устройств, поэтому решения, которые всегда ожидают одинакового количества записывающих устройств для каждого измерения, не будут работать.

# A tibble: 12 x 4
# Groups:   device [4]
   timestamp                  device measurement new_id
   <dttm>                     <fct>        <dbl>  <int>
 1 2019-08-27 07:29:20.671313 sdr_03        49.9      1
 2 2019-08-27 07:29:20.932043 sdr_02        54.2      1
 3 2019-08-27 07:29:21.839312 sdr_03        49.0      2
 4 2019-08-27 07:29:21.850454 sdr_02        50.8      2
 5 2019-08-27 08:57:01.990833 sdr_03        50.5      3
 6 2019-08-27 08:57:02.022798 sdr_04        51.1      1
 7 2019-08-27 09:16:56.454308 sdr_02        57.4      3
 8 2019-08-27 09:16:56.482433 sdr_04        50.0      2
 9 2019-08-27 09:16:56.761775 sdr_01        71.5      1
10 2019-08-27 09:16:57.305510 sdr_02        56.9      4
11 2019-08-27 09:16:57.333627 sdr_04        60.4      3
12 2019-08-27 09:16:57.612972 sdr_01        73.5      2

В то время как решение Sotos дает больше последовательных идентификаторов совпадений, чем существуют уникальные устройства. Например, строки 5-9

# A tibble: 12 x 4
   timestamp           device measurement new_id
   <chr>               <fct>        <dbl>  <int>
 1 2019-08-27 07:29:20 sdr_03        49.9      1
 2 2019-08-27 07:29:20 sdr_02        54.2      1
 3 2019-08-27 07:29:21 sdr_03        49.0      2
 4 2019-08-27 07:29:21 sdr_02        50.8      2
 5 2019-08-27 08:57:01 sdr_03        50.5      3
 6 2019-08-27 08:57:02 sdr_04        51.1      3
 7 2019-08-27 09:16:56 sdr_02        57.4      3
 8 2019-08-27 09:16:56 sdr_04        50.0      3
 9 2019-08-27 09:16:56 sdr_01        71.5      3
10 2019-08-27 09:16:57 sdr_02        56.9      4
11 2019-08-27 09:16:57 sdr_04        60.4      4
12 2019-08-27 09:16:57 sdr_01        73.5      4

Оба решения отлично работают (спасибо!), Если разница между измерениями составляет> 0,7 сек c или 4 устройства записаны одновременно. К сожалению, в большинстве случаев это не так. Я думаю, что решение, которое игнорирует временные метки и скорее проверяет дубликаты в последовательных строках, может быть лучше. Я нашел много решений для повторяющихся значений, используя rle () или data.table, но не нашел решения для определения последовательностей уникальных значений. Пожалуйста, помогите мне здесь!

Ответы [ 5 ]

2 голосов
/ 22 января 2020

Разве это не может быть сделано проще?

library(dplyr)

df               %>% 
group_by(device) %>% 
mutate(new_id = seq_len(length(device)), timestamp = as.POSIXct(timestamp))

#> # A tibble: 12 x 4
#> # Groups:   device [4]
#>    timestamp           device measurement new_id
#>    <dttm>              <fct>        <dbl>  <int>
#>  1 2019-08-27 09:48:54 sdr_02        80.2      1
#>  2 2019-08-27 09:48:54 sdr_01        71.7      1
#>  3 2019-08-27 09:48:54 sdr_04        74.2      1
#>  4 2019-08-27 09:48:54 sdr_03        62.6      1
#>  5 2019-08-27 09:48:55 sdr_02        77.1      2
#>  6 2019-08-27 09:48:55 sdr_01        69.2      2
#>  7 2019-08-27 09:48:55 sdr_03        62.1      2
#>  8 2019-08-27 09:48:55 sdr_02        77.1      3
#>  9 2019-08-27 09:48:55 sdr_01        54.6      3
#> 10 2019-08-27 09:48:55 sdr_03        64.3      3
#> 11 2019-08-27 09:48:56 sdr_02        66.5      4
#> 12 2019-08-27 09:48:56 sdr_01        71.7      4



ОБНОВЛЕНИЕ

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

# Code                                     # Pseudocode
# =======================================  # ===================================
group_instances <- function(my_labels)     #
{                                          #
  my_labels <- as.character(my_labels)     # (Ensure we use a character vector)
                                           #
  result    <- numeric(length(my_labels))  # Create a numeric result vector
  matches   <- as.character(my_labels[1])  # Create tally of encountered devices
                                           #
  for(i in seq_along(my_labels)[-1])       # For each device record after the first
  {                                        #
    if(my_labels[i] %in% matches)          # If we have this device in our tally
    {                                      #
      matches   <- my_labels[i]            # Reset our tally of devices
      result[i] <- result[i - 1] + 1       # and increment our ID
    }                                      #
    else                                   # Otherwise
    {                                      #
      matches <- c(matches, my_labels[i])  # Add it to our tally of devices
      result[i] <- result[i - 1]           # and copy the ID from the row above
    }                                      #
  }                                        #
  return(result + 1)                       # Our IDs started at zero, so add one
}

Теперь мы можем сделать

my_data %>% mutate(ID = as.factor(group_instances(device)))
#>                     timestamp device measurement ID
#> 1  2019-08-27 07:29:20.671313 sdr_03    49.86882  1
#> 2  2019-08-27 07:29:20.932043 sdr_02    54.16083  1
#> 3  2019-08-27 07:29:21.839312 sdr_03    48.97448  2
#> 4  2019-08-27 07:29:21.850454 sdr_02    50.80867  2
#> 5  2019-08-27 08:57:01.990833 sdr_03    50.53306  3
#> 6  2019-08-27 08:57:02.022798 sdr_04    51.14332  3
#> 7  2019-08-27 09:16:56.454308 sdr_02    57.44715  3
#> 8  2019-08-27 09:16:56.482433 sdr_04    50.01275  4
#> 9  2019-08-27 09:16:56.761776 sdr_01    71.50030  4
#> 10 2019-08-27 09:16:57.305510 sdr_02    56.85118  4
#> 11 2019-08-27 09:16:57.333628 sdr_04    60.39014  5
#> 12 2019-08-27 09:16:57.612972 sdr_01    73.47034  5
2 голосов
/ 22 января 2020

Я почти уверен, что действительно обдумал это, но это рабочее решение,

library(dplyr)

data %>% 
 mutate(timestamp = format(timestamp, '%Y-%m-%d %H:%M:%S')) %>%
 group_by(timestamp) %>% 
 mutate(new = data.table::rleid(duplicated(device))) %>% 
 group_by(timestamp, new) %>% 
 mutate(new1 = row_number() + new) %>% 
 ungroup() %>% 
 mutate(new_id = cumsum(c(TRUE, diff(new1) < 0))) %>% 
 select(-c(new, new1))

, которое дает,

# A tibble: 12 x 4
   timestamp           device measurement new_id
   <fct>               <fct>        <dbl>  <int>
 1 2019-08-27 09:48:54 sdr_02        80.2      1
 2 2019-08-27 09:48:54 sdr_01        71.7      1
 3 2019-08-27 09:48:54 sdr_04        74.2      1
 4 2019-08-27 09:48:54 sdr_03        62.6      1
 5 2019-08-27 09:48:55 sdr_02        77.1      2
 6 2019-08-27 09:48:55 sdr_01        69.2      2
 7 2019-08-27 09:48:55 sdr_03        62.1      2
 8 2019-08-27 09:48:55 sdr_02        77.1      3
 9 2019-08-27 09:48:55 sdr_01        54.6      3
10 2019-08-27 09:48:55 sdr_03        64.3      3
11 2019-08-27 09:48:56 sdr_02        66.5      4
12 2019-08-27 09:48:56 sdr_01        71.7      4
1 голос
/ 23 января 2020

Вот один из подходов, который использует Reduce() и %in% для назначения идентификаторов путем сопоставления последующего с более ранними значениями устройства и сброса при совпадении.

library(dplyr)

my_data %>%
    mutate(match_id = cumsum(lengths(
        Reduce(function(x, y)
            if (y %in% x)
                y
            else
                c(x, y), as.integer(as.factor(device)), accumulate = TRUE)
    ) == 1))

                    timestamp device measurement match_id
1  2019-08-27 07:29:20.671313 sdr_03    49.86882        1
2  2019-08-27 07:29:20.932043 sdr_02    54.16083        1
3  2019-08-27 07:29:21.839312 sdr_03    48.97448        2
4  2019-08-27 07:29:21.850454 sdr_02    50.80867        2
5  2019-08-27 08:57:01.990833 sdr_03    50.53306        3
6  2019-08-27 08:57:02.022798 sdr_04    51.14332        3
7  2019-08-27 09:16:56.454308 sdr_02    57.44715        3
8  2019-08-27 09:16:56.482433 sdr_04    50.01275        4
9  2019-08-27 09:16:56.761776 sdr_01    71.50030        4
10 2019-08-27 09:16:57.305510 sdr_02    56.85118        4
11 2019-08-27 09:16:57.333628 sdr_04    60.39014        5
12 2019-08-27 09:16:57.612972 sdr_01    73.47034        5
1 голос
/ 23 января 2020

Я думаю, что требуется рекурсивная функция. По сути, вам нужно начинать новую группу всякий раз, когда устройство найдено в предыдущей группе. Вот реализация в Rcpp:

library(Rcpp)
cppFunction("
IntegerVector dev_not_in_prev_grp(IntegerVector device, int ndev) {
    int i, j, k, sz = device.size();
    std::vector<bool> exists(ndev);
    IntegerVector res(sz);

    for (k=0; k<ndev; k++) 
        exists[k] = false;

    for (i=0; i<sz; i++) {
        if (exists[device[i]-1]) {
            res[i] = 1;

            for (k=0; k<ndev; k++) 
                exists[k] = false;
        } 
        exists[device[i]-1] = true;
    }

    return(res);
}
")

использование:

ndev <- 4L
devmap <- setNames(1L:ndev, sprintf("sdr_%02d", 1L:ndev))    
cumsum(dev_not_in_prev_grp(devmap[my_data$device], ndev)) + 1L

вывод:

[1] 1 1 2 2 3 3 3 4 4 4 5 5
0 голосов
/ 23 января 2020

Пожалуйста, посмотрите, работает ли это. Я использовал для l oop для решения этой проблемы. Вам нужно взглянуть на столбец «match_id». running_string для справки.

my_data$match_id <- 1
my_data$running_string <- ''

test_str <- ''


for(i in (1:nrow(my_data))){
  if(grepl(my_data$device[i],test_str)){
    my_data$match_id[i] <- my_data$match_id[i-1] + 1
    test_str <- as.character(my_data$device[i])
  } else{
    test_str <- paste0(test_str,my_data$device[i])
    if(i > 1){
       my_data$match_id[i] <- my_data$match_id[i-1] 
    }

  }
  my_data$running_string[i] <- test_str
}
head(my_data,20)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...