使用前面的行而不是 for 循环实现应用
Implementing apply using previous rows instead of for loop
我正在尝试避免 for 循环并使用 apply
代替我检测到的 post 处理标志。
我有一个时间序列,其中有一列显示质量是否正常。数据框如下所示:
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])
所以,实际上 for 循环正在获取连续重复 desired_rep
次的标志。如果我们在两个 FLAG
之后有一个 PASS
,则 1 是 Flag_Counter
最后我们做 df[, df$Flag_Counter == 3]
我们可以使用 post-processed 标志。现在,这非常慢。我想知道我们是否可以使用 apply
来加快这项任务。我在 Python
中完成了此操作,但我不知道如何访问我的预定义函数中的前几行,然后使用 apply
。我感谢您的帮助。
试一试:
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
列,如果 Check
是 Pass
或者 Time_Difference
小于 60 并且 return OK
连续 Check
个少于 desired_rep
个。
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
>
试试这个:
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()
在需要按顺序更新 "state" 时很有用,但限制是输入是单个 list/vector(这里,temp
列)。
我正在尝试避免 for 循环并使用 apply
代替我检测到的 post 处理标志。
我有一个时间序列,其中有一列显示质量是否正常。数据框如下所示:
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])
所以,实际上 for 循环正在获取连续重复 desired_rep
次的标志。如果我们在两个 FLAG
之后有一个 PASS
,则 1 是 Flag_Counter
最后我们做 df[, df$Flag_Counter == 3]
我们可以使用 post-processed 标志。现在,这非常慢。我想知道我们是否可以使用 apply
来加快这项任务。我在 Python
中完成了此操作,但我不知道如何访问我的预定义函数中的前几行,然后使用 apply
。我感谢您的帮助。
试一试:
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
列,如果 Check
是 Pass
或者 Time_Difference
小于 60 并且 return OK
连续 Check
个少于 desired_rep
个。
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
>
试试这个:
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()
在需要按顺序更新 "state" 时很有用,但限制是输入是单个 list/vector(这里,temp
列)。