The Breeding Bird Survey is an annual survey conducted by the USGS each breeding season. Highly skilled citizen scientists visit several thousand locations across the United States and Canada to survey breeding birds. At each location, observers conduct 50 3-minute point counts along a 40km route.
We’ll use a few functions:
unzipBBS() unzips the files that you’ll need. This will take a little whilereadBBS() reads in the raw BBS data and combines it into one dataframesummarizeBBS() reduces point counts to a single count for the entire routeaouToSp() replaces AOU codes with species namesunzipBBS <- function(path) {
print("Unzipping folder")
folderpath <- paste0(path, ".zip")
unzip(folderpath,
exdir = path)
print("Unzipping 50-StopData.zip")
unzip(paste0(path, "/50-StopData.zip"), overwrite = T,
exdir = path)
base <- paste0(path, "/50-StopData/1997ToPresent_SurveyWide")
filesZip <- list.files(base, pattern = ".zip")
print("Unzipping files")
pb <- txtProgressBar(min = 0, max = 10, initial = 0, style = 3)
for (i in 1:length(filesZip)) {
#print(paste0("Unzipping file ", i))
unzip(paste0(base, "/", filesZip[i]),
exdir = base,
overwrite = T)
setTxtProgressBar(pb,i)
}
print("Unzipping migrants")
unzip(paste0(path, "/MigrantNonBreeder.zip"), overwrite = T,
exdir = path)
unzip(paste0(path, "/MigrantNonBreeder/Migrants.zip"), overwrite = T,
exdir = path)
print("Unzipping other data")
unzip(paste0(path, "/Routes.zip"), overwrite = T,
exdir = path)
unzip(paste0(path, "/Weather.zip"), overwrite = T,
exdir = path)
}
readBBS <- function(path, migrant = F) {
base <- paste0(path, "/50-StopData/1997ToPresent_SurveyWide")
files <- list.files(base, pattern = ".csv")
print("Reading in breeding birds...")
pb <- txtProgressBar(min = 0, max = 10, initial = 0, style = 3)
data <- read.csv(paste0(base, "/", files[1]))
colnames(data) <- tolower(colnames(data))
setTxtProgressBar(pb,1)
for (j in 2:length(files)) {
tmp <- read.csv(paste0(base, "/", files[j]))
colnames(tmp) <- tolower(colnames(tmp))
setTxtProgressBar(pb,j)
data <- rbind(data, tmp)
}
if (migrant == T) {
print("Reading in migrants...")
# unzip(paste0(path, "MigrantNonBreeder.zip"), overwrite = T)
# unzip(paste0(path, "MigrantNonBreeder/Migrants.zip"), overwrite = T)
#
mig <- read.csv(paste0(path, "/Migrants.csv"))
colnames(mig) <- tolower(colnames(mig))
data <- rbind(data, mig)
}
return(data)
}
summarizeBBS <- function(raw) {
raw$count <- rowSums(raw[,8:57])
raw$id <- paste0(raw$countrynum, "_", raw$statenum, "_", raw$route)
raw1 <- raw[,c("id", "year", "aou", "count")]
return(raw1)
}
aouToSp <- function(raw, path) {
spList <- read.fwf(file = paste0(path, "/SpeciesList.txt"),
widths = c(7, 5, 50, 50, 50,
50, 50, 50, 50),
skip = 14,
col.names = c("Seq", "aou", "English",
"French", "Spanish",
"Order", "Family", "Genus", "Species"),
strip.white = T)
spList$gs <- paste(spList$Genus, spList$Species, sep = " ")
spList <- spList[,c("aou", "gs")]
bbsSp <- merge(raw, spList, by = "aou")
bbsSp <- bbsSp[,c("id", "year", "gs", "count")]
colnames(bbsSp) <- c("id", "year", "species", "count")
return(bbsSp)
}
And the tidyverse package:
library(tidyverse)
Raw Breeding Bird Survey data can be downloaded from the BBS ScienceBase Site. Under “Attached Files”, select “Download All” to download the data in a .zip folder called “2020Release-Nor.zip”. You need to manually unzip this file, which will give you a folder called “2020Release-Nor”. Set your working directory to wherever you saved this folder:
path <- "X:/data-BBS/2022Release_Nor"
This step will take a little while. You only need to run this one time, when you first download the data. After that, the unzipped files will be saved on your computer.
unzipBBS(path)
The data are separated into 10 .csv files that hold the counts at each of the 50 stops on a BBS route. readBBS() reads these .csv files and combines these folders into a single dataframe.
bbs <- readBBS(path = path)
The BBS keeps records of migratory or non-breeding birds in a separate file. To read in these species as well, include migrant = T.
bbs <- readBBS(path = path,
migrant = T)
Usually when working with BBS data, a single count for an entire route is sufficient. This function adds up all the point counts within a route to a single value. It also creates an id column that contains the country, state, and route codes.
bbs <- summarizeBBS(bbs)
BBS data uses AOU codes, but specie names are more useful. aouToSp() reads in the SpeciesList.txt, which was downloaded with the raw BBS data, and uses it to replace AOU codes with species names.
bbs <- aouToSp(bbs, path = path)
First, get the latitude and longitude from routes.csv.
locs <- read.csv(paste0(path, "/routes.csv")) %>%
mutate(id = paste0(CountryNum, "_", StateNum, "_", Route)) %>%
select(id, Latitude, Longitude)
Then, read in weather.csv to get observer ID for each observation and RunType, which is used to filter the quality of each observation. You may also want to keep other weather variables, such as temperature and wind speed.
weather <- read.csv(paste0(path, "/weather.csv")) %>%
mutate(id = paste0(CountryNum, "_", StateNum, "_", Route),
year = Year) %>%
select(id, year, ObsN, RunType)
Merge these two dataframe with the count data. We’ll also filter the data in two ways. First, keep only routes that have RunType = 1. Second, we only read in data from 1997 to present, so keep only routes from after 1996.
bbs <- bbs %>%
inner_join(locs, by = "id") %>%
inner_join(weather, by = c("id", "year")) %>%
filter(RunType == 1,
year > 1996)
The BBS data is now ready to use!