Vad är korskorrelation?
   — Undersökning av en tidsserie

Ingemar Sjöström, SFK-StaM

juni 2025















Bakgrund

Det finns många behov och möjligheter att undersöka en tidsserie och korskorrelation är ett sätt (CCF — cross correlation function). De flesta datorprogram har ett stort avsnitt med metoder för tidsserieanalys. Det är vanligt att beräkna korrelationskoefficienten mellan två variabler, t.ex. längd och vikt hos samma enhet, t.ex. hos en människa.
Korskorrelation beräknar i stället korrelationen mellan mätvärden om den ena tidsserien förskjuts i ‘tidsled’. Kanske man misstänker att värdena i tidsserie A har en korrelation med tidsserie B men vi en annan tidpunkt.

Exempel. Antag att vi har delar av två tidsserier där mätvärdena är från t.ex. olika flöden och inte vid samma tid. Frågan är då om det finns en korrelation mellan mätvärdena:

Tidsserie A: ____ 49.9 44.1 48.2 54.1 44.9 48.8 47.0 41.8 51.0 49.7 ____

Tidsserie B: ____  2.1  1.4  7.3  4.1  8.9  6.1  7.6  1.1  2.7  6.2 ____


Simulering — tre olika exempel

    1. Två helt slumpmässiga serier

    2. Två serier där en är en linjär kombination av den andra

    3. Två serier där en är en linjär kombination av den andra plus en slumpterm


 1. Två helt slumpmässiga serier

Här skapas en tidsserie A med initialt 1000 normalfördelade värden N(50, 4.5) och tidsserie B med 800 normalfördelade värden N(1.5, 0.4). Det finns ingen trend eller inget beroende mellan mätdata. Kodraderna skapar ett diagram (CCF-plott) för att visa korskorrelation mellan de två tidsserierna.

library(ggplot2)      # Bibliotek för att rita grafer.
library(forecast)     # Bibliotek för att göra graf för ccf.
library(dplyr)        # 
myA    <- 50           # Processens medelvärde (A)
sigmaA <- 4.5          # Processens sigma      (A)
antalA <- 1000         # Antal tidsdata        (A)

myB    <- 1.5          # Processens medelvärde (B)
sigmaB <- 0.4          # Processens sigma      (B)
antalB <- 800          # Antal tidsdata        (B)

error <- 0.2           # Sigma error

serieA <- rnorm(antalA, myA, sigmaA)                # Exempel 1
serieB <- rnorm(antalB, myB, sigmaB)
ggCcf(serieA, serieB, main="Exempel 1 - serie A och Serie B") +
      annotate("text", x = 0, y = Inf, label="Ingen error-term, lag 0", vjust=1.5, size=3.5, colour="blue")

Kommentar till simulering 1. Eftersom de simulerade tidsserierna är helt slumpmässiga förväntas inga konstigheter i diagrammet. Praktiskt taget alla värden (‘stolpar’) ligger inom ‘felmarginalen’.
CCF-diagrammet skapas av ‘ccf’-kommandot (cross correlation function) och visar för varje ‘lag’ (tidsförskjutning) korrelationskoefficienten (upp t.o.m. lag +/-30). De två horisontella streckade linjerna anger att cirka 95 % av alla värden förväntas ligga inom dessa gränser om det inte finns någon korrelation. (Eftersom p = 0.05 och n = 60 förväntas 3 värden utanför gränserna.)
(Notera att CCF-grafen skapas av ggCdf-kommandot vilket ger ett bättre diagram i denna mjukvara.)



2. Två serier där en är en linjär kombination av den andra

Här skapas en tidsserie (Serie C) som en funktion av Serie A. Eftersom det inte finns något slumpterm blir korrelationen i CCF-diagrammet exakt 1.0 vid lag 6:


\[ \begin{align} \Large SerieC = 0.2+0.05 \cdot Serie A \phantom{tomt} (\text{ingen slumpterm}) \end{align} \]

serieC <- 0.2 + 0.05*serieA                         # Exempel 2
serieC <- na.omit(lead(serieC, n = 6))              # Förskjuter data 6 steg.
ggCcf(serieA, serieC, main="Exempel 2 - serie A och Serie C, lag 6") +
      annotate("text", x = 0, y = Inf, label="Ingen error-term, lag 6", vjust=1.5, size=3.5, colour="blue")

Kommentar till simulering 2. Vid simuleringen förskjuts Serie C 6 steg (sker med kommandot ‘lead’) och detta plockas lätt upp av analysen som anger korrelationsvärdet 1.0 vid lag 6.



3. Två serier där en är en linjär kombination av den andra plus en slumpterm

Denna simulering är exakt samma som föregående men här med lag 20, samt att data nu innehåller en slumpterm, alltså mer realistisk:


\[ \begin{align} \Large SerieC = 0.2+0.05 \cdot Serie A + N(0, 0.2) \phantom{tomt.} (N(0, 0.2) \text{ slumpterm, sigma = 0.2}) \end{align} \]

serieC <- 0.2 + 0.05*serieA                         # Exempel 3
serieC <- na.omit(lead(serieC, n = 20))
serieD <- serieC + rnorm(length(serieC), 0, error)
ggCcf(serieA, serieD, main="Exempel 3 - serie A och Serie D, lag 20") +
      annotate("text", x = 0, y = Inf, label="Inkl error-term, lag 20", vjust=1.5, size=3.5, colour="blue")

Kommentar till simulering 3. Denna analys plockar fram korskorrelationen mellan de två serierna och högsta värdet hittas vid lag 20. Eftersom data nu innehåller en slumpterm blir den beräknade korrelationen cirka 0.65. Om data innehåller en ännu större slumpterm (brus) kommer ju ‘korrelationsignalen’ att bli allt lägre.
Men med kännedom om processens olika flöde går det kanske att hitta en anledning till en korrelation mellan det två tidsserierna vid lag 20.



Avslutningsvis. Vid analys av data ingår det att klämma och vrida på datamängden för att förstå dess hemligheter. Till detta behöver man ett lämpligt datorprogram och många av dem har har verktyg för tidsserieanalys och om man har datamängden preparerad tar kontrollen ovan bara några sekunder.

(Analyserna har gjorts med datorprogrammet ‘R’ och med det grafiska gränssnittet ‘R-studio’ och bägge är gratis tillgängliga på nätet. Se https://www.indstat.se och knappen [Statistikprogram - R] för installation.)

(Se https://www.indstat.se för många andra simuleringsövningar.)

(Se https://www.ing-stat.se/autokorr.html för simulering av s.k. auto correlation.)