2016-12-15 6 views
1

Я пытаюсь избежать цикла и использовать apply вместо флагов пост-обработки, которые я обнаружил.Реализация применяется с использованием предыдущих строк, а не для цикла

У меня есть временная серия с колонкой, показывающая, было ли качество в порядке или нет. Вот как dataframe выглядит следующим образом:

n <- 100 
tstart <- strptime("12/15/16 16:00:00", "%m/%d/%y %H:%M:%S") 
df <- data.frame(Date = tstart + seq(0,n*5-1,5) + sample(seq(0,3,1), n, replace = T), 
      Check = sample(c("FLAG", "PASS"), n, replace = T)) 

# head of df 
#   Date   Check 
# 1 2016-12-15 16:00:02 FLAG 
# 2 2016-12-15 16:00:05 PASS 
# 3 2016-12-15 16:00:13 FLAG 
# 4 2016-12-15 16:00:17 PASS 
# 5 2016-12-15 16:00:22 FLAG 
# 6 2016-12-15 16:00:26 FLAG 

Я не хотел, чтобы забрать все FLAG сек, хотя. Я хочу, чтобы применить три условия:

1) ИГНОРИРОВАНИЯ флаги, где разница во времени от предыдущего ряда более чем 60 секунд

2) Я хотел бы держать флаги, которые были повторяющимися на некоторое время.

Вот как я реализую это:

df$Time_Difference <- c(0,as.numeric(diff(df$Date))) 
df$Flag_Counter <- 0 
desired_rep <- 3 
# Start the clock! 
ptm <- proc.time() 
for (row_index in 2:nrow(df)){ 
    if (df[row_index, "Time_Difference"] > 60){ 
     df[row_index, "Flag_Counter"] <- 0 
    } 
    else { 
     if (df[row_index, "Check"] == "PASS"){ 
      df[row_index, "Flag_Counter"] <- max(0, df[row_index-1, "Flag_Counter"] - 1) 
     } 
     else { 
      df[row_index, "Flag_Counter"] <- min(desired_rep, df[row_index-1, "Flag_Counter"] + 1) 
     } 
    } 
} 
# Stop the clock 
x <- proc.time() - ptm 
print(x[3]) 

Таким образом, на самом деле цикл получает флаги, которые были повторяющимися для desired_rep раз подряд. В случае, если у нас есть PASS после двух FLAG s, 1 - Flag_Counter, и, наконец, мы делаем df[, df$Flag_Counter == 3], мы можем постобработки. Теперь это очень медленно. Мне было интересно, можем ли мы использовать apply, чтобы ускорить выполнение этой задачи. Я сделал это в Python, но я не знаю, как получить доступ к предыдущим строкам в моей предопределенной функции, а затем использовать apply. Я ценю вашу помощь.

+0

Это трудный воспроизводимый пример, поскольку между рядами нет разницы во времени более 60 секунд. Кроме того, каков ваш желаемый результат? Просто новый столбец * FlagCounter *? – Parfait

+0

Для примеров, связанных с случайными процессами, [пожалуйста, добавьте 'set.seed' для воспроизводимости] (http://stackoverflow.com/questions/13605271/reasons-for-using-the-set-seed-function). –

ответ

2

Попробуйте это:

desired_rep = 3 

# If Time_Difference > 60, 0, otherwise 1 if "Flag", -1 if "Pass" 
df$temp = ifelse(df$Check=='FLAG',1,-1)*(df$Time_Difference<=60) 

# Do a "cumsum" that's bounded between 0 and 3, and resets to 0 if Time_Difference > 60 
df$Flag_Counter = Reduce(function(x,y) max(0, min(desired_rep,x+y))*(y!=0), df$temp, acc=T) 

В общем, Reduce() полезно, когда вам нужно обновить «состояние», последовательно, с тем ограничением, что вход является единый список/вектор (здесь temp колонка).

+0

Отлично. Мой исходный цикл занимает 180 секунд для заполнения кадра данных в 200 тыс. Строк. Ваш метод делает это менее чем за секунду! Является ли ключ для превосходной работы 'Уменьшить'? Что здесь делает 'acc = T'? Благодарю. – ahoosh

+1

Это дорого стоит получить доступ к элементам data.frame, перейдя по строкам (не уверен, в чем причина, если честно, но сравните 'for (i in 1: 1000000) {}' и 'd = data.frame (x = 1: 1000000), для (i в 1: 1000000) {d [i,]} '). Если вы посмотрите на исходный код 'Reduce', вы увидите, что он также использует for-loops, но более списки, что более эффективно. ('e = as.list (1: 1000000), для (i в 1: 1000000) {e [[i]]}') – sirallen

+1

'accumulate = T' заставляет' Reduce' возвращать все промежуточные результаты, поэтому вы получаете вектор той же длины, что и 'x'. – sirallen

1

Дайте этому попытку:

n <- 100 
tstart <- strptime("12/15/16 16:00:00", "%m/%d/%y %H:%M:%S") 
df <- data.frame(Date = tstart + seq(0,n*5-1,5) + sample(seq(0,3,1), n, replace = T), 
       Check = sample(c("FLAG", "PASS"), n, replace = T)) 

desired_rep <- 3 #set the desired repetition limit 

Время, которое вы использовали в коде примера был End_Time. Я предполагаю, что это должно быть Date из исходного набора данных?

df$Time_Difference <- c(0,as.numeric(diff(df$Date))) 

Найти последовательные флаги. Благодаря этому post.

df$consecutive_flag_count <- sequence(rle(as.character(df$Check))$lengths) 

Создать check_again столбец, который будет возвращать OK если Check является Pass или Time_Difference меньше, чем 60, и есть меньше, чем desired_rep последовательных Check.

df$check_again <- ifelse(df$Check == "PASS", "OK", 
ifelse(df$Time_Difference < 60 & df$consecutive_flag_count >= desired_rep, "CHECK_AGAIN","OK")) 

Вы можете легко отфильтровать в CHECK_AGAIN элементов следующим образом.

df_check_again <- df[df$check_again == "CHECK_AGAIN", ] 
> df_check_again 
        Date Check Time_Difference consecutive_flag_count check_again 
3 2016-12-15 16:00:11 FLAG    4      3 CHECK_AGAIN 
4 2016-12-15 16:00:18 FLAG    7      4 CHECK_AGAIN 
17 2016-12-15 16:01:23 FLAG    5      3 CHECK_AGAIN 
18 2016-12-15 16:01:26 FLAG    3      4 CHECK_AGAIN 
19 2016-12-15 16:01:30 FLAG    4      5 CHECK_AGAIN 
20 2016-12-15 16:01:37 FLAG    7      6 CHECK_AGAIN 
27 2016-12-15 16:02:10 FLAG    3      3 CHECK_AGAIN 
28 2016-12-15 16:02:18 FLAG    8      4 CHECK_AGAIN 
29 2016-12-15 16:02:20 FLAG    2      5 CHECK_AGAIN 
42 2016-12-15 16:03:27 FLAG    4      3 CHECK_AGAIN 
43 2016-12-15 16:03:33 FLAG    6      4 CHECK_AGAIN 
44 2016-12-15 16:03:38 FLAG    5      5 CHECK_AGAIN 
55 2016-12-15 16:04:33 FLAG    7      3 CHECK_AGAIN 
56 2016-12-15 16:04:36 FLAG    3      4 CHECK_AGAIN 
57 2016-12-15 16:04:41 FLAG    5      5 CHECK_AGAIN 
58 2016-12-15 16:04:45 FLAG    4      6 CHECK_AGAIN 
85 2016-12-15 16:07:02 FLAG    7      3 CHECK_AGAIN 
> 
+0

Спасибо за ответ.Это отличное решение. Я думаю, что следующая строка должна быть изменена следующим образом: 'df $ check_again <- ifelse (df $ Check ==" PASS "," OK ", ifelse (df $ Time_Difference <60 & df $ consecutive_flag_count> = wish_rep," CHECK_AGAIN " , «ОК»)) ', чтобы мы получили правильный ответ. – ahoosh

+0

Отличный звонок, @bikhaab. Обновление ответа сейчас. –

+0

Ваше решение очень быстро. Но я не получаю тот же результат, который я получил из цикла for. Я расследую, почему! – ahoosh