Appendix B — Data Sources

This appendix is where the data lives. Most of the time you will just read the file that comes with the book and get to work, and that is all the intro chapter asks of you. But data goes stale, and sooner or later someone wants the newest numbers. So for the datasets that keep updating I will also show you how to pull a fresh copy yourself. You can skip everything past the first section on a first read.

Code
library(tidyverse)

The Bellingham Weather Data

The running example in the early chapters is daily weather from Bellingham International Airport: maximum temperature, minimum temperature, and precipitation. The data come from the Global Historical Climatology Network daily dataset (GHCN-Daily), which is the reference daily climate record maintained by NOAA’s National Centers for Environmental Information.1 The airport’s station id is USW00024217.

Reading It

The file data/kbli.csv comes with the book. Reading it is the whole story for day-to-day work.

Code
kbli <- read_csv("data/kbli.csv")
glimpse(kbli)
Rows: 9,667
Columns: 4
$ DATE <date> 2000-01-01, 2000-01-02, 2000-01-03, 2000-01-04, 2000-01-05, 2000…
$ TMAX <dbl> 6.7, 4.4, 8.9, 9.4, 6.7, 5.6, 8.3, 8.3, 6.1, 3.9, 3.3, 4.4, 5.6, …
$ TMIN <dbl> 1.7, 0.0, 0.0, 3.9, 1.7, 1.1, 5.0, 1.7, 2.2, 0.0, 0.0, 0.6, 0.0, …
$ PRCP <dbl> 4.3, 1.3, 8.4, 0.5, 0.0, 4.1, 3.8, 4.1, 6.6, 4.3, 3.6, 3.0, 0.0, …

Four columns. DATE is a calendar date. TMAX and TMIN are the daily maximum and minimum temperature in degrees Celsius. PRCP is total precipitation in millimeters. There is no single mean-temperature column because GHCN-Daily does not reliably report one for this station, so when a chapter wants a daily mean it builds one the standard way, as the midpoint of the day’s high and low.

Code
kbli <- kbli |> mutate(TEMP = (TMAX + TMIN) / 2)

A few things to know about the file. The values that NOAA’s quality-control process flags have been set to NA rather than dropped, so a day with a bad precipitation reading still keeps its good temperatures. Two days are missing from the record entirely (2006-06-29 and 2012-02-28), which leaves actual gaps in the daily index. That is normal for a long station record, and it is a useful thing to see early, because the rest of the book has to account for gaps rather than pretend they are not there.

Getting a Fresh Copy

The copy in the book runs through the date it was last built, but a working station keeps reporting. The most recent week or two always lags, because NOAA ingests and quality-controls the observations before releasing them, so do not be surprised when the last few days are not there yet.

To pull your own copy you need the GHCNr package, which talks to the NCEI servers for you.

Code
library(GHCNr)

The daily() function takes a station id and a date range. This call reaches out to the network, so it is set to not run when the book is built; run it yourself at the console.

Code
raw <- daily(
  station_id = "USW00024217",
  start_date = "2000-01-01",
  end_date   = as.character(Sys.Date())
)

What comes back has the temperature and precipitation columns plus a quality-control flag column for each one. A flag is blank when the value passed every check and carries a letter code when it did not. The careful move is to null out only the value that was flagged and keep the rest of the day, then drop the flag columns and round to a sensible precision.

Code
kbliFresh <- raw |>
  mutate(
    tmax = if_else(tmax_flag != "", NA_real_, tmax),
    tmin = if_else(tmin_flag != "", NA_real_, tmin),
    prcp = if_else(prcp_flag != "", NA_real_, prcp)
  ) |>
  transmute(
    DATE = date,
    TMAX = round(tmax, 1),
    TMIN = round(tmin, 1),
    PRCP = round(prcp, 1)
  ) |>
  arrange(DATE)

Then write it back over the existing file and you are current.

Code
write_csv(kbliFresh, "data/kbli.csv")

Finding the id for a different station is the one fiddly part. The easiest route is NOAA’s station search, where you can look up an airport or town and copy its GHCN-Daily id.2 GHCNr can also search by location with stations() and filter_stations() if you would rather stay in R.

A note on why GHCN-Daily and not the airport-feed alternatives like GSOD. GHCN-Daily assigns precipitation to the calendar day it actually fell on, midnight to midnight. The aviation summaries report on accumulation windows tied to the observation hour, which smears rain across the wrong day even when the yearly totals come out right. For a course about when things happen in time, getting the day right matters.

A Longer Monthly Record

The filters chapter wants a longer, coarser record than the daily file above: monthly mean temperature for the whole period this station has been reporting, not just since 2000. The file data/kbli_monthly.csv holds one row per month from January 1949 (as far back as GHCN-Daily goes for this station) through the most recently completed month, built the same way as the daily temperature above, the midpoint of each day’s high and low, averaged over the month.

Code
kbliMonthly <- read_csv("data/kbli_monthly.csv")
glimpse(kbliMonthly)
Rows: 929
Columns: 2
$ DATE <date> 1949-01-01, 1949-02-01, 1949-03-01, 1949-04-01, 1949-05-01, 1949…
$ TAVG <dbl> -2.17, 1.23, 6.08, 8.88, 12.52, 14.07, 15.72, 15.89, 14.74, 8.00,…

A month is only kept if at least 20 of its days have a valid temperature; short of that it comes back as NA rather than a mean built on a handful of days. That rule catches an actual hole in the record: the station stopped reporting for about two years in the mid-1990s, so July 1996 through September 1998 are all NA. Build it fresh with the same GHCNr call as above, starting the date range at 1949-01-01, then aggregate to monthly means with the completeness rule shown here.

Code
raw <- daily(
  station_id = "USW00024217",
  start_date = "1949-01-01",
  end_date   = as.character(Sys.Date())
)

dailyTemp <- raw |>
  mutate(
    tmax = if_else(tmax_flag != "", NA_real_, tmax),
    tmin = if_else(tmin_flag != "", NA_real_, tmin),
    tavg = (tmax + tmin) / 2
  )

monthly <- dailyTemp |>
  mutate(year = year(date), month = month(date)) |>
  summarise(nValid = sum(!is.na(tavg)), TAVG = mean(tavg, na.rm = TRUE),
    .by = c(year, month)) |>
  mutate(TAVG = if_else(nValid >= 20, TAVG, NA_real_),
    DATE = make_date(year, month, 1))

fullGrid <- tibble(DATE = seq(min(monthly$DATE), max(monthly$DATE), by = "month"))
kbliMonthlyFresh <- fullGrid |>
  left_join(select(monthly, DATE, TAVG), by = "DATE") |>
  mutate(TAVG = round(TAVG, 2))

write_csv(kbliMonthlyFresh, "data/kbli_monthly.csv")

The fullGrid join is the important step. Without it, a month with zero data just vanishes from the table instead of showing up as a calendar gap, an off-by-one error that’s easy to miss because nothing throws a warning, and it shifts every later month one slot out of place the moment you convert the column to a ts. A missing-data aside uses this exact gap as a worked example of filling one station’s hole from a neighboring station, without altering the file above.

A Neighboring Station for Filling the Gap

The missing-data aside pairs kbli_monthly.csv with a second station’s monthly temperature: USC00450587, Bellingham 3 SSW, a Historical Climatology Network station about three miles from the airport that was reporting the whole time the airport gauge went dark.

Code
donor <- read_csv("data/bellingham_3ssw_temp.csv")
glimpse(donor)
Rows: 192
Columns: 2
$ DATE <date> 1990-01-01, 1990-02-01, 1990-03-01, 1990-04-01, 1990-05-01, 1990…
$ TAVG <dbl> 5.93, 3.64, 7.34, 10.73, 13.04, 15.30, 18.53, 18.80, 15.71, 10.66…

Getting a Fresh Copy

Code
donorRaw <- daily("USC00450587", start_date = "1990-01-01", end_date = "2005-12-31",
  variables = c("tmax", "tmin"))
Code
donorFresh <- donorRaw |>
  mutate(tavg = (tmax + tmin) / 2, date = as.Date(date), year = year(date), month = month(date)) |>
  filter(!is.na(tavg)) |>
  summarise(TAVG = mean(tavg), nDays = n(), .by = c(year, month)) |>
  filter(nDays >= 20) |>
  transmute(DATE = make_date(year, month, 1), TAVG = round(TAVG, 2))
write_csv(donorFresh, "data/bellingham_3ssw_temp.csv")

The Nooksack River Discharge

The book includes a long, clean record of a snowmelt-driven river: the North Fork Nooksack River below Cascade Creek near Glacier, Washington, which drains the west side of Mount Baker. The data are daily mean discharge from the U.S. Geological Survey, gage 12205000, which has been read since 1938.3

Reading It

The file is included as data/nooksack.csv, with a date and the day’s mean discharge in cubic meters per second.

Code
nooksack <- read_csv("data/nooksack.csv")
glimpse(nooksack)
Rows: 32,142
Columns: 2
$ DATE <date> 1938-01-01, 1938-01-02, 1938-01-03, 1938-01-04, 1938-01-05, 1938…
$ FLOW <dbl> 19.71, 16.51, 14.30, 12.77, 11.75, 10.90, 10.36, 9.68, 9.57, 13.6…

The USGS reports discharge in cubic feet per second, so the file you have has already been converted to cubic meters per second (one cubic foot per second is about 0.0283 cubic meters per second), which is the SI unit the rest of the world uses for streamflow.

Getting a Fresh Copy

The USGS serves daily values straight from a web address, so you do not need a special package. The query below asks for parameter 00060 (discharge) and statistic 00003 (the daily mean) from one gage, in the agency’s plain-text rdb format. It is a network call, so it is set not to run when the book is built.

Code
url <- paste0(
  "https://waterservices.usgs.gov/nwis/dv/?format=rdb",
  "&sites=12205000",
  "&startDT=1938-01-01",
  "&endDT=", Sys.Date(),
  "&parameterCd=00060&statCd=00003"
)
raw <- read_tsv(url, comment = "#", show_col_types = FALSE)

The rdb format puts a one-line table of column types right under the header, so the first row is not data and gets dropped. The discharge column has a machine-generated name ending in 00060_00003; we find it, convert to cubic meters per second, and keep the date and flow.

Code
flowCol <- grep("00060_00003$", names(raw), value = TRUE)[1]
nooksackFresh <- raw[-1, ] |>
  transmute(
    DATE = as.Date(datetime),
    FLOW = round(as.numeric(.data[[flowCol]]) * 0.0283168, 2)   # cfs -> m^3/s
  ) |>
  arrange(DATE)

write_csv(nooksackFresh, "data/nooksack.csv")

To use a different river, change the sites id in the query. The USGS mapper lets you find a gage and read off its number.4 If you would rather not build the web address by hand, the dataRetrieval package wraps all of this in a readNWISdv() call.

Arizona Electricity Sales

The decomposition chapter uses monthly residential electricity sales for Arizona as its multiplicative example, a series that grows: Phoenix runs on summer air conditioning, and the state’s demand has climbed with its population. The data come from the U.S. Energy Information Administration, series ELEC.SALES.AZ-RES.M. The EIA reports it in millions of kilowatt-hours, which is the same as gigawatt-hours (GWh), the unit the book uses.5

Reading It

The file is bundled as data/az_electricity.csv, a month and the residential sales total. It runs from January 2001, where the EIA monthly retail series begins, through December 2025.

Code
az <- read_csv("data/az_electricity.csv")
glimpse(az)
Rows: 300
Columns: 2
$ DATE  <date> 2001-01-01, 2001-02-01, 2001-03-01, 2001-04-01, 2001-05-01, 200…
$ SALES <dbl> 2062.6, 1790.0, 1690.1, 1404.4, 1983.7, 2571.9, 3052.2, 3116.5, …

Because the series is already monthly, there is no daily-to-monthly aggregation to do. To decompose it you turn the SALES column into a ts with a yearly frequency, ts(az$SALES, start = c(2001, 1), frequency = 12).

Getting a Fresh Copy

The EIA serves this through its open-data API, which needs a free key you register for once.6 With a key in hand the request is one URL; it is a network call, so it is set not to run when the book is built.

Code
key <- "YOUR_EIA_KEY"      # register at https://www.eia.gov/opendata/
url <- paste0(
  "https://api.eia.gov/v2/electricity/retail-sales/data/",
  "?api_key=", key,
  "&frequency=monthly&data[0]=sales",
  "&facets[stateid][]=AZ&facets[sectorid][]=RES",
  "&start=2001-01&sort[0][column]=period&sort[0][direction]=asc"
)
raw <- jsonlite::fromJSON(url)$response$data

What comes back has a period like "2001-01" and a sales value. Convert the period to a first-of-month date and keep the two columns.

Code
azFresh <- raw |>
  transmute(DATE = as.Date(paste0(period, "-01")),
    SALES = round(as.numeric(sales), 1)) |>
  arrange(DATE)

write_csv(azFresh, "data/az_electricity.csv")

To use a different state or sector, change the stateid and sectorid facets in the query (for example TX and RES, or AZ and COM for commercial). If you would rather not register a key, the same series lives in EIA’s no-key bulk download ELEC.zip, though that file is large and you have to filter it down to the one series yourself.

The Lake Washington Plankton Data

The cross-correlation chapter uses two monthly series, producers and grazers, from the long-term plankton monitoring of Lake Washington. The underlying record was collected by W. T. Edmondson and the University of Washington and is distributed in tidy form with the MARSS package (Holmes et al. 2012); the ecological story behind it is told in Hampton et al. (2006). The file that comes with the book is already pooled and cleaned, so the chapter just reads it.

Code
lwa <- read_csv("data/lake_wa_plankton.csv")
head(lwa)
# A tibble: 6 × 4
   Year Month producers grazers
  <dbl> <dbl>     <dbl>   <dbl>
1  1962     1    -1.18   -0.328
2  1962     2    -0.582  -0.257
3  1962     3    -0.92    0.038
4  1962     4    -0.136   0.398
5  1962     5    -0.095   0.445
6  1962     6    -0.403  -0.111

Each row is one month, with producers an index of the phytoplankton and grazers an index of the zooplankton that eat them. Both are dimensionless: I built them by pooling several taxa, and because plankton abundances are right-skewed and span orders of magnitude across taxa, each taxon is log-transformed and standardized before pooling so that no single bloom dominates the index.

Building It

Here is the recipe, in case you want to change which taxa go into each index or pull the source data yourself. The MARSS (Holmes et al. 2012) package ships the record as lakeWAplanktonRaw; you only need it to rebuild the file, so the book does not load it.

Code
library(MARSS)
data(lakeWAplankton)
raw <- as.data.frame(lakeWAplanktonRaw)

algae   <- c("Cryptomonas", "Diatoms", "Greens",
  "Bluegreens", "Unicells", "Other.algae")
grazers <- c("Daphnia", "Diaptomus", "Cyclops",
  "Epischura", "Non.daphnid.cladocerans", "Non.colonial.rotifers")

# log each taxon to tame the skew, standardize so scales are comparable,
# then average to a single producer and grazer index
zlog <- function(m) scale(log1p(m))
producers <- rowMeans(zlog(as.matrix(raw[algae])),   na.rm = TRUE)
grazers   <- rowMeans(zlog(as.matrix(raw[grazers])), na.rm = TRUE)

# linear-interpolate the handful of months a taxon went unrecorded
fill <- function(v) approx(seq_along(v), ifelse(is.finite(v), v, NA),
  seq_along(v), rule = 2)$y

lwaFresh <- tibble(Year = raw$Year, Month = raw$Month,
  producers = round(fill(producers), 3),
  grazers   = round(fill(grazers),   3))
write_csv(lwaFresh, "data/lake_wa_plankton.csv")

The record runs monthly from 1962 to 1994. It does not update the way a weather station does, so there is no live source to refresh against; the recipe is here so you can see exactly how the two indices were built and swap taxa in or out if you want to.

The Colorado River Flow Data

The regression chapter models annual flow on the Colorado River. The series is the water-year flow at Lees Ferry, Arizona, the gaging point used to divide the river’s upper and lower basins, paired with cool-season precipitation, spring-summer temperature, and autumn soil moisture for the upper basin. The data come from the supplementary material of Woodhouse et al. (2016), whose paper works out how temperature amplifies the effect of precipitation deficits on flow.

Reading It

Code
flow <- read_csv("data/woodhouse.csv")
glimpse(flow)
Rows: 107
Columns: 5
$ Year       <dbl> 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915,…
$ LeesWYflow <dbl> 18214678, 21234305, 11773952, 21841427, 14736670, 15125081,…
$ OctAprP    <dbl> 267.6978, 271.0136, 185.7287, 275.7565, 198.2104, 252.9581,…
$ MarJulT    <dbl> 9.852823, 10.052273, 9.910895, 9.545044, 11.950688, 10.4929…
$ novsoil    <dbl> 1064625.6, 1067966.5, 706853.4, 924749.4, 868586.4, 684712.…

LeesWYflow is the water-year flow in acre feet, OctAprP is October-to-April precipitation in millimeters, MarJulT is March-to-July temperature in degrees Celsius, and novsoil is November soil moisture. The record runs from 1906 to 2012. The original supplement is an Excel file linked from the paper; the version here is the same table saved as CSV.

The New York Air Quality Data

The regression chapter uses a short air-quality record as a counterexample, a time-series regression whose residuals turn out to be clean. The data are daily measurements of ground-level ozone and weather in New York over the summer of 1973, the same record distributed as environmental in the lattice package and as airquality in base R. The version here keeps the complete cases and adds a sequential day index.

Reading It

Code
air <- read_csv("data/ny_air_quality.csv")
glimpse(air)
Rows: 111
Columns: 5
$ Day         <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
$ ozone       <dbl> 41, 36, 12, 18, 23, 19, 8, 16, 11, 14, 18, 14, 34, 6, 30, …
$ radiation   <dbl> 190, 118, 149, 313, 299, 99, 19, 256, 290, 274, 65, 334, 3…
$ temperature <dbl> 67, 72, 74, 62, 65, 59, 61, 69, 66, 68, 58, 64, 66, 57, 68…
$ wind        <dbl> 7.4, 8.0, 12.6, 11.5, 8.6, 13.8, 20.1, 9.7, 9.2, 10.9, 13.…

ozone is the ground-level ozone concentration in parts per billion, radiation is solar radiation, temperature is in degrees Fahrenheit, and wind is wind speed in miles per hour. The Day column is just the row order, a stand-in for the calendar so the series can be treated as evenly spaced.

Building It

Code
library(lattice)
airFresh <- tibble(Day = seq_len(nrow(environmental)), environmental)
write_csv(airFresh, "data/ny_air_quality.csv")

The Poorly-Launched Sensor Data

The filters chapter uses two short logger records, hourly radiation and a misaligned temperature series, to teach time alignment and gap-filling. Both are constructed examples built to look like a field deployment gone sideways (a temperature logger running on the wrong schedule, a radiation sensor with a loose cable), not readings from a station that exists. There’s no live source to refresh, and no need for one: the point of the exercise is the alignment and interpolation mechanics, not the specific numbers.

Reading It

Code
tmp <- read_csv("data/tmp.csv")
rad <- read_csv("data/rad.csv")
glimpse(tmp)
Rows: 1,104
Columns: 2
$ DateTime <dttm> 2014-06-01 00:03:00, 2014-06-01 02:03:00, 2014-06-01 04:03:0…
$ tmp      <dbl> 4.307, 2.873, 3.109, 5.089, 7.209, 8.700, 11.540, 12.810, 12.…
Code
glimpse(rad)
Rows: 2,208
Columns: 2
$ DateTime <dttm> 2014-06-01 00:00:00, 2014-06-01 01:00:00, 2014-06-01 02:00:0…
$ rad      <dbl> 0, 0, 0, 0, 0, 37, 309, 529, 733, 901, 1025, 1090, 1096, 1039…

tmp is air temperature in degrees Celsius, recorded every other hour starting three minutes after the hour. rad is solar radiation in watts per square meter, hourly, with about 10% of one summer missing.

The Orbital Insolation Data

The spectral analysis chapter’s Milankovitch exercise uses a reconstruction of July insolation at 65°N over the past five million years, from Berger and Loutre (1991). Unlike everything else in this appendix, it isn’t a measurement of anything: it’s calculated directly from orbital mechanics, so there’s no sensor, station, or survey behind it and nothing to refresh from a live source.

Reading It

Code
insolation <- read_csv("data/jul65N.csv")
glimpse(insolation)
Rows: 5,001
Columns: 2
$ kya      <dbl> 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -1…
$ W.per.m2 <dbl> 426.76, 430.12, 434.69, 440.20, 446.28, 452.48, 458.31, 463.2…

kya is thousands of years relative to the present and runs negative into the past (0 is now, -5000 is five million years ago). W.per.m2 is the calculated insolation in watts per square meter.

The Cherry Point Tide Data

The spectral analysis chapter’s tides example is hourly water level for all of 2023 at Cherry Point, a NOAA CO-OPS tide gauge just north of Bellingham (station 9449424).

Reading It

Code
tides <- read_csv("data/cherry_point_tides.csv")
glimpse(tides)
Rows: 8,760
Columns: 2
$ DateTime      <dttm> 2023-01-01 00:00:00, 2023-01-01 01:00:00, 2023-01-01 02…
$ water_level_m <dbl> 1.442, 0.928, 0.538, 0.343, 0.354, 0.599, 0.903, 1.271, …

water_level_m is water level in meters relative to mean lower low water (MLLW), the standard tidal datum.

Getting a Fresh Copy

NOAA’s Tides and Currents API is public and needs no key. This pulls one calendar year of the hourly_height product.

Code
library(jsonlite)

url <- paste0(
  "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter?",
  "product=hourly_height&application=timeSeriesBook",
  "&begin_date=20230101&end_date=20231231",
  "&datum=MLLW&station=9449424&time_zone=GMT&units=metric&format=json"
)
tidesRaw <- fromJSON(url)$data
Code
tidesFresh <- tidesRaw |>
  transmute(DateTime = as.POSIXct(t, tz = "UTC"),
    water_level_m = as.numeric(v))
write_csv(tidesFresh, "data/cherry_point_tides.csv")

The Blowfly Population Data

The wavelets chapter’s last exercise uses a record with nothing to do with weather: adult counts from one of A. J. Nicholson’s laboratory blowfly experiments, tabulated in Nicholson (1957). Nicholson raised the Australian sheep blowfly, Lucilia cuprina, on a fixed food ration and counted the population by hand every two days for two years, watching food-limited generations drive their own boom and bust.

Reading It

Code
blowfly <- read_csv("data/blowfly.csv")
glimpse(blowfly)
Rows: 361
Columns: 2
$ Day   <dbl> 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 3…
$ Count <dbl> 948, 942, 911, 858, 801, 676, 504, 397, 248, 146, 1801, 6235, 59…

Day counts days since the start of the experiment, in steps of two. Count is the number of adult flies counted that day.

There is no live source to refresh here. This is one closed lab experiment run once in the 1950s, not a station still reporting; the digitized table traces back through Nicholson (1957) and a widely used retabulation by Brillinger, Guckenheimer, Guttorp, and Oster (1980).7

The Pacific Halibut Stock and Recruitment Data

The forecasting chapter’s backtesting example uses annual recruitment for Pacific Halibut (Hippoglossus stenolepis), the stock the International Pacific Halibut Commission has managed jointly for the US and Canada since 1923. The series comes packaged in FSAdata (Ogle 2023), itself compiled from IPHC annual reports and a technical report by Myhre and colleagues; you only need the package to rebuild the file, so the book does not load it.

Reading It

Code
halibut <- read_csv("data/halibut.csv")
glimpse(halibut)
Rows: 63
Columns: 5
$ year  <dbl> 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939…
$ ssb   <dbl> NA, NA, NA, NA, NA, NA, 43841.5, 47712.9, 49381.2, 51448.7, 5392…
$ rec   <dbl> NA, NA, NA, NA, NA, NA, 7227.7, 8199.4, 8326.3, 8452.3, 8016.5, …
$ land  <dbl> 56.9, 49.4, 44.2, 44.5, 46.8, 47.5, 47.3, 48.9, 49.5, 49.6, 50.9…
$ fmort <dbl> NA, NA, NA, NA, NA, NA, 0.27, 0.37, 0.37, 0.32, 0.28, 0.30, 0.29…

year runs 1929 to 1991. ssb is spawning stock biomass in tonnes, rec is recruits in thousands of fish, land is landings in millions of pounds, and fmort is fishing-related mortality. The book’s forecast only uses rec, and only over 1935 to 1981, the span with no missing years; biomass and recruitment were not estimated for the record’s earliest and most recent years, and the file keeps those gaps as honest NAs rather than papering over them.

Building It

Code
library(FSAdata)
data(HalibutPAC)

halibutFresh <- HalibutPAC
halibutFresh$ssb <- round(halibutFresh$ssb, 1)
halibutFresh$rec <- round(halibutFresh$rec, 1)
write_csv(halibutFresh, "data/halibut.csv")

This is a closed historical record from a specific stock assessment vintage, not a feed that updates, so there is nothing to refresh against; the recipe is here so you can see exactly where the numbers came from.

The Rest of the Data

Every other dataset the book uses lives in the same data/ folder, and you can download the whole bundle as a single data.zip from the book’s website. Each dataset is credited where it first appears, with a pointer back here when there is a way to refresh it yourself. As more chapters move over to this style, their sources will be documented in this appendix too.

Image Credits

The one piece of third-party artwork in the book, Allison Horst’s autocorrelation illustration in the Autocorrelation chapter, is reused under a Creative Commons license, with attribution as required. A machine-readable log of this credit, suitable for a publisher’s permissions desk, lives in permissions.csv at the repository root.

Image Author License Source
Autocorrelation function illustration Allison Horst CC BY 4.0 allisonhorst.com

  1. https://www.ncei.noaa.gov/products/land-based-station/global-historical-climatology-network-daily↩︎

  2. https://www.ncei.noaa.gov/access/search/data-search/daily-summaries↩︎

  3. https://waterdata.usgs.gov/monitoring-location/12205000/↩︎

  4. https://maps.waterdata.usgs.gov/mapper/↩︎

  5. https://www.eia.gov/electricity/data.php↩︎

  6. https://www.eia.gov/opendata/↩︎

  7. http://ionides.github.io/531w16/final_project/blowfly4.csv↩︎