Anstatt zu versuchen, die Zeitreihen explizit zu zerlegen, würde ich stattdessen vorschlagen, dass Sie die Daten räumlich-zeitlich modellieren, da, wie Sie unten sehen werden, der langfristige Trend wahrscheinlich räumlich variiert und der saisonale Trend mit dem langfristigen Trend variiert und räumlich.
Ich habe festgestellt, dass generalisierte additive Modelle (GAMs) ein gutes Modell für die Anpassung unregelmäßiger Zeitreihen sind, wie Sie sie beschreiben.
Im Folgenden zeige ich ein schnelles Modell, das ich für die vollständigen Daten des folgenden Formulars vorbereitet habe
E ( yich)= α + f1( ToDich) + f2( DoYich) + f3( Jahrich) + f4( xich, yich) +f5( DoYich, Jahrich) + f6( xich, yich, ToDich) +f7( xich, yich, DoYich) + f8( xich, yich,Jahrich)
wo
- α ist der Modellabschnitt,
- f1( ToDich) ist eine reibungslose Funktion der Tageszeit.
- f2( DoYich) ist eine glatte Funktion des Tages des Jahres,
- f3( Jahrich) ist eine glatte Funktion des Jahres,
- f4( xich, yich) ist eine 2D-Glättung von Längen- und Breitengrad.
- f5( DoYich, Jahrich) ist ein Tensorprodukt, das Tag und Jahr
- f6( xich, yich, ToDich) Tensorprodukt glatt von Ort und Tageszeit
- f7( xich, yich, DoYich) Tensorprodukt glatt vom Ort Tag des Jahres &
- f8( xich, yich, Jahrich Tensorprodukt glatt von Ort und Jahr
Tatsächlich sind die ersten vier Glättungen die Haupteffekte von
- Uhrzeit,
- Jahreszeit,
- langfristiger Trend,
- räumliche Variation
während die verbleibenden vier Tensorprodukte das Modell glatter Wechselwirkungen zwischen den angegebenen Kovariaten glätten, welches Modell
- wie sich das saisonale Temperaturmuster im Laufe der Zeit ändert,
- wie sich der Tageszeiteffekt räumlich ändert,
- wie der saisonale Effekt räumlich variiert, und
- wie der langfristige Trend räumlich variiert
Die Daten werden in R geladen und mit dem folgenden Code ein wenig massiert
library('mgcv')
library('ggplot2')
library('viridis')
theme_set(theme_bw())
library('gganimate')
galveston <- read.csv('gbtemp.csv')
galveston <- transform(galveston,
datetime = as.POSIXct(paste(DATE, TIME),
format = '%m/%d/%y %H:%M', tz = "CDT"))
galveston <- transform(galveston,
STATION_ID = factor(STATION_ID),
DoY = as.numeric(format(datetime, format = '%j')),
ToD = as.numeric(format(datetime, format = '%H')) +
(as.numeric(format(datetime, format = '%M')) / 60))
Das Modell selbst wird mithilfe der bam()
Funktion angepasst, mit der GAMs an größere Datensätze wie diesen angepasst werden können. Sie können gam()
dieses Modell auch verwenden, aber es dauert etwas länger, bis es passt.
knots <- list(DoY = c(0.5, 366.5))
M <- list(c(1, 0.5), NA)
m <- bam(MEASUREMENT ~
s(ToD, k = 10) +
s(DoY, k = 30, bs = 'cc') +
s(YEAR, k = 30) +
s(LONGITUDE, LATITUDE, k = 100, bs = 'ds', m = c(1, 0.5)) +
ti(DoY, YEAR, bs = c('cc', 'tp'), k = c(15, 15)) +
ti(LONGITUDE, LATITUDE, ToD, d = c(2,1), bs = c('ds','tp'),
m = M, k = c(20, 10)) +
ti(LONGITUDE, LATITUDE, DoY, d = c(2,1), bs = c('ds','cc'),
m = M, k = c(25, 15)) +
ti(LONGITUDE, LATITUDE, YEAR, d = c(2,1), bs = c('ds','tp'),
m = M), k = c(25, 15)),
data = galveston, method = 'fREML', knots = knots,
nthreads = 4, discrete = TRUE)
Die s()
Bedingungen sind die wichtigsten Effekte, während die ti()
Begriffe Tensorproduktes sind Interaktion glättet , wo die wichtigsten Auswirkungen der genannten Kovariablen wurden von der Basis entfernt. Diese ti()
Glättungen sind eine Möglichkeit, Wechselwirkungen der angegebenen Variablen numerisch stabil einzubeziehen.
Das knots
Objekt legt nur die Endpunkte der zyklischen Glättung fest, die ich für den Tag des Jahres-Effekts verwendet habe. Wir möchten, dass 23:59 am 31. Dezember reibungslos mit 00:01 am 1. Januar verbunden werden. Dies macht zum Teil Schaltjahre aus.
Die Modellzusammenfassung zeigt, dass alle diese Effekte signifikant sind.
> summary(m)
Family: gaussian
Link function: identity
Formula:
MEASUREMENT ~ s(ToD, k = 10) + s(DoY, k = 12, bs = "cc") + s(YEAR,
k = 30) + s(LONGITUDE, LATITUDE, k = 100, bs = "ds", m = c(1,
0.5)) + ti(DoY, YEAR, bs = c("cc", "tp"), k = c(12, 15)) +
ti(LONGITUDE, LATITUDE, ToD, d = c(2, 1), bs = c("ds", "tp"),
m = list(c(1, 0.5), NA), k = c(20, 10)) + ti(LONGITUDE,
LATITUDE, DoY, d = c(2, 1), bs = c("ds", "cc"), m = list(c(1,
0.5), NA), k = c(25, 12)) + ti(LONGITUDE, LATITUDE, YEAR,
d = c(2, 1), bs = c("ds", "tp"), m = list(c(1, 0.5), NA),
k = c(25, 15))
Parametric coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.75561 0.07508 289.8 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Approximate significance of smooth terms:
edf Ref.df F p-value
s(ToD) 3.036 3.696 5.956 0.000189 ***
s(DoY) 9.580 10.000 3520.098 < 2e-16 ***
s(YEAR) 27.979 28.736 59.282 < 2e-16 ***
s(LONGITUDE,LATITUDE) 54.555 99.000 4.765 < 2e-16 ***
ti(DoY,YEAR) 131.317 140.000 34.592 < 2e-16 ***
ti(ToD,LONGITUDE,LATITUDE) 42.805 171.000 0.880 < 2e-16 ***
ti(DoY,LONGITUDE,LATITUDE) 83.277 240.000 1.225 < 2e-16 ***
ti(YEAR,LONGITUDE,LATITUDE) 84.862 329.000 1.101 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
R-sq.(adj) = 0.94 Deviance explained = 94.2%
fREML = 29807 Scale est. = 2.6318 n = 15276
Eine genauere Analyse würde prüfen wollen, ob wir all diese Interaktionen benötigen. Einige der räumlichen ti()
Begriffe erklären nur geringe Abweichungen in den Daten, wie in der Statistik angegeben. Es gibt hier viele Daten, so dass selbst kleine Effektgrößen statistisch signifikant, aber uninteressant sein können.F.
Zur schnellen Überprüfung führt das Entfernen der drei räumlichen ti()
Glättungen ( m.sub
) jedoch zu einer deutlich schlechteren Anpassung, wie von AIC bewertet:
> AIC(m, m.sub)
df AIC
m 447.5680 58583.81
m.sub 239.7336 59197.05
Mit der plot()
Methode können wir die Teileffekte der ersten fünf Glättungen darstellen - die Glättungen des 3D-Tensorprodukts können nicht einfach und nicht standardmäßig dargestellt werden.
plot(m, pages = 1, scheme = 2, shade = TRUE, scale = 0)
Das scale = 0
Argument dort setzt alle Diagramme auf ihre eigene Skala, um die Größen der Effekte zu vergleichen, können wir dies ausschalten:
plot(m, pages = 1, scheme = 2, shade = TRUE)
Jetzt können wir sehen, dass der saisonale Effekt dominiert. Der langfristige Trend (im Durchschnitt) ist in der oberen rechten Darstellung dargestellt. Um den langfristigen Trend wirklich zu betrachten, müssen Sie jedoch eine Station auswählen und dann aus dem Modell für diese Station eine Vorhersage treffen, wobei die Tageszeit und der Tag des Jahres auf einige repräsentative Werte festgelegt werden (Mittag, für einen Tag des Jahres im Sommer) sagen). Dort weist das frühe oder zweite Jahr der Serie einige niedrige Temperaturwerte im Vergleich zu den übrigen Aufzeichnungen auf, die wahrscheinlich in allen beteiligten Glättungen erfasst werden YEAR
. Diese Daten sollten genauer betrachtet werden.
Dies ist nicht wirklich der richtige Ort, um darauf einzugehen, aber hier sind einige Visualisierungen der Modellanpassungen. Zuerst betrachte ich das räumliche Temperaturmuster und wie es sich über die Jahre der Serie ändert. Um dies zu tun, sage ich aus dem Modell für ein 100x100-Raster über dem räumlichen Bereich um 12.00 Uhr am Tag 180 eines jeden Jahres voraus:
pdata <- with(galveston,
expand.grid(ToD = 12,
DoY = 180,
YEAR = seq(min(YEAR), max(YEAR), by = 1),
LONGITUDE = seq(min(LONGITUDE), max(LONGITUDE), length = 100),
LATITUDE = seq(min(LATITUDE), max(LATITUDE), length = 100)))
fit <- predict(m, pdata)
dann habe ich eingestellt NA
, dass die vorhergesagten Werte fit
für alle Datenpunkte fehlen, die in einiger Entfernung von den Beobachtungen liegen (proportional; dist
)
ind <- exclude.too.far(pdata$LONGITUDE, pdata$LATITUDE,
galveston$LONGITUDE, galveston$LATITUDE, dist = 0.1)
fit[ind] <- NA
und verbinden Sie die Vorhersagen mit den Vorhersagedaten
pred <- cbind(pdata, Fitted = fit)
Wenn Sie die vorhergesagten Werte NA
so einstellen, können wir nicht mehr über die Unterstützung der Daten hinaus extrapolieren.
Verwenden von ggplot2
ggplot(pred, aes(x = LONGITUDE, y = LATITUDE)) +
geom_raster(aes(fill = Fitted)) + facet_wrap(~ YEAR, ncol = 12) +
scale_fill_viridis(name = expression(degree*C), option = 'plasma',
na.value = 'transparent') +
coord_quickmap() +
theme(legend.position = 'top', legend.key.width = unit(2, 'cm'))
wir erhalten folgendes
Wir können die Temperaturschwankungen von Jahr zu Jahr etwas detaillierter sehen, wenn wir die Handlung eher animieren als facettieren
p <- ggplot(pred, aes(x = LONGITUDE, y = LATITUDE, frame = YEAR)) +
geom_raster(aes(fill = Fitted)) +
scale_fill_viridis(name = expression(degree*C), option = 'plasma',
na.value = 'transparent') +
coord_quickmap() +
theme(legend.position = 'top', legend.key.width = unit(2, 'cm'))+
labs(x = 'Longitude', y = 'Latitude')
gganimate(p, 'galveston.gif', interval = .2, ani.width = 500, ani.height = 800)
Um die langfristigen Trends genauer zu betrachten, können wir für bestimmte Stationen Vorhersagen treffen. Für STATION_ID
13364 und Vorhersagen für Tage in den vier Quartalen können wir beispielsweise Folgendes verwenden, um Werte der Kovariaten zu erstellen, um die wir vorhersagen möchten (Mittag, am Tag des Jahres 1, 90, 180 und 270, an der ausgewählten Station) und Bewertung des langfristigen Trends bei 500 gleich beabstandeten Werten)
pdata <- with(galveston,
expand.grid(ToD = 12,
DoY = c(1, 90, 180, 270),
YEAR = seq(min(YEAR), max(YEAR), length = 500),
LONGITUDE = -94.8751,
LATITUDE = 29.50866))
Dann sagen wir Standardfehler voraus und fordern sie an, um ein ungefähres punktweises 95% -Konfidenzintervall zu bilden
fit <- data.frame(predict(m, newdata = pdata, se.fit = TRUE))
fit <- transform(fit, upper = fit + (2 * se.fit), lower = fit - (2 * se.fit))
pred <- cbind(pdata, fit)
was wir planen
ggplot(pred, aes(x = YEAR, y = fit, group = factor(DoY))) +
geom_ribbon(aes(ymin = lower, ymax = upper), fill = 'grey', alpha = 0.5) +
geom_line() + facet_wrap(~ DoY, scales = 'free_y') +
labs(x = NULL, y = expression(Temperature ~ (degree * C)))
produzieren
Natürlich ist die Modellierung dieser Daten viel mehr als das, was ich hier zeige, und wir möchten die verbleibende Autokorrelation und Überanpassung der Splines überprüfen, aber die Annäherung an das Problem als Modellierung der Merkmale der Daten ermöglicht eine detailliertere Darstellung Untersuchung der Trends.
Sie könnten natürlich jedes STATION_ID
einzeln modellieren , aber das würde Daten wegwerfen, und viele Stationen haben nur wenige Beobachtungen. Hier leiht sich das Modell alle Stationsinformationen aus, um die Lücken zu schließen und die Schätzung der interessierenden Trends zu unterstützen.
Einige Anmerkungen zu bam()
Das bam()
Modell verwendet alle Tricks von mgcv , um das Modell schnell (mehrere Threads 4 ), die schnelle Auswahl der REML-Glätte ( method = 'fREML'
) und die Diskretisierung von Kovariaten abzuschätzen . Wenn diese Optionen aktiviert sind, passt das Modell in weniger als einer Minute auf meine 2013er Dual-4-Core-Xeon-Workstation mit 64 GB RAM.
Eine Möglichkeit wäre die Verwendung einer Regressionsstrategie, die Ihre Daten als Ziehungen aus einer kontinuierlichen zugrunde liegenden Funktion behandelt (siehe Diskussion hier: Gibt es einen Goldstandard für die Modellierung unregelmäßig beabstandeter Zeitreihen? ). Von dort aus können Sie das Signal mit einer Methode wie der Singular Spectrum Analysis zerlegen (für R: https://cran.r-project.org/web/packages/Rssa/index.html ).
quelle