R исправление противоречивых записей данных - PullRequest
0 голосов
/ 06 февраля 2019

Я записываю четыре переменные каждые 5 минут.Когда я строю временной ряд в R из четырех переменных, я понимаю, что переменная 3 записывает противоречивые данные из-за ошибки в сборе данных (ошибка устройства записи / ошибка датчика).Как я могу исправить записи данных?

Записи данных переменной 3 показывают некоторые ненормальные скачки, и это не физический эффект исследуемой переменной.Изображение показывает одну неделю записи данных с ежедневными колебаниями.Между двумя показаниями подряд не должно быть таких высоких прыжков.Некоторое время назад я попробовал какой-то пакет R-выбросов, но с ним не получилось ...

enter image description here

Когда я строю весь временной ряд, результатхуже.

enter image description here

Любая помощь будет принята с благодарностью.Спасибо

Я делюсь данными с изображения 1:

Рисунок 1 CSV данных

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

После прочтения ваших данных и построения графиков я вижу это:

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» , то есть: значенияниже "в 1,5 раза IQR ниже нижнего квартиля" (и аналогично выше, хотя и не представлен в этих данных) можно смело считать выбросами.

(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::gathertidyr::spread);если вы используете базовую графику, то вам может не потребоваться изменение формы данных (что предполагает предпочтительную замену данных для каждого столбца).

0 голосов
/ 06 февраля 2019

Вот возможное решение, но сначала нам нужно сгенерировать некоторые данные, которые представляют вашу проблему.Что хорошо в вашем сценарии, так это то, что ложные точки данных представляют собой большие всплески, которые довольно очевидны даже визуально.

Генерация данных

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 единиц должно быть достаточно).

Код функцииis:

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 в New.df

...