Getting Started

Why Time Series

Many environmental data are time series. Temperature records. Streamflow. Tree rings. CO2 measured at Mauna Loa. All of it indexed to time, all of it carrying a signature of the processes that generated it.

But it goes further than that. Even data sets that aren’t obviously temporal, like a soil sample or a species survey or a water chemistry measurement, exist in time. They were collected at a moment, and the system that produced them has a history. Time is the dimension that almost every environmental process moves through, and most of those processes have structure in time: cycles, trends, memory, responses to forcing. Learning to read that structure is part of what it means to do environmental science.

It also matters for getting the statistics right. Temporal dependence, the fact that observations close together in time tend to be more similar than observations far apart, violates the independence assumption that underlies most standard statistical methods. Ignore it, and your standard errors are too small, your p-values too optimistic, your conclusions too confident. Time series analysis gives you the tools to account for that dependence rather than pretend it isn’t there.

And beyond the bookkeeping, temporal structure is information. A seasonal cycle tells you something about the drivers of a system. A long-term trend tells you something has changed. Lagged correlations between two series can reveal how one system influences another. The goal of this book is to help you extract that information, work with it carefully, and communicate what it means.

This is a hands-on book. You will write code, look at data, fit models, and interpret results. Each chapter builds on what came before. The math shows up when it needs to, but the emphasis throughout is on doing time series analysis and understanding what you’re doing, not on deriving things for their own sake.

George Edward Pelham Box FRS need a proper introduction. Box was a British statistician who spent most of his career at the University of Wisconsin, and a fair amount of what’s ahead traces back to him one way or another. The systematic way of identifying and fitting autoregressive and moving-average models, the one you’ll meet in the ARMA chapter, comes from a 1970 book he wrote with Gwilym Jenkins; people still call the whole approach Box-Jenkins. The transformation you reach for when a series’ variance needs stabilizing before you trust it carries his name too, alongside David Cox.

None of that is why I keep bringing him up with students, though. In a 1976 paper for the Journal of the American Statistical Association, Box wrote: all models are wrong, but some are useful. (The cleaner two-clause version everyone quotes actually comes from a later book he wrote with Norman Draper, but it’s the same point.) He wasn’t being glib. Every model simplifies whatever produced your data, so asking whether a model is true doesn’t get you anywhere. Asking whether it’s useful for the question in front of you does. Keep that distinction handy. You’ll watch a beautifully fitting AR(20) explain nothing, an impressive R² turn out to be nearly automatic, and a forecast prove itself only once it’s been checked against data it never saw. Every time, Box’s line is the reason we keep asking the second question instead of stopping at the first.

How This Book Is Laid Out

Most of the chapters follow a natural progression. Each one builds on the last, and working through them in order is the right call. But scattered through the book you’ll find Asides: short detours that dig into something that doesn’t quite fit the main flow. An Aside might work through the math behind a method, explain how R handles something under the hood, or fill in background that makes the surrounding chapters make more sense.

You can skip the Asides and still follow the core material. But they’re there because the questions they answer are the ones that actually come up when you’re working through a chapter and start wondering why something works the way it does. If that’s how your brain works, the Asides are for you.

What’s in This Book

The chapters fall into four parts, each with its own job.

Foundations

Get oriented: how R represents a time series, and how to pull one apart into trend, season, and noise before anything harder starts.

  • The Measure of Time: What a time series is, how R represents temporal data, and the vocabulary we’ll use throughout.
    • Aside: Understanding Methods and Generics in R. A quick look under the hood at how R dispatches functions like plot() and summary() depending on the class of the object you give them. Useful background for the whole book.
    • Aside: How R Keeps Track of Dates and Times. Parsing dates, time zones, daylight saving, leap days, and the calendar quirks that show up in climate data specifically.
  • Decomposition: How to separate a time series into trend, seasonal, and irregular components. The core ideas are simple; the implications for analysis are not.

The Anatomy of Dependence

A series’ own past is the first kind of dependence you’ll meet. This part builds the vocabulary for it: the ACF and PACF to see it, stationarity to say when it holds steady, and ARMA to model it directly.

  • Autocorrelation: What it is, how to measure it, and what the ACF and PACF are actually telling you. Foundational for everything that follows.
    • Aside: Correlation is not Regression. Why a correlation coefficient and a regression slope answer different questions, even on the same pair of numbers.
    • Aside: Why the ACF and PACF Look the Way They Do in an MA(1) Process. Works through the theory behind those canonical shapes. Worth reading once you’ve seen the patterns and started wondering where they come from.
    • Aside: The PACF by Hand. Derives the partial autocorrelation function from the Durbin-Levinson recursion instead of just calling pacf().
  • Stationarity: What it means for a series to behave the same way throughout its length, why almost everything else in this book assumes it, and how to test for it.
  • ARMA(p,q): Autoregressive and moving-average models for stationary series, and how to identify, fit, and check them.

Explore, Infer, Predict

Once you can describe a series’ own memory, this part asks what that buys you: whether two series move together, whether a slope or a trend can be trusted, and whether any of it predicts something you haven’t already seen.

  • Cross-Correlation: How to measure the relationship between two time series, including lagged relationships, and why a raw cross-correlation can lie to you.
    • Aside: How Ccf Normalizes. Reproduces R’s cross-correlation normalization by hand.
  • Regression: What happens when your regression residuals are autocorrelated, why it matters, and how to fix it with generalized least squares.
    • Aside: OLS via Algebra and Matrices. Derives the OLS solution from scratch in both algebraic and matrix form, then implements it in R. Useful background before GLS.
  • Trend Detection: Mann-Kendall and Theil-Sen as a rank-based alternative to a regression trend, and why they need the same autocorrelation fix regression does.
    • Aside: Detecting a Breakpoint. Explores when change in a time series can be described by an abrupt change rather than a gradual change.
  • Forecasting: Using a fitted model’s correlation structure to predict future values, and backtesting before you trust any of it.
  • Reconstruction: Reconstructing centuries of streamflow from tree rings, and the calibration-and-verification habit that makes any out-of-sample prediction trustworthy.

The Frequency Domain

Trade the clock for the cycle. Smooth a series to see its slow-moving pattern, then decompose it into the frequencies it’s built from.

  • Filtering and Smoothing: Extracting signal from noisy data using moving averages, splines, and other filters. When you want to see the forest, not the individual trees.
  • Spectral Analysis: Spectral methods for identifying periodic signals. If you’ve ever wondered how to find a cycle in noisy data, this is the chapter.
    • Aside: A Glimpse of Cross-Spectral Analysis. Extends the periodogram to a pair of series: a correlation and a lag, computed separately for every frequency instead of once for the whole record.
  • Wavelets: A periodogram assumes a series’s frequency content holds steady for the whole record. Wavelets keep time and frequency on the same plot, so you can ask what frequency and when at once.

What You Should Know Beforehand

This isn’t a book for absolute beginners, and a few things will go more smoothly if you’ve seen them before. None of it is a hard prerequisite, but here’s what I’m assuming.

R basics. You should be comfortable enough in R to read a data file, poke at a data frame, understand functions, and not panic when something throws an error. You don’t need to be an expert. If you can follow along and look things up when you’re stuck, you’re ready.

Some statistics. We lean on the standard introductory toolkit throughout: correlation, standard errors, p-values, and linear models. You don’t need to have aced a theory course, but the ideas should feel familiar rather than brand new. If “fit a linear model and look at the residuals” means nothing to you, a stats class first will make this book a lot more rewarding.

Matrix algebra (gently). It comes up mostly in the Asides, when I want to show what a method is actually doing under the hood. If you’ve never multiplied two matrices, the main chapters will still work fine. The Asides are there if you want to look deeper.

One more thing about the code. We store data as tsibble objects, tidy tables with a visible time index, and wrangle them with ordinary tidyverse verbs: dplyr for manipulation, ggplot2 for plots, the pipe to chain it together. But a lot of the actual modeling machinery, arima.sim, lm, spectrum, comes from base R, since that’s where most of the classic time series tools live, and we build several of them from scratch before reaching for the canned version. If tidyverse syntax is new to you, don’t let it slow you down. The place to start is tidyverse.org1, and from there R for Data Science (Wickham et al. 2023) is the canonical resource.

Go Do Great Things

You are going to learn to see the world in time. By the end of this book you will be able to look at a time series, describe its structure, fit a model, check your assumptions, and say something about what the data suggest.

The people who do this work well are not necessarily the ones who find it easiest. They’re the ones who run the code when it breaks, read the error messages, ask questions, and keep going.

So: set up your project, download the data, and let’s get to work.

Setup

The book is built around R, so you’ll need a working R installation. The code here was written and tested with R version 4.5.2 (2025-10-31). You should be reasonably up to date on your versions of R, RStudio, and relevant packages. If you’re not sure, run:

update.packages()

Do it now, and anytime it occurs to you. It’s almost always the right thing to do.

Project Structure

To follow along with the examples, you’ll want a working RStudio project.

  1. Create a new RStudio project
    Go to File → New Project → New Directory → New Project. Give it a name (something like timeseries-analysis) and choose where to save it.

  2. Download the data/ folder
    The datasets used in the examples are bundled into a single data.zip. Download it and unzip it inside your project directory.

    You can download the data directly:

    https://timeseries.andybunn.org/data.zip

    Once it’s unzipped, your folder structure should look something like this:

    timeseries-analysis/
    ├── data/
    │   ├── kbli.csv
    │   ├── nooksack.csv
    │   └── ...
    └── timeseries-analysis.Rproj
  3. Use relative paths in your code
    Use paths like "data/kbli.csv" rather than full file paths. This keeps the code portable. It will run on any machine without modification.

    Code
    kbli <- read_csv("data/kbli.csv")
  4. Save your files in the project root
    Keep your working files in the project’s root directory. Quarto (.qmd) and R Markdown (.Rmd) are close cousins; the syntax is nearly identical and either works fine here. After working through the decomposition chapter, your structure might look like this:

    timeseries-analysis/
    ├── data/
    │   ├── kbli.csv
    │   ├── nooksack.csv
    │   └── ...
    ├── decompositionWork.qmd
    └── timeseries-analysis.Rproj

  1. https://www.tidyverse.org/↩︎