记住序列的最后一个 "correct" 值(用于去除异常值)
Memorize the last "correct" value of a sequence (for removing outliers)
我在函数中遇到了一个小问题。
它的目的是删除我在 data.frame 中检测到的异常值。当与之前的正确值差异太大时(例如 c(1,2,3,20,30,4,5,6)
:“20”和“30”是异常值),它们就会被检测到。但是我的数据比这复杂得多。
我的想法是将我列的前两个数值视为 "correct"。然后,我想测试每个下一个值:
- 如果测试值与前一个值的差<20,那么它是一个新的正确值,测试必须从这个新的正确值开始(而不是从前一个正确值开始)
- 如果相同的差异>20,那么它是错误的。必须在错误值旁边放置一个索引,并且测试仍必须从同一个正确值继续,直到检测到新的正确值
这是我的函数和假 DF 的示例:
myts <- data.frame(x=c(12,12,35,39,46,45,33,5,26,28,29,34,15,15),z=NA)
test <- function(x){
st1 = NULL
temp <- st1[1] <- x[1]
st1 <- numeric(length(x))
for (i in 2:(length(x))){
if((!is.na(x[i])) & (!is.na(x[i-1]))& (abs((x[i])-(temp)) > 20)){
st1[i] <- 1
} }
return(st1)
}
myts[,2] <- apply(as.data.frame(myts[,1]),2,test)
myts[,2] <- as.numeric(myts[,2])
它几乎完成了工作,但问题是没有记住最后一个正确的值。它仍然从第一个正确的值开始进行测试。
因此,未检测到我示例中的第 9 行到第 11 行。我让你想象一下 500,000 行上的问题 data.frame.
我该如何解决这个小问题?其余功能可能没问题。
您只需更新 temp
任何非异常值的指数:
test <- function(x) {
temp <- x[1]
st1 <- numeric(length(x))
for (i in 2:(length(x))){
if(!is.na(x[i]) & !is.na(x[i-1]) & abs(x[i]-temp) > 20) {
st1[i] <- 1
} else {
temp <- x[i]
}
}
return(st1)
}
myts[,2] <- apply(as.data.frame(myts[,1]),2,test)
myts[,2] <- as.numeric(myts[,2])
myts
# x z
# 1 12 0
# 2 12 0
# 3 35 1
# 4 39 1
# 5 46 1
# 6 45 1
# 7 33 1
# 8 5 0
# 9 26 1
# 10 28 1
# 11 29 1
# 12 34 1
# 13 15 0
# 14 15 0
需要注意的一件事是,与向量化函数相比,R 中的 for 循环会非常慢。但是,由于向量中的每个元素都以复杂的方式依赖于之前的元素,因此很难使用 R 的内置向量化函数来有效地计算向量。您可以将此代码几乎逐字地转换为 C++,并使用 Rcpp 包重新获得效率:
library(Rcpp)
test2 <- cppFunction(
"IntegerVector test2(NumericVector x) {
const int n = x.length();
IntegerVector st1(n, 0);
double temp = x[0];
for (int i=1; i < n; ++i) {
if (!R_IsNA(x[i]) && !R_IsNA(x[i]) && fabs(x[i] - temp) > 20.0) {
st1[i] = 1;
} else {
temp = x[i];
}
}
return st1;
}")
all.equal(test(myts[,1]), test2(myts[,1]))
# [1] TRUE
# Benchmark on large vector with some NA values:
set.seed(144)
large.vec <- c(0, sample(c(1:50, NA), 1000000, replace=T))
all.equal(test(large.vec), test2(large.vec))
# [1] TRUE
library(microbenchmark)
microbenchmark(test(large.vec), test2(large.vec))
# Unit: milliseconds
# expr min lq mean median uq max neval
# test(large.vec) 2343.684164 2468.873079 2667.67970 2604.22954 2747.23919 3753.54901 100
# test2(large.vec) 9.596752 9.864069 10.97127 10.23011 11.68708 16.67855 100
Rcpp 代码在长度为 100 万的向量上大约快 250 倍。根据您的用例,这种加速可能重要也可能不重要。
我在函数中遇到了一个小问题。
它的目的是删除我在 data.frame 中检测到的异常值。当与之前的正确值差异太大时(例如 c(1,2,3,20,30,4,5,6)
:“20”和“30”是异常值),它们就会被检测到。但是我的数据比这复杂得多。
我的想法是将我列的前两个数值视为 "correct"。然后,我想测试每个下一个值:
- 如果测试值与前一个值的差<20,那么它是一个新的正确值,测试必须从这个新的正确值开始(而不是从前一个正确值开始)
- 如果相同的差异>20,那么它是错误的。必须在错误值旁边放置一个索引,并且测试仍必须从同一个正确值继续,直到检测到新的正确值
这是我的函数和假 DF 的示例:
myts <- data.frame(x=c(12,12,35,39,46,45,33,5,26,28,29,34,15,15),z=NA)
test <- function(x){
st1 = NULL
temp <- st1[1] <- x[1]
st1 <- numeric(length(x))
for (i in 2:(length(x))){
if((!is.na(x[i])) & (!is.na(x[i-1]))& (abs((x[i])-(temp)) > 20)){
st1[i] <- 1
} }
return(st1)
}
myts[,2] <- apply(as.data.frame(myts[,1]),2,test)
myts[,2] <- as.numeric(myts[,2])
它几乎完成了工作,但问题是没有记住最后一个正确的值。它仍然从第一个正确的值开始进行测试。 因此,未检测到我示例中的第 9 行到第 11 行。我让你想象一下 500,000 行上的问题 data.frame.
我该如何解决这个小问题?其余功能可能没问题。
您只需更新 temp
任何非异常值的指数:
test <- function(x) {
temp <- x[1]
st1 <- numeric(length(x))
for (i in 2:(length(x))){
if(!is.na(x[i]) & !is.na(x[i-1]) & abs(x[i]-temp) > 20) {
st1[i] <- 1
} else {
temp <- x[i]
}
}
return(st1)
}
myts[,2] <- apply(as.data.frame(myts[,1]),2,test)
myts[,2] <- as.numeric(myts[,2])
myts
# x z
# 1 12 0
# 2 12 0
# 3 35 1
# 4 39 1
# 5 46 1
# 6 45 1
# 7 33 1
# 8 5 0
# 9 26 1
# 10 28 1
# 11 29 1
# 12 34 1
# 13 15 0
# 14 15 0
需要注意的一件事是,与向量化函数相比,R 中的 for 循环会非常慢。但是,由于向量中的每个元素都以复杂的方式依赖于之前的元素,因此很难使用 R 的内置向量化函数来有效地计算向量。您可以将此代码几乎逐字地转换为 C++,并使用 Rcpp 包重新获得效率:
library(Rcpp)
test2 <- cppFunction(
"IntegerVector test2(NumericVector x) {
const int n = x.length();
IntegerVector st1(n, 0);
double temp = x[0];
for (int i=1; i < n; ++i) {
if (!R_IsNA(x[i]) && !R_IsNA(x[i]) && fabs(x[i] - temp) > 20.0) {
st1[i] = 1;
} else {
temp = x[i];
}
}
return st1;
}")
all.equal(test(myts[,1]), test2(myts[,1]))
# [1] TRUE
# Benchmark on large vector with some NA values:
set.seed(144)
large.vec <- c(0, sample(c(1:50, NA), 1000000, replace=T))
all.equal(test(large.vec), test2(large.vec))
# [1] TRUE
library(microbenchmark)
microbenchmark(test(large.vec), test2(large.vec))
# Unit: milliseconds
# expr min lq mean median uq max neval
# test(large.vec) 2343.684164 2468.873079 2667.67970 2604.22954 2747.23919 3753.54901 100
# test2(large.vec) 9.596752 9.864069 10.97127 10.23011 11.68708 16.67855 100
Rcpp 代码在长度为 100 万的向量上大约快 250 倍。根据您的用例,这种加速可能重要也可能不重要。