Load packages

library(tidyverse)
library(astsa)
library(fpp3)
library(epidatasets)

Random walk

n = 500 # number of time points
r = 100 # number of repetitions
delta = 0.2 # drift

x0 = matrix(rnorm(n * r), nrow = n)
y0 = matrix(rnorm(n * r) + delta, nrow = n)

x = apply(x0, 2, cumsum)
y = apply(y0, 2, cumsum)

mu_x = apply(x, 1, mean)
mu_y = apply(y, 1, mean)

matplot(x, type = "l", lty = 1, col = rgb(0, 0, 0, 0.2),
        xlab = "t", ylab = "x_t", main = "delta = 0")
lines(mu_x, lty = 1, lwd = 3, col = 1)

matplot(y, type = "l", lty = 1, col = rgb(0.96, 0.28, 0.24, 0.2),
        xlab = "t", ylab = "x_t", main = paste("delta =", delta))
lines(mu_y, lty = 1, lwd = 3, col = 2)

Auto-correlation heatmaps

n = 500
m1 = m2 = matrix(0, n, n)

# Moving avg autocorrelation matrix
m1[row(m1) == col(m1)-2] = 1/3
m1[row(m1) == col(m1)-1] = 2/3
m1[row(m1) == col(m1)] = 1
m1[row(m1) == col(m1)+1] = 2/3
m1[row(m1) == col(m1)+2] = 1/3

# Random walk autocorrelation matrix
for (s in 1:n) {
  for (t in 1:n) {
    m2[s,t] = min(s,t) / sqrt(s*t)
  }
}

# Handy rotate function --- the orientation of R's image() function is to plot
# a heamtap under a ***90 degrees counterclockwise rotation*** of the standard
# way we think of matrices being laid out. So we can use this function below to
# pre-rotate the matrix clockwise by 90 degrees, to effectively undo this and
# have it print in a way that aligns with the standard matrix layout
clockwise90 = function(a) { t(a[nrow(a):1,]) }

# Now for the heatmaps
par(mar = c(2,2,2,2), mfrow = c(1,2))
image(clockwise90(m1), main = "Moving average", xaxt = "n", yaxt = "n")
image(clockwise90(m2), main = "Random walk", xaxt = "n", yaxt = "n")

Covid-19 cross-correlation

# Covid-19 cases and deaths in California, pivot longer
df = cases_deaths_subset |>
  filter(geo_value == "ca") |>
  select(time_value, case_rate_7d_av, death_rate_7d_av) |>
  pivot_longer(cols = c(case_rate_7d_av, death_rate_7d_av)) |>
  mutate(name = recode(name, 
                       case_rate_7d_av = "Cases", 
                       death_rate_7d_av = "Deaths"))

# Handy function to produce a transformation from one range to another
trans = function(x, from_range, to_range) {
  (x - from_range[1]) / (from_range[2] - from_range[1]) *
    (to_range[2] - to_range[1]) + to_range[1]
}

# Compute ranges of the two signals, and transformations in b/w them
range1 = df |> filter(name == "Cases") |> select("value") |> range()
range2 = df |> filter(name == "Deaths") |> select("value") |> range()
trans12 = function(x) trans(x, range1, range2)
trans21 = function(x) trans(x, range2, range1)

ggplot(bind_rows(
  df |> filter(name == "Cases"),
  df |> filter(name == "Deaths") |> mutate_at("value", trans21)),
  aes(x = time_value, y = value)) +
  geom_line(aes(color = name)) +
  scale_color_manual(values = palette()[c(2,4)]) +
  scale_y_continuous(
    name = "Reported Covid-19 cases per 100k people", 
    limits = range1,
    sec.axis = sec_axis(
      trans = trans12, 
      name = "Reported Covid-19 deaths per 100k people")) +
  labs(title = "Covid-19 cases and deaths in California", x = "Date") +
  theme_bw() + 
  theme(legend.position = "bottom", legend.title = element_blank())

ccf(df |> filter(name == "Cases") |> select(value),
    df |> filter(name == "Deaths") |> select(value),
    lag.max = 40, ylab = "Cross-correlation", main = "")

Speech auto-correlation

plot(speech, type = "l", ylab = "Vocal response")

acf(speech, lag.max = 250, ylab = "Auto-correlation", main = "")