Найти непрерывные дни работы двигателя

Я хочу вычислить дни непрерывной работы двигателя: на самом деле, несколько двигателей, но я ограничил свое MRE одним двигателем, надеясь, что вы покажете мне решение, которое может быть легко адаптировано для случая с несколькими двигателями. Для этого у меня есть несколько измерений датчиков, в разное время, для разных двигателей. Обычно эти измерения датчиков происходят довольно часто, но время от времени они могут давать сбой, поэтому частота дискретизации может быть не постоянной. Однако, если разница во времени между двумя последовательными измерениями больше, чем, скажем, 1 день, это означает, что двигатель остановился, и количество рабочих дней должно снова начаться с 0. Пример:

library(lubridate)
library(dplyr)
library(tibble)

set.seed(3)

# a single engine
a_day <- 6
n <- a_day*10
engine <- factor(rep("engine_A", each = n))
end_date <- as_datetime("2018-09-13 19:26:29")
start_date <- end_date - n * hours(4)
date_time <- seq(start_date, end_date, length.out = n)
x <- runif(n)
y <- rnorm(n)
my_df <- data.frame(engine, date_time, x, y)

# short stops don't restart the running days count
short_stops <- sample(seq_len(n), 5)
# long stops, however, do
medium_stop <- sample(seq_len(n), 1)
medium_stop <- rep(medium_stop, each = a_day) + (-3:2)
long_stop <- seq(30,40)
# merge stop indices
index <- sort(unique(c(short_stops, medium_stop, long_stop)))
# remove the rows corresponding to the stops
my_df <- my_df[-index, ]

Так как в этом случае я определил места остановки, я могу вычислить run_days как:

my_df <- my_df %>%
  rowid_to_column() %>%
  mutate(run_days = as.integer(round(case_when(
            rowid < 14 ~ (date_time - start_date) / ddays(1),
            rowid < 20 ~ (date_time - date_time[14]) / ddays(1),
            rowid >= 20 ~ (date_time - date_time[20]) / ddays(1))))) 

т.е.

> my_df
   rowid   engine           date_time          x            y run_days
1      1 engine_A 2018-09-03 19:26:29 0.16804153  0.900624729        0
2      2 engine_A 2018-09-03 23:30:33 0.80751640  0.851770447        0
3      3 engine_A 2018-09-04 03:34:37 0.38494235  0.727715174        0
4      4 engine_A 2018-09-04 07:38:41 0.32773432  0.736502146        1
5      5 engine_A 2018-09-04 11:42:45 0.60210067 -0.352129617        1
6      6 engine_A 2018-09-04 19:50:53 0.12463344  1.300357989        1
7      7 engine_A 2018-09-04 23:54:57 0.29460092  0.038252014        1
8      8 engine_A 2018-09-05 03:59:01 0.57760992 -0.979283770        1
9      9 engine_A 2018-09-05 12:07:09 0.51201590  0.786506872        2
10    10 engine_A 2018-09-05 20:15:17 0.53403535  1.698884846        2
11    11 engine_A 2018-09-06 00:19:21 0.55724944 -0.794593709        2
12    12 engine_A 2018-09-06 08:27:30 0.82970869 -2.265401074        3
13    13 engine_A 2018-09-06 12:31:34 0.11144915 -0.162205279        3
14    14 engine_A 2018-09-07 17:00:02 0.09338193 -1.737263711        0
15    15 engine_A 2018-09-07 21:04:06 0.23688501 -1.411425136        0
16    16 engine_A 2018-09-08 01:08:10 0.79114741 -0.453551227        0
17    17 engine_A 2018-09-08 05:12:14 0.59973157 -1.035491275        1
18    18 engine_A 2018-09-08 09:16:18 0.91014771  1.362142893        1
19    19 engine_A 2018-09-08 13:20:22 0.56042455  0.917456737        1
20    20 engine_A 2018-09-10 14:09:11 0.28146879 -0.031325502        0
21    21 engine_A 2018-09-10 18:13:15 0.78628120  0.467097310        0
22    22 engine_A 2018-09-10 22:17:19 0.17301935  1.024197674        0
23    23 engine_A 2018-09-11 02:21:23 0.57074752  0.267358452        1
24    24 engine_A 2018-09-11 06:25:27 0.41928296  0.231826103        1
25    25 engine_A 2018-09-11 10:29:32 0.26762217  0.747592465        1
26    26 engine_A 2018-09-11 14:33:36 0.04780944  1.217068511        1
27    27 engine_A 2018-09-11 18:37:40 0.10349305  0.383358345        1
28    28 engine_A 2018-09-11 22:41:44 0.31403146 -0.988052822        1
29    29 engine_A 2018-09-12 02:45:48 0.80064106 -0.156852910        2
30    30 engine_A 2018-09-12 06:49:52 0.22932470  1.735535216        2
31    31 engine_A 2018-09-12 10:53:56 0.21299844 -0.352298306        2
32    32 engine_A 2018-09-12 14:58:00 0.87710091  0.688640044        2
33    33 engine_A 2018-09-12 19:02:04 0.99322196  1.224406096        2
34    34 engine_A 2018-09-12 23:06:08 0.84424702  0.794296303        2
35    35 engine_A 2018-09-13 03:10:12 0.91043655 -0.006402398        3
36    36 engine_A 2018-09-13 07:14:16 0.47126973  0.219150635        3
37    37 engine_A 2018-09-13 11:18:20 0.22441841 -0.886463751        3
38    38 engine_A 2018-09-13 15:22:24 0.12781466  0.439760291        3
39    39 engine_A 2018-09-13 19:26:29 0.27968351 -0.886389751        3

В общем случае у меня только датафрейм my_df и мне нужно сгенерировать столбец run_daysКонечно, не путем ручного осмотра. Как я могу это сделать?

2 ответа

Вот еще один ответ, который не использует цикл, но более эффективен tidyverse функции:

library(tidyverse)
offThreshold <- 1
df <-  my_df %>% 
  mutate(off = (date_time - lag(date_time)) / ddays(1) > offThreshold, # lag() means previous record
         timediff = if_else(off, 0, (date_time - lag(date_time)) / ddays(1)),
         timediff = if_else(is.na(timediff), 0, timediff))

dat <- df %>%
  filter(off == TRUE | is.na(off)) %>% # select signals that indicate stopping
  select(engine, date_time) %>%
  mutate(runNo = row_number(date_time)) %>% # number the times of stopping
  {left_join(df, ., by = c("engine",
                           "date_time"))} %>% # add the runNo to the original data
  fill(runNo, .direction = "down") %>% # repopulate runNo to subsequent rows
  group_by(engine, runNo) %>% # create a separate gp. for each machine/run combination
  mutate(run_days = round(cumsum(timediff))) %>% # compute run time for each gp.
  ungroup() %>%
  select(-off, -timediff, -runNo)

head(dat, 15)

Это конечный результат

# A tibble: 15 x 5
   engine   date_time                x       y run_days
   <fct>    <dttm>               <dbl>   <dbl>    <dbl>
 1 engine_A 2018-09-03 19:26:29 0.168   0.901         0
 2 engine_A 2018-09-03 23:30:33 0.808   0.852         0
 3 engine_A 2018-09-04 03:34:37 0.385   0.728         0
 4 engine_A 2018-09-04 07:38:41 0.328   0.737         1
 5 engine_A 2018-09-04 11:42:45 0.602  -0.352         1
 6 engine_A 2018-09-04 19:50:53 0.125   1.30          1
 7 engine_A 2018-09-04 23:54:57 0.295   0.0383        1
 8 engine_A 2018-09-05 03:59:01 0.578  -0.979         1
 9 engine_A 2018-09-05 12:07:09 0.512   0.787         2
10 engine_A 2018-09-05 20:15:17 0.534   1.70          2
11 engine_A 2018-09-06 00:19:21 0.557  -0.795         2
12 engine_A 2018-09-06 08:27:30 0.830  -2.27          3
13 engine_A 2018-09-06 12:31:34 0.111  -0.162         3
14 engine_A 2018-09-07 17:00:02 0.0934 -1.74          0
15 engine_A 2018-09-07 21:04:06 0.237  -1.41          0

Для этого вам может понадобиться цикл, что означает, что он будет немного медленным, если у вас есть большие данные. Однако я попытался оптимизировать его для вас следующим образом:

Давайте сначала определим нашу функцию runDays():

runDays <- function(dat, x){
  offThreshold <- x
  dat$run_days <- 0
  dat$timediff <- difftime(dat$date_time, lag(dat$date_time), units = "days")
  for (i in 2:nrow(dat)){
    if (dat$timediff[i] < offThreshold){
      dat$run_days[i] <- dat$timediff[i] + dat$run_days[i-1]
    }
  }
  return(dat %>% select(-timediff))
}

Мы можем вызвать эту функцию напрямую как runDay(my_df, 1) но это не сработает, если у вас более одного двигателя. Таким образом, нам нужно два других dplyr функции; group_by() а также do():

newDat <- my_df %>% 
  group_by(engine) %>%
  do(runDays(., 1))
head(newDat, 15)
# A tibble: 15 x 5
# Groups:   engine [1]
   engine   date_time                x       y run_days
   <fct>    <dttm>               <dbl>   <dbl>    <dbl>
 1 engine_A 2018-09-03 19:26:29 0.168   0.901     0    
 2 engine_A 2018-09-03 23:30:33 0.808   0.852     0.169
 3 engine_A 2018-09-04 03:34:37 0.385   0.728     0.339
 4 engine_A 2018-09-04 07:38:41 0.328   0.737     0.508
 5 engine_A 2018-09-04 11:42:45 0.602  -0.352     0.678
 6 engine_A 2018-09-04 19:50:53 0.125   1.30      1.02 
 7 engine_A 2018-09-04 23:54:57 0.295   0.0383    1.19 
 8 engine_A 2018-09-05 03:59:01 0.578  -0.979     1.36 
 9 engine_A 2018-09-05 12:07:09 0.512   0.787     1.69 
10 engine_A 2018-09-05 20:15:17 0.534   1.70      2.03 
11 engine_A 2018-09-06 00:19:21 0.557  -0.795     2.20 
12 engine_A 2018-09-06 08:27:30 0.830  -2.27      2.54 
13 engine_A 2018-09-06 12:31:34 0.111  -0.162     2.71 
14 engine_A 2018-09-07 17:00:02 0.0934 -1.74      0    
15 engine_A 2018-09-07 21:04:06 0.237  -1.41      0.169

Вы можете определенно выбрать любой порог в качестве второго аргумента в вашем runDays() вызов.

Другие вопросы по тегам