¿Cómo puedo resaltar parches ruidosos en una serie de tiempo?

9

Tengo muchos datos de series temporales: niveles y velocidades del agua frente al tiempo. Es la salida de una simulación de modelo hidráulico. Como parte del proceso de revisión para confirmar que el modelo está funcionando como se esperaba, tengo que trazar cada serie de tiempo para asegurarme de que no haya "bamboleo" en los datos (ver ejemplo de bamboleo menor a continuación). Usar la interfaz de usuario del software de modelado es una forma bastante lenta y laboriosa de verificar estos datos. Por lo tanto, escribí una macro VBA corta para importar varios bits de datos del modelo, incluidos los resultados en Excel y trazarlos todos a la vez. Espero escribir otra macro VBA corta para analizar los datos de series de tiempo y resaltar cualquier sección que sea sospechosa.

Mi único pensamiento hasta ahora es que podría hacer un análisis sobre la pendiente de los datos. Cualquier lugar donde la pendiente cambie rápidamente de positivo a negativo varias veces dentro de una ventana de búsqueda dada podría clasificarse como inestable. ¿Me estoy perdiendo algún truco más simple? Esencialmente, una simulación "estable" debería proporcionar una curva muy suave. Es probable que cualquier cambio repentino sea el resultado de una inestabilidad en los cálculos.

Ejemplo de inestabilidad menor

davehughes87
fuente
1
Lea el libro EDA de Tukey para obtener un conjunto de métodos simples. Al principio del libro, por ejemplo, describe suavizadores simples y su uso para obtener residuos. Un seguimiento continuo de los residuos absolutos trazaría la variabilidad local de sus curvas, yendo alto donde tiene cambios rápidos, repentinos o periféricos, y de lo contrario se mantendrá bajo. Son posibles muchos métodos mucho más sofisticados, pero quizás esto sea suficiente. Los suavizadores de Tukey son relativamente fáciles de codificar en VBA: lo he hecho .
whuber
@whuber ¿Esto es esencialmente el poder del filtro deslizante de paso alto?
ameba
@amoeba Quizás. Entiendo que tales filtros no son completamente locales y definitivamente no son robustos, mientras que los suavizadores de Tukey tienen estas dos propiedades importantes. (Hoy en día, las personas usan Loess o GAM para suavizar, lo cual está bien, pero son mucho menos fáciles de implementar.)
whuber

Respuestas:

10

1αα

Figura

1201α=0.201

αα0.20α0.20

Los detalles de la suavidad no importan mucho. En este ejemplo, un loess suavizar (implementado en Rque loesscon span=0.05localizarla) fue utilizado, pero incluso una ventana media tendría bien hecho. Para suavizar los residuos absolutos ejecuté una media de ventana de ancho 17 (aproximadamente 24 minutos) seguida de una mediana de ventana. Estos suavizados en ventanas son relativamente fáciles de implementar en Excel. Una implementación eficiente de VBA (para versiones anteriores de Excel, pero el código fuente debería funcionar incluso en versiones nuevas) está disponible en http://www.quantdec.com/Excel/smoothing.htm .


R Código

#
# Emulate the data in the plot.
#
xy <- matrix(c(0, 96.35,  0.3, 96.6, 0.7, 96.7, 1, 96.73, 1.5, 96.74, 2.5, 96.75, 
               4, 96.9, 5, 97.05, 7, 97.5, 10, 98.5, 12, 99.3, 12.5, 99.35, 
               13, 99.355, 13.5, 99.36, 14.5, 99.365, 15, 99.37, 15.5, 99.375, 
               15.6, 99.4, 15.7, 99.41, 20, 99.5, 25, 99.4, 27, 99.37),
             ncol=2, byrow=TRUE)
n <- 401
set.seed(17)
noise.x <- cumsum(rexp(n, n/max(xy[,1])))
noise.y <- rep(c(-1,1), ceiling(n/2))[1:n]
noise.amp <- runif(n, 0.8, 1.2) * 0.04
noise.amp <- noise.amp * ifelse(noise.x < 16 | noise.x > 24.5, 0.05, 1)
noise.y <- noise.y * noise.amp

g <- approxfun(noise.x, noise.y)
f <- splinefun(xy[,1], xy[,2])
x <- seq(0, max(xy[,1]), length.out=1201)
y <- f(x) + g(x)
#
# Plot the data and a smooth.
#
par(mfrow=c(1,2))
plot(range(xy[,1]), range(xy[,2]), type="n", main="Data", sub="With Smooth",
     xlab="Time (hours)", ylab="Water Level")
abline(h=seq(96, 100, by=0.5), col="#e0e0e0")
abline(v=seq(0, 30, by=5), col="#e0e0e0")
#curve(f(x) + g(x), xlim=range(xy[,1]), col="#2070c0", lwd=2, add=TRUE, n=1201)
lines(x,y, type="l", col="#2070c0", lwd=2)

span <- 0.05
fit <- loess(y ~ x, span=span)
y.hat <- predict(fit)
lines(fit$x, y.hat)
#
# Plot the absolute residuals to the smooth.
#
r <-  abs(resid(fit))
plot(fit$x, r, type="l", col="#808080",
     main="Absolute Residuals", sub="With Smooth and a Threshold",
     xlab="Time hours", ylab="Residual Water Level")
#
# Smooth plot an indicator of the smoothed residuals.
#
library(zoo)
smooth <- function(x, window=17) {
  x.1 <- rollapply(ts(x), window, mean)
  x.2 <- rollapply(x.1, window, median)
  return(as.vector(x.2))
}
alpha <- 0.2
threshold <- quantile(r, 1-alpha)
abline(h=threshold, lwd=2, lty=3)
r.hat <- smooth(r >threshold)
x.hat <- smooth(fit$x)
z <- max(r)/2 * (r.hat > alpha)
lines(x.hat, z, lwd=2, col="#c02020")
par(mfrow=c(1,1))
whuber
fuente
1
+1. ¿Raspó de alguna manera los datos de la trama del OP?
ameba
2
@Amoeba Eso sería demasiado problema, especialmente para los bits ondulantes después de 15 horas. Observé una docena de puntos en la curva, dibujé una spline, inserté algunos puntos intermedios para eliminar los picos extraños que puede producir una spline, y agregué un error correlacionado fuertemente negativo heteroscedastic. Todo el proceso tomó solo unos minutos y resultó en un conjunto de datos cualitativamente como el que se muestra en la pregunta.
whuber
¡Me preguntaba cómo obtuviste los datos de mi trama! ¡Salud! Voy a darle una oportunidad.
davehughes87
FWIW, publiqué el código que usé para hacer la ilustración. Aunque no es VBA, tal vez aclarará los detalles. (cc @amoeba)
whuber