This document briefly reviews the functionality of bbsRDM. Although this package can be used to calculate and visualize BBS data using time series, the example at hand presents an application to large spatial transects across the continental United States.
knitr::opts_chunk$set( collapse = TRUE, # comment = "#>", eval=TRUE, echo=FALSE, # cache.path = "cache/", cache=TRUE, warning=FALSE, message=FALSE, out.width="65%", fig.show="" ) options(knitr.graphics.auto_pdf = TRUE) # load("runthrough.RData")
First, install the bbsRDM and regimeDetectionMeasures packages from GitHub.
devtools::install_github("trashbirdecology/bbsRDM", force = FALSE, dependencies = FALSE) devtools::install_github("trashbirdecology/regimedetectionmeasures", force = FALSE, dependencies = FALSE)
library(bbsRDM) library(regimeDetectionMeasures) library(sp) library(raster) library(feather) library(here) library(ggplot2) ggplot2::theme_set(theme_bw())
Next, create diretories for storing the BBS and results locally (if directories exist, this BBS data will not be downloaded (unless you specify 'downloadBBSData=TRUE'), however, the results will be overwritten!).
# a. Create a directory to store and/or load the BBS data as feathers bbsDir <- here::here("bbs_raw_data") # If bbsDir is NOT empty then we will NOT download any data. if (length(list.files(bbsDir, pattern = "*.feather")) > 0) { downloadBBSData = FALSE } else ( { dir.create(bbsDir) downloadBBSData = TRUE } ) # If this returns a warning, proceed with caution as directory already exists, # results WILL be written over # b. Create a directory to store and/or load the BBS data as feathers resultsDir <- here::here("myResults") dir.create(resultsDir) # c. Create directory for storing early warning signal results dir.create(here::here("myResults/ews")) # d. Create directory for storing distance travelled results dir.create( here::here("myResults/distances"))
If necessary, download all the state data. Downloading the entire BBS data takes a while, so only run if you have not recently downloaded the BBS data. If you only need a subset of the data, please see the documentation for bbsAssistant::get_regions
and bbsRDM::getDataBBS
.
# a. Load the regional .txt file from Patuxent's FTP server # (you must be connected to the internet to perform this step) regions <- bbsAssistant::get_regions() # b. Create a series or one filenames for states, regions regionFileName <- regions$zipFileName %>% na.omit() # c. Download and unzip the BBS data. if(downloadBBSData==TRUE){ for(i in 1:length(regionFileName)){ bbsData <- importDataBBS( # arguments for getDataBBS() file = regionFileName[i], dir = "ftp://ftpext.usgs.gov/pub/er/md/laurel/BBS/DataFiles/States/", year = NULL, aou = NULL, countrynum = NULL, states = NULL, # arguments for getRouteInfo(): routesFile = "routes.zip", routesDir = "ftp://ftpext.usgs.gov/pub/er/md/laurel/BBS/DataFiles/", RouteTypeID = 1, # one or more of c(1,2,3) Stratum = NULL, BCR = NULL ) # d. Save the unzipped files to disk. birdsToFeathers(dataIn = bbsData, newDir = bbsDir, filename = regionFileName[i]) # e. Clear large object from memory rm(bbsData)}}
Next we build a spatial sampling grid for aligning BBS data to regularly spaced cells. This is important for spatial interpretation of the regime detection metric results.
# Define the grid's cell size (lat, long; unit:degrees) ## 1 deg latitude ~= 69 miles ## 1 deg longitude ~= 55 miles cs <- c(0.5, 0.5) # default is cell size 0.5 deg lat x 0.5 deg long # Create the grid routes_gridList <- createSamplingGrid(cs = cs)
Now we load in the BBS data from the feathers we created and align with the sampling grid. This requires a bit of memory, proceed with caution.
feathers <- NULL featherNames <- list.files(bbsDir, pattern = ".feather") featherNames <- str_c("/", featherNames) #add separator for (i in 1:length(featherNames)) { feather <- NULL feather <- loadBirdFeathers(newDir = bbsDir, filename = featherNames[i]) feather <- feather %>% dplyr::rename(lat = latitude, long = longitude) %>% left_join(routes_gridList$routes_grid, by = c("countrynum", "statenum", "route", "lat", "long")) feathers <- rbind(feathers, feather) rm(feather) }
Although subsetting the species is optional, this package contains features for subsetting by AOU code, functional groups, or by spatial location (e.g. remove all Montana observaitons).
Subset species according to AOU species codes (i.e. by family, genera, etc..)
For this example we will remove shorebirds, wading birds, and waterfowl (i.e., AOU species' codes 0000:2880). *See R/subsetByAOU.R
source code or documentation for options (see: subset.by
)
# Subset the species feathers <- subsetByAOU(myData = feathers, subset.by= 'remove.shoreWaderFowl')
Note: eval = FALSE...change to true if you wish to evaluate in rmd knitting.
# Create a single list of mass and functional traits. funMass <- funcMass(dataWD = here::here("data/"), fxn = TRUE, # get functional trait data? mass = TRUE) # get body mass data? # Combine the functional trqits and/or body mass bbsData <- mergeFunMassBBS(bbsData = feathers[1:1e3,], funMass = funMass)
# Which metrics do you want to calculate? metrics.to.calc <- c("distances", "ews") # If calculating "EWSs, you can calculate select metrics. ## Default = all early-warning signals, FI, and VI to.calc = c("EWS", "FI", "VI") # Choose spatial or temporal analysis direction <- "South-North" # choose one of : 'South-North', 'East-West', or 'temporal' # Choose the fill value as the unobserved value. fill = 0 # Minimum number of sites within each transect min.samp.sites = 8 # Minimum number of BBS routes within each moving window min.window.dat = 3 # Which Equation of Fisher Information to use (default = 7.12) fi.equation = "7.12" # By what % of the entire data should the window move? winMove = 0.25 # Define some filtering and labeling parameters based on # direction of spatial analysis if (direction == "South-North") { dir.use = unique(feathers$colID) %>% na.omit(colID) %>% sort()} if (direction == "East-West") { dir.use = unique(feathers$rowID) %>% na.omit(rowID) %>% sort()}
For this (spatial) example, we will analyze only every tenth year
# Get all possible years years.use = unique(feathers$year) # Keep only the years which are divisible by T T = 10 years.use <- years.use[which(years.use %% T == 0 & years.use > 1975)] %>% sort()
This section will loop through years.use
and dir.use
, running each BBS route (temporal analysis) or spatial transect by year (spatial analysis) at a time. Results are saved in directories created in section below. Please note: depending on the # of years and # spatial transects, this could take a while.
###################################### ### NOTE: change eval=TRUE if the #### ### results are not yet stored in file. ###################################### for (j in 1:(length(dir.use))) { # For east-west analysis if (direction == "East-West"){ birdsData <- feathers %>% filter(rowID == dir.use[j]) %>% mutate(direction = direction, dirID = dir.use[j]) } # For south-north analysis if (direction == "South-North"){ birdsData <- feathers %>% filter(colID == dir.use[j]) %>% mutate(direction = direction, dirID = dir.use[j]) } if (nrow(birdsData) < min.samp.sites) { next(print(paste0("Not enough data to analyze. Skipping j-loop ", dir.use[j]))) } # Analyze the subset of data for (i in 1:length(years.use)){ # a. Subset the data birdData <- birdsData %>% filter(year == years.use[i]) %>% dplyr::rename(variable = aou, value = stoptotal) if (nrow(birdData) == 0){ next } # b. Munge the data further birdData <- mungeSubsetData(birdData) # c. Calculate metrics calculateMetrics(dataIn = birdData, metrics.to.calc, direction = direction, yearInd = years.use[i]) message(paste0("End i-loop (years) ", i, " of ", length(years.use))) } message(paste0("End j-loop (transects) ", j, " of ", length(dir.use))) }
First, use the function 'importResults' to import and combine the results as created in the previous code chunk. The following chunk chunk will import the EWS results and the distance results separately, combining each into their own data frames.
# Import EWS results results_ews <- importResults(resultsDir = resultsDir, myPattern = 'ews', subset.by = direction) %>% # assign the end of the window as the cellID mutate(cellID = cellID_max) # glimpse(results_ews) # Import distance results results_dist <- importResults(resultsDir = resultsDir, myPattern = 'distances', subset.by = direction) # glimpse(results_dist)
Next, get the results to align with our sampling grid for visualizing results across space.
# Get the spatial sampling grid coordinates coords_grd <- cbind(routes_gridList$sp_grd@data, coordinates(routes_gridList$sp_grd)) %>% rename(lat = s2, long = s1, cellID = id) # Join coords_grd with results # note: a full join will likely produce many cells with NO results data.. # but NO lat or long should == NA! results_dist <- full_join(coords_grd, results_dist) %>% na.omit(metricType) results_ews <- full_join(coords_grd, results_ews) %>% na.omit(metricType) %>% dplyr::select(-cellID_min,-cellID_max, -winStart , -winStop) # Set coordinate system and projection of both results coordinates(results_dist) <- coordinates(results_ews) <- c("long", "lat") sp::proj4string(results_dist) <- sp::proj4string(results_ews) <- sp::CRS("+proj=longlat +datum=WGS84")
First, specify plotting parameters (below). We can visualize either the distance results (results_dist
) or the early-warning signal results (results_ews
). For this example we will visualize the distance results.
# Define the results we want to visualize plotResults <- results_dist # Sort the years year.ind <- unique(plotResults@data$year) %>% sort() # Create a label for plotting, depending on direction sortVar.lab <- ifelse(unique(plotResults@data$direction)=="South-North", "latitude", "longitude")
# Specify the transect # we want to see dirID.ind <- 13 metric.ind <- "s" # build plot (p.s <- sort.year.line(plotResults, metric.ind, year.ind, dirID.ind, dirInd, scale = T, center = T))
Plot the distance travelled, s for transect # 13 (Figure \@ref(fig:plots)). This measure indicates how quickly the multivariable system is moving through phase space. Notice that the absolute value of s increases over time. This is unsurprising given that the number of species in BBS censuses has increased over time. The importance of this phenomenon should be evaluated at the local scale.
# Specify the transect # we want to see dirID.ind <- 13 metric.ind <- "dsdt" (p.dsdt <- sort.year.line(plotResults, metric.ind, year.ind, dirID.ind, dirInd, scale = T, center = T))
Plot the velocity of the distance travelled, dsdt for transect # 13 (Figure \@ref(fig:plotdsdt)). Rapid changes in the velocity of the distance travelled indicates rapid species turnover, or large changes in the abundances of a few species. The ecological significance of rapid change in community abundances, or among the state variables used to calculate distance travelled and velocity, should be evaluated at local and sub-regional scales.
library(viridis) temp <- as.data.frame(plotResults) %>% filter( year %in% c(1980, 1990, 2000, 2010), metricType == "s") %>% group_by(dirID, year, analysis) %>% mutate(scaledMetricValue.bycol = base::scale(metricValue, center =T, scale = T)) %>% ungroup() %>% na.omit(metricValue) ggplot(temp) + geom_raster(aes(x = long, y = lat, fill = scaledMetricValue.bycol)) + coord_fixed(ratio = 1) + scale_fill_viridis(direction = -1) + facet_wrap(~year, strip.position = "top", nrow=2) + theme(legend.position = "none")
Plot the distance travelled across the entire range (Figure \@ref(fig:plotsus)).
library(viridis) temp <- as_tibble(plotResults) %>% filter(year %in% c(1980, 1990, 2000, 2010), metricType == "dsdt") %>% group_by(dirID, year) %>% mutate(logmetric = log(metricValue)) %>% ungroup() ggplot(temp) + geom_raster(aes(x = long, y = lat, fill = logmetric)) + coord_fixed(ratio = 1) + scale_fill_viridis(direction = -1) + facet_wrap(~year, strip.position = "top", nrow=2) + theme(legend.position = "none")
Plot the velocity of the distance travelled across the entire range (Figure \@ref(fig:plotdsdtus)).
t <- as_tibble(plotResults@data) %>% filter(year %in% c(1980, 1990, 2000, 2010), metricType == "s") %>% group_by(dirID, year, analysis) %>% mutate(scaledMetricValue.bycol = base::scale(metricValue, center =T, scale = T)) %>% ungroup() %>% group_by(metricType, cellID) %>% arrange(year) %>% mutate(dScaledMetricValue.bycol = scaledMetricValue.bycol - lag(scaledMetricValue.bycol)) ggplot() + geom_raster(data = t, aes(x = long, y = lat, fill = dScaledMetricValue.bycol)) + coord_fixed(ratio = 1) + scale_fill_viridis(direction = -1) + facet_wrap(~year, strip.position = "top") + theme(legend.position = "none")+ geom_point(data = milBases)
# save.image("runthrough.RData")
Use the function getMilBases()
to retrieve MIRTA military base point shapefiles:
milBases <- getMilBases() # default arguments will download the MIRTA FY 2018 points sp::proj4string(milBases) <- sp::CRS("+proj=longlat +datum=WGS84")# Set projection to WGS84 lat long friendly
Next, we need to assign the row and column ID numbers from our sampling grid to each base location, such that we can visualize the results of the regime detection measures within our spatial sampling grid.
# Extract the CELL ID in which each military base falls. milBases.df <- milBases %>% as.data.frame() %>% rename(long = coords.x1, lat = coords.x2) milBases.df$cellID <- milBases %over% routes_gridList$sp_grd # remove bases outside our sampling grid milBases.df <- milBases.df %>% filter(!is.na(cellID))
ggplot(milBases.df, aes(long, lat, color = COMPONENT))+ geom_point()+ ggtitle("Military Bases in the Continental U.S.")
Plot the location of military bases (Figure \@ref(fig:plotmilbases)).
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.