R纠正不一致的数据记录

时间:2019-02-06 02:49:23

标签: r plot outliers

我每5分钟记录四个变量。当我在四个变量的R中绘制时间序列时,我意识到变量3由于数据收集中的错误(记录设备错误/传感器错误)而记录的数据不一致。如何纠正数据记录?

变量3的数据记录显示一些异常跳跃,这不是所研究变量的物理效应。该图显示了一周的数据记录,每天都有波动。连续两个读数之间不应有如此高的跳变。 我前段时间尝试了一些R离群值软件包,但没有结果...

enter image description here

当我绘制整个时间序列时,结果会更糟。

enter image description here

任何帮助将不胜感激。 谢谢

我分享了图片1中的数据:

Figure 1 csv data

2 个答案:

答案 0 :(得分:2)

读完您的数据并作图后,我看到了:

df <- read.csv("~/StackOverflow/RaülOo.csv")
df$TIMESTAMP <- as.POSIXct(df$TIMESTAMP)
library(dplyr)
library(tidyr)
library(ggplot2)
gather(df, k, v, -X, -TIMESTAMP) %>%
  ggplot(aes(TIMESTAMP, v, color=k)) +
  geom_path()

unfiltered

是否简单到“高于-50”?十分之类的样子:

quantile(unlist(df[,3:6]), seq(0,1,len=11))
#        0%       10%       20%       30%       40%       50%       60%       70% 
# -122.7000  -22.9600  -17.5500  -13.4200  -10.0700   -5.9615    3.4800   16.0500 
#       80%       90%      100% 
#   26.6040   35.6860   81.4000 

IQR大约为37。类似于箱线图中的“胡须”,假设"1.5 IQR"可能是现实的,即:值低于“下四分位以下IQR的1.5倍”(同样高于,但不超过存在于此数据中)可以安全地认为是异常值。

(q <- quantile(unlist(df[,3:6]), c(0.25, 0.75)))
#      25%      75% 
# -15.4000  22.0025 
unname( q[1] - 1.5*diff(q) ) # "unname" only to remove the now-misleading percentile label
# -71.50375 
gather(df, k, v, -X, -TIMESTAMP) %>%
  filter(v > q[1] - 1.5*diff(q)) %>%
  ggplot(aes(TIMESTAMP, v, color=k)) +
  geom_path()

partially filtered

因此1.5可能不足以真正识别异常值,但这取决于您的需求。如果您只需要一个清理过的图(并且有些异常值不会使您衰弱),那么我建议使用标准的“ 1.5倍IQR”就足够了。如果您想对其进行更多控制,也许可以使用更接近1的值。

gather(df, k, v, -X, -TIMESTAMP) %>%
  filter(v > q[1] - diff(q)) %>%
  ggplot(aes(TIMESTAMP, v, color=k)) +
  geom_path()

filtered

如果您需要“宽”格式的纸,可以这样做:

gather(df, k, v, -X, -TIMESTAMP) %>%
  filter(v > -50) %>%
  spread(k, v) %>%
  slice(37:43) # just for demonstration
#    X           TIMESTAMP   four    one  three    two
# 1 37 2018-07-15 03:05:00 -21.68 -32.04 -23.11 -12.87
# 2 38 2018-07-15 03:10:00 -21.79 -31.71 -23.11 -12.87
# 3 39 2018-07-15 03:15:00 -21.79 -31.71 -23.11 -12.87
# 4 40 2018-07-15 03:20:00 -21.79 -31.71 -23.11 -12.87
# 5 41 2018-07-15 03:25:00 -17.43 -25.37     NA -10.29
# 6 42 2018-07-15 03:30:00 -21.79 -31.71 -23.11 -12.87
# 7 43 2018-07-15 03:35:00 -21.79 -31.28 -23.11 -12.87

您的异常值现在为NA的位置。更为简洁的非dplyr / tidyr替代方案可能是:

df[,3:6] <- lapply(df[,3:6], function(a) ifelse(a < -50, NA, a))

,然后您进行的任何后续处理或绘图都需要考虑(忽略)NA值。


我要走的更远,因为(对您来说)了解不良数据的发送频率(或定期)可能很有趣。

newdat <- df %>%
  gather(k, v, -X, -TIMESTAMP) %>%
  mutate(v = if_else(v < q[1] - diff(q), NA_real_, v))
baddat <- filter(newdat, is.na(v))
newdat <- filter(newdat, !is.na(v))
baddat$v <- min(newdat$v) - 5 # arbitrary

ggplot(newdat, aes(TIMESTAMP, v, color = k)) +
  geom_path() +
  geom_point(data = baddat)

filtered with outlier dots

在这里您可以查看问题数据点的位置,而无需扩展图表的其余部分。


注释

  • 这似乎是帮助您入门的快速技巧。例如,如果不是四个均一的量表在很大程度上不同,而不是同质的,则需要按列进行。

  • 尽管并非严格要求使用dplyr进行数据处理。这可以在base-R中使用相对简单的功能轻松完成。使用ggplot2强制使用长数据,因此要使用tidyr::gather(和tidyr::spread);如果您使用的是基本图形,则可能不需要重整数据的形状(这建议使用每列数据替换)。

答案 1 :(得分:1)

这是一个可能的解决方案,但首先我们需要生成一些代表您的问题的数据。这种情况的好处是,虚假数据点是很大的峰值,即使在视觉上也很明显。

生成数据

set.seed(15161)
x <- seq(pi/10,10*pi,by=pi/100)
y <- sin(x) # using sin() generates some osciliating data
z <- sample(c(0,-5),length(y),
            prob=c(0.99,0.01),replace=TRUE) # pepper the data with random spikes
y <- y + z
df <- data.frame(cbind(x,y,z))
length(which(df$z==-5)) # the number of spikes ~ 13
plot(df$x,df$y,type="l",ylim=c(-10,2),col="blue",xlab="x",ylab="y")
abline(h=0,lty=5)

enter image description here

删除杂散测量(清除数据)

在您提供的数据中,与良好测量的背景相比,杂散数据点非常大。也就是说,您的测量以缓慢的增加或减少的方式很好地移动,然后使跳跃/下降幅度大于20个单位。因此,我编写了一个函数,该函数可以发现并删除表示超出某个阈值(在您的情况下为〜20个单位,在我的工作示例中为〜2个单位以上)的任何数据点。

功能代码为:

f <- function(df,clean,threshold){
  y <- df[,clean]
  for(i in 1:length(y)){
    if(is.na(y[i]) | is.na(y[i+1])){
      next
    }
    if(abs(y[i+1]-y[i])>threshold){
      y[i+1] <- NA
    }
  }
  return(df[!is.na(y),])
}
cleaned.df <- f(df,clean="y",threshold=2) # Run the function to clean the data
length(which(cleaned.df$z==-5)) # number of spikes in cleaned data is now 0

绘制清理结果

plot(cleaned.df$x,cleaned.df$y,type="l",ylim=c(-10,2),col="blue",xlab="x",ylab="y")
abline(h=0,lty=5)

enter image description here

注释和警告

  1. 在运行该功能(即按时间顺序排序的测量)之前,请确保数据已顺序排序
  2. 我建议您选择20个单位左右的阈值(仅通过肉眼检查图形就可以了。
  3. 清洁功能可能无法有效去除2个或更多连续的尖峰。 但是,您可以通过清理功能多次运行数据,这应该可以工作。
  4. 我们可以设计更严格的方法,但我认为这种解决方案将简单有效。让我们知道您是否还有问题,我们可以开发更严格的解决方案。

编辑1:

我刚刚看到您上传了一些实际数据。略微调整功能以适应会改变测量符号的尖峰。这是适用于您数据的结果,看起来对我有用。

df <- read.csv("figure1data.csv")
plot(df$X,df$three,type="l",col="blue",xlab="x",ylab="y",ylim=c(-150,50))
    abline(h=0,lty=5)

enter image description here

cleaned.df1 <- f(df,clean="three",threshold=20)
plot(cleaned.df1$X,cleaned.df1$three,type="l",col="blue",xlab="x",ylab="y",
     ylim=c(-150,50))
abline(h=0,lty=5)

enter image description here

编辑2:对OP评论的回复

要消除连续出现尖峰的情况,只需对清除的数据重新运行该功能。

cleaned.df2 <- f(cleaned.df1,clean="three",threshold=20)

要将所有行恢复到数据并将尖峰变量“三个”点转换为NA,只需按如下所示合并数据即可。

New.df <- merge(df[,colnames(df)!="three"],
               cleaned.df2[,colnames(df) %in% c("X","three")],
               by="X",all.x=TRUE)

要检查一切是否按预期进行

df[which(!complete.cases(New.df)),] 
New.df[which(!complete.cases(New.df)),]

您清楚地看到,NA的{​​{1}}中的变量“三个”尖峰现在位于