Trend bei unregelmäßigen Zeitreihendaten


8

Ich habe einen Datensatz mit Wassertemperaturmessungen, die über einen Zeitraum von Jahrzehnten in unregelmäßigen Abständen an einem großen Wasserkörper durchgeführt wurden. (Galveston Bay, TX, wenn Sie interessiert sind)

Hier ist der Kopf der Daten:

  STATION_ID     DATE  TIME LATITUDE LONGITUDE YEAR MONTH DAY SEASON MEASUREMENT
1      13296  6/20/91 11:04 29.50889 -94.75806 1991     6  20 Summer        28.0
2      13296  3/17/92  9:30 29.50889 -94.75806 1992     3  17 Spring        20.1
3      13296  9/23/91 11:24 29.50889 -94.75806 1991     9  23   Fall        26.0
4      13296  9/23/91 11:24 29.50889 -94.75806 1991     9  23   Fall        26.0
5      13296  6/20/91 11:04 29.50889 -94.75806 1991     6  20 Summer        28.0
6      13296 12/17/91 10:15 29.50889 -94.75806 1991    12  17 Winter        13.0

(MESSUNG ist die interessierende Temperaturmessung.)

Der vollständige Satz ist hier verfügbar: https://github.com/jscarlton/galvBayData/blob/master/gbtemp.csv

Ich möchte die Auswirkungen saisonaler Schwankungen entfernen, um den Trend (falls vorhanden) der Temperatur im Zeitverlauf zu beobachten. Ist eine Zeitreihenzerlegung der beste Weg, dies zu tun? Wie gehe ich damit um, dass die Messungen nicht in regelmäßigen Abständen durchgeführt wurden? Ich hoffe, es gibt ein R-Paket für diese Art der Analyse, obwohl Python oder Stata auch in Ordnung wären. 

(Hinweis: Bei dieser Analyse ignoriere ich die räumliche Variabilität der Messungen. Idealerweise würde ich das auch berücksichtigen, aber ich denke, dass dies hoffnungslos komplex wäre.)

Antworten:


18

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

  1. Uhrzeit,
  2. Jahreszeit,
  3. langfristiger Trend,
  4. räumliche Variation

während die verbleibenden vier Tensorprodukte das Modell glatter Wechselwirkungen zwischen den angegebenen Kovariaten glätten, welches Modell

  1. wie sich das saisonale Temperaturmuster im Laufe der Zeit ändert,
  2. wie sich der Tageszeiteffekt räumlich ändert,
  3. wie der saisonale Effekt räumlich variiert, und
  4. 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 knotsObjekt 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)

Geben Sie hier die Bildbeschreibung ein

Das scale = 0Argument 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)

Geben Sie hier die Bildbeschreibung ein

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 fitfü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 NAso 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

Geben Sie hier die Bildbeschreibung ein

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)

Geben Sie hier die Bildbeschreibung ein

Um die langfristigen Trends genauer zu betrachten, können wir für bestimmte Stationen Vorhersagen treffen. Für STATION_ID13364 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

Geben Sie hier die Bildbeschreibung ein

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_IDeinzeln 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.


0

Sie können einfach die Funktion verwenden, decomposedie Ihre Zeitreihen in drei Komponenten unterteilt: Trend, Saison und Zufall. Sie können die verschiedenen Werte auch aus dem Ergebnis extrahieren und grafisch darstellen. Stellen Sie sicher, dass Sie Ihre Daten als Zeitreihen definieren. Die Funktion stlmacht im Grunde das Gleiche, bietet Ihnen jedoch mehr Flexibilität bei der Zerlegung Ihrer Daten.

Ich empfehle auch die folgende Website

https://www.otexts.org/fpp/6

Hilft das?


Danke für die Antwort. Ich schaue jetzt durch die Website. Die Herausforderung besteht darin, dass es Datenlücken gibt, die Monate oder sogar ganze Jahreszeiten dauern und in denen keine Daten erfasst wurden, was es mir schwer macht, mich mit dem sich ändernden Zeitfenster auseinanderzusetzen.
Griseus

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.