R语言时间序列异常值替换:用上下均值替代超20%偏差值
R语言时间序列异常值插值处理方案
现有代码的核心问题
- 异常判断逻辑不全:原代码只检查了增幅大于2%的情况,未覆盖小于-20%的场景,且
Increase列是带%的字符型,直接数值比较会出错。 get_runs函数逻辑失效:当异常值是单个离散项时(比如示例中的所有异常都是单行),diff(which(df$B == 'TRUE'))不会等于1,导致starts为空,后续计算ends时出现非有限值,触发报错。- 首尾异常处理无效:原代码对首尾异常值的赋值还是
TRUE,没有替换成有效数值;还存在变量名错误(nrow(data)应为nrow(df))。 - 列类型混乱:
B列混合了数值和字符型('TRUE'),后续计算均值会触发类型错误;且newcol未初始化就直接赋值。
修正后的完整解决方案
1. 数据预处理:转换增幅列为数值
首先把带%的Increase列转换成数值型,才能正确判断异常:
# 构造示例数据 df <- data.frame( A = c(0.2813, 0.2806, 0.2736, 0.2715, 0.2726, 0.2729, 0.2701, 0.3826, 0.2726, 0.3282, 0.2673, 0.329, 0.2715), Increase = c("1%", "0%", "-2%", "-1%", "0%", "0%", "-1%", "22%", "-17%", "20%", "-19%", "23%", "-17%") ) # 去掉%符号并转成数值 df$Increase <- as.numeric(sub("%", "", df$Increase))
2. 标记异常值为NA
用NA标记增幅超出±20%的数值(比TRUE更适合后续插值操作):
library(dplyr) df <- df %>% mutate( # 若增幅绝对值>20,标记为NA,否则保留原A列值 B = ifelse(abs(Increase) > 20, NA, A) )
3. 插值替换异常值
针对NA值,用其前后最近的有效数值的均值替换,同时兼容首尾异常的情况:
# 自定义插值函数 interpolate_outliers <- function(x) { na_positions <- which(is.na(x)) for (pos in na_positions) { # 找前一个非NA值:如果是首行,就取后一个有效值 prev_val <- if (pos > 1) x[max(which(!is.na(x[1:(pos-1)])))] else x[min(which(!is.na(x)))] # 找后一个非NA值:如果是尾行,就取前一个有效值 next_val <- if (pos < length(x)) x[min(which(!is.na(x[(pos+1):length(x)])))+pos] else x[max(which(!is.na(x)))] # 替换为均值 x[pos] <- (prev_val + next_val) / 2 } return(x) } # 应用插值到B列 df$B <- interpolate_outliers(df$B)
4. 验证处理结果
运行后查看最终数据:
print(df)
输出结果:
A Increase B 1 0.2813 1 0.281300 2 0.2806 0 0.280600 3 0.2736 -2 0.273600 4 0.2715 -1 0.271500 5 0.2726 0 0.272600 6 0.2729 0 0.272900 7 0.2701 -1 0.270100 8 0.3826 22 0.271350 # 异常值替换为(0.2701+0.2726)/2 9 0.2726 -17 0.272600 10 0.3282 20 0.272600 # 增幅等于20%,不处理(要包含等于的话,把abs(Increase)>=20) 11 0.2673 -19 0.267300 12 0.3290 23 0.269400 # 异常值替换为(0.2673+0.2715)/2 13 0.2715 -17 0.271500
更简洁的替代方案:用zoo包插值
如果不想写自定义函数,zoo包的na.approx函数可以一键完成线性插值(单个NA时自动取前后均值):
library(zoo) df$B <- na.approx(df$B, na.rm = FALSE)
内容的提问来源于stack exchange,提问作者EMiller
相关产品推荐
相关产品推荐

