← Blog/Time Series Analysis in Web Analytics — The Theory Behind the Trend
Time SeriesStatisticsGA4Forecasting
Analytics · 8 min read

Time Series Analysis in Web Analytics — The Theory Behind the Trend

Most marketers read traffic charts as pictures. Time series analysis treats them as data — with structure, decomposable components, and testable properties. Here's the theory.

March 16, 2025 · Martin Guzman

Most analysts look at a traffic chart and see a picture. Time series analysis treats that picture as data — structured, decomposable, and governed by mathematical properties that can be estimated, tested, and forecasted. The difference in what you can extract is enormous.

What is a Time Series?

Formally, a time series is a sequence of observations indexed by time:

{yt}t=1T={y1,y2,,yT}\{y_t\}_{t=1}^{T} = \{y_1, y_2, \dots, y_T\}

where yty_t is the value of some metric (sessions, users, revenue) at time tt, and the observations are ordered and equally spaced. That last part matters: web analytics data is almost always daily or weekly, and gaps in that regularity introduce analytical problems before you even start modeling.

The key distinction between a time series and a cross-sectional dataset is that observations are not independent. What happens on Monday is correlated with what happened on Sunday. That dependence is not noise — it is the signal.

The Decomposition Model

The foundational idea in time series analysis is that any observed series can be broken into components that have different causes and different behaviors. The classical additive decomposition is:

yt=Tt+St+Rty_t = T_t + S_t + R_t

where:

  • TtT_t is the trend — the long-run direction of the series
  • StS_t is the seasonal component — periodic fluctuations that repeat with a fixed frequency
  • RtR_t is the residual — everything left over after trend and seasonality are removed

For web analytics, this decomposition is not merely academic. Organic search traffic has a strong weekly seasonality (StS_t): B2B sites consistently drop on weekends. Campaign effects show up as trend shifts in TtT_t. A technical outage or a viral moment appears as a spike in RtR_t. Conflating these components leads to bad decisions — attributing a weekend dip to a content problem, or a seasonal peak to a campaign that happened to run at the right time.

When the seasonal amplitude grows with the trend (common in e-commerce), the multiplicative form is more appropriate:

yt=TtStRty_t = T_t \cdot S_t \cdot R_t

The choice between additive and multiplicative is empirical, not arbitrary — you check whether the seasonal swings scale with the level of the series.

Stationarity — Why It Matters

A time series is stationary if its statistical properties do not change over time. Formally, weak stationarity requires:

E[yt]=μ(constant mean)\mathbb{E}[y_t] = \mu \quad \text{(constant mean)} Var(yt)=σ2(constant variance)\text{Var}(y_t) = \sigma^2 \quad \text{(constant variance)} Cov(yt,yt+k)=γ(k)(covariance depends only on lag k, not on t)\text{Cov}(y_t, y_{t+k}) = \gamma(k) \quad \text{(covariance depends only on lag } k \text{, not on } t\text{)}

Most web traffic series are not stationary — they trend upward (or downward), and their variance shifts. This matters because most classical forecasting models assume stationarity. The standard fix is differencing: instead of modeling yty_t, you model Δyt=ytyt1\Delta y_t = y_t - y_{t-1}.

You can test for stationarity formally using the Augmented Dickey-Fuller test, which tests the null hypothesis that a unit root is present:

Δyt=α+βt+γyt1+i=1pδiΔyti+εt\Delta y_t = \alpha + \beta t + \gamma y_{t-1} + \sum_{i=1}^{p} \delta_i \Delta y_{t-i} + \varepsilon_t

Rejecting the null (γ<0\gamma < 0 significantly) means the series is stationary. Failing to reject means you difference and test again.

Autocorrelation — The Memory of a Series

Because time series observations are dependent, the correlation between yty_t and ytky_{t-k} is the core quantity of interest. The autocorrelation function (ACF) at lag kk is:

ρ(k)=Cov(yt,ytk)Var(yt)=γ(k)γ(0)\rho(k) = \frac{\text{Cov}(y_t, y_{t-k})}{\text{Var}(y_t)} = \frac{\gamma(k)}{\gamma(0)}

For organic search traffic, ρ(7)\rho(7) is typically high — today's traffic correlates with traffic 7 days ago. That's the weekly seasonal pattern expressed mathematically.

The partial autocorrelation function (PACF) at lag kk measures the correlation between yty_t and ytky_{t-k} after removing the linear effect of all intermediate lags yt1,,ytk+1y_{t-1}, \dots, y_{t-k+1}. Together, ACF and PACF plots are the primary diagnostic tools for model identification — their decay patterns tell you what kind of model your data is asking for.

Classical Models — AR, MA, ARIMA

The workhorse model family for stationary time series is ARIMA(pp, dd, qq):

ϕ(B)(1B)dyt=θ(B)εt\phi(B)(1-B)^d y_t = \theta(B)\varepsilon_t

where BB is the backshift operator (Byt=yt1By_t = y_{t-1}), (1B)d(1-B)^d is the differencing operator applied dd times, and εtN(0,σ2)\varepsilon_t \sim \mathcal{N}(0, \sigma^2) is white noise.

Unpacking the components:

Autoregressive term — AR(pp): The current value is a linear function of the previous pp values:

yt=ϕ1yt1+ϕ2yt2++ϕpytp+εty_t = \phi_1 y_{t-1} + \phi_2 y_{t-2} + \dots + \phi_p y_{t-p} + \varepsilon_t

Moving average term — MA(qq): The current value is a linear function of the previous qq error terms:

yt=εt+θ1εt1++θqεtqy_t = \varepsilon_t + \theta_1 \varepsilon_{t-1} + \dots + \theta_q \varepsilon_{t-q}

Integration — I(dd): The dd in ARIMA indicates how many times the series must be differenced to achieve stationarity.

For seasonal data, the model extends to SARIMA(pp,dd,qq)(PP,DD,QQ)m_m, where mm is the seasonal period (7 for weekly patterns in daily data) and the uppercase parameters capture the seasonal autoregressive and moving average structure.

Prophet's Additive Model

Facebook's Prophet, widely used for web traffic forecasting, takes a different approach — it models the decomposition directly rather than fitting ARIMA parameters:

y(t)=g(t)+s(t)+h(t)+εty(t) = g(t) + s(t) + h(t) + \varepsilon_t

where:

  • g(t)g(t) is a piecewise linear or logistic trend with automatic changepoint detection
  • s(t)s(t) is a Fourier series representation of seasonality
  • h(t)h(t) captures holiday and special event effects
  • εt\varepsilon_t is the error term

The seasonality component uses a Fourier series because it can represent arbitrary periodic patterns with a small number of parameters:

s(t)=n=1N(ancos2πntP+bnsin2πntP)s(t) = \sum_{n=1}^{N} \left( a_n \cos\frac{2\pi n t}{P} + b_n \sin\frac{2\pi n t}{P} \right)

where PP is the period and NN controls how quickly the seasonality can change. For weekly seasonality with daily data, P=7P = 7 and N=3N = 3 is typically sufficient.

The practical advantage of Prophet over ARIMA for web analytics is that it handles missing data, outliers, and structural breaks (a site redesign, a major campaign launch) more gracefully — because those are explicitly part of the model rather than violations of its assumptions.

The Residual is the Real Story

Once trend and seasonality are accounted for, what remains — RtR_t — is where the analytically interesting events live. A residual that spikes on a particular date tells you something happened that the model didn't expect. That anomaly might be:

  • A technical issue (tracking broke, site went down)
  • An external event (press mention, algorithm update)
  • A campaign effect you want to measure

This is the connection between time series analysis and causal inference. The residual is what's left after the predictable variation is removed — it's your cleanest window into the effect of specific interventions. If you ran a campaign on day tt^*, the causal question is whether RtR_{t^*} is large relative to the distribution of residuals before and after. That's the logic behind methods like Bayesian Structural Time Series (BSTS) and synthetic control.

The Iterative Nature of the Work

Time series analysis is not a linear pipeline. It is a flywheel:

  1. You decompose the series and examine the residuals
  2. The residuals reveal structure you didn't account for — a secondary seasonal pattern, a trend shift
  3. You update the model and decompose again
  4. You check whether the new residuals look like white noise

White noise residuals — zero autocorrelation at all lags, constant variance — are the goal. They mean the model has captured everything systematic in the data, and what's left is genuinely unpredictable. That's when you trust the forecast.

from statsmodels.stats.diagnostic import acorr_ljungbox
 
# Ljung-Box test: null hypothesis is that residuals are white noise
result = acorr_ljungbox(residuals, lags=[7, 14, 21], return_df=True)
print(result)
# p-values > 0.05 → residuals are white noise → model is well-specified

Most of the work in time series analysis is in this diagnostic loop — not in running the forecast itself. A model that fails the white noise test is telling you something systematic is still in the residuals. Listening to it is how you get from a technically correct model to one that actually understands your data.