Set-up
Install necessary packages and import appropriate data
knitr::opts_chunk$set(echo = TRUE)
pacman::p_load(tidyverse, readxl, raster, vegan, tigris, sf, sjPlot, sp, spOccupancy, ggrepel, lme4, lmerTest, MuMIn, brms, MCMCvis, bayesplot, patchwork)
set.seed(97)
# Tree PCQ Data
tree_data <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/05_SharedData/Field_Data_FL_AL_MS.xlsx",
sheet = "Tree_PCQ")
# CWD Data
cwd_data <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/05_SharedData/Field_Data_FL_AL_MS.xlsx",
sheet = "CWD")
# Soil Data
fuel_data <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/05_SharedData/Field_Data_FL_AL_MS.xlsx",
sheet = "Fuel_Sampling")
# Veg Data
Veg_Cover <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/05_SharedData/Field_Data_FL_AL_MS.xlsx",
sheet = "Veg_Cover")
# Shrub Cover Data
shrub_data <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/05_SharedData/Field_Data_FL_AL_MS.xlsx",
sheet = "Shrub_Cover")
# Site Data
CameraData <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/04_Wildlife/02_Data/CameraData.xlsx")
CameraLoc <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/04_Wildlife/02_Data/CameraLoc.xlsx",
sheet = "CameraLocations")
# Add effort data
effort_matrix <- read_excel("C:/Users/DrewIvory/OneDrive - University of Florida/Desktop/School/PHD/01_Projects/04_Wildlife/02_Data/CameraLoc.xlsx",
sheet = "Effort_Matrix_Full") %>%
pivot_longer(cols = matches("^202[4-5]-"), names_to = "week", values_to = "days") %>%
filter(days == "7") %>%
dplyr::select(Plot, week)
Number of quadrats sampled per plot
# Count the total number of quadrats per plot
quadrat_count <- Veg_Cover %>%
group_by(Plot) %>%
summarize(total_quadrats = n_distinct(Quadrat), .groups = "drop")
Filter All data to only include specified species (Per PLANTS
database)
#Filter tree data to only include trees with "tree" in the growth column
tree_data <- dplyr::filter(tree_data, Growth == "Tree")
#Filter Veg Cover to exclude Shrubs and Trees
Veg_Cover <- dplyr::filter(Veg_Cover, Growth != "Shrub" & Growth != "Tree")
#Filter Shrub Cover to only include Shrubs and Trees
shrub_data <- dplyr::filter(shrub_data, Growth == "Shrub" | Growth == "Tree")
Provides the option to rarify vegetative species
# Calculate the total number of sites
total_sites <- nrow(CameraLoc)
# Function to filter data by frequency
filter_by_frequency <- function(df) {
# Group data by species and calculate the frequency
freq <- df %>%
group_by(Species) %>%
summarise(Frequency = n_distinct(Plot) / nrow(CameraLoc) * 100) %>%
filter(Frequency >= 0)
# Filter the original data to include only species with frequency >= XX%
filtered_df <- df %>%
filter(Species %in% freq$Species)
return(filtered_df)
}
# Filter tree data by frequency
tree_data <- filter_by_frequency(tree_data)
# Filter Veg Cover data by frequency
Veg_Cover <- filter_by_frequency(Veg_Cover)
# Filter Shrub Cover data by frequency
shrub_data <- filter_by_frequency(shrub_data)
Shrub Cover Conversion
# Total length of Shrub cover at a site
shrub_cover <- shrub_data %>%
mutate(Cover = Line_End - Line_Start) %>%
group_by(Species_Name, Plot) %>%
summarise(Shrub_Total_Cover = sum(Cover, na.rm = TRUE), .groups = "drop") %>%
mutate(Shrub_Percent_Cover = Shrub_Total_Cover / 3000 * 100)
# Summed length of shrub over at a site
shrub_cover_summed <- shrub_cover %>%
group_by(Plot) %>%
summarize(total_shrub_cover = sum(Shrub_Total_Cover, na.rm = TRUE), .groups = "drop")
Herbacous Cover Conversion
# Combine Plot and Quadrat columns
Veg_Cover <- Veg_Cover %>%
mutate(Plot_Quadrat = paste(Plot, Quadrat, sep = '_'))
# Join with CogonSites to get site information
Veg_Cover <- Veg_Cover %>%
left_join(CameraLoc, by = "Plot")
# Sum species cover across quadrats for each species at each plot
veg_cover_summed <- Veg_Cover %>%
group_by(Plot, Species_Name) %>%
summarize(total_cover = sum(Cover_Per, na.rm = TRUE), .groups = "drop")
# Calculate average herbaceous species cover
avg_species_cover <- veg_cover_summed %>%
left_join(quadrat_count, by = "Plot") %>%
mutate(avg_cover = total_cover / total_quadrats)
Merging Herb cover with Shrub
This species matrix includes herbaceous and shrub species
# Merge shrub cover with herbaceous average cover
combined_cover <- avg_species_cover %>%
full_join(
shrub_cover %>%
dplyr::select(Plot, Species_Name, Shrub_Percent_Cover),
by = c("Plot", "Species_Name")
) %>%
mutate(
overlap_flag = ifelse(!is.na(avg_cover) & !is.na(Shrub_Percent_Cover), TRUE, FALSE), # Flag overlaps
final_cover = case_when(
!is.na(avg_cover) & is.na(Shrub_Percent_Cover) ~ avg_cover, # Use herbaceous cover if no shrub data
is.na(avg_cover) & !is.na(Shrub_Percent_Cover) ~ Shrub_Percent_Cover, # Use shrub cover if no herbaceous data
TRUE ~ NA_real_ # NA where overlaps exist
)
)
# Species Matrix
species_matrix <- combined_cover %>%
dplyr::select(Plot, Species_Name, final_cover) %>%
pivot_wider(
names_from = Species_Name,
values_from = final_cover,
values_fill = 0
)
Summarize Cogongrass Cover
avg_cogongrass_cover <- species_matrix %>%
group_by(Plot) %>%
summarize(Avg_Cogongrass_Cover = sum(Imperata_cylindrica, na.rm = TRUE) / n(), .groups = "drop")
Herbacous Shannon Diversity Index
# Summarize species cover by site
site_species_cover <- Veg_Cover %>%
group_by(Plot, Species_Name) %>%
summarize(total_cover = sum(Cover_Per, na.rm = TRUE)) %>%
ungroup()
## `summarise()` has grouped output by 'Plot'. You can override using the
## `.groups` argument.
## Remove all Imperata_cylindrica_Live and Imperata_cylindrica from species
site_species_cover <- site_species_cover %>%
filter(Species_Name != "Imperata_cylindrica_Live" & Species_Name != "Imperata_cylindrica")
# Calculate Shannon diversity per site
Veg_shannon_diversity <- site_species_cover %>%
group_by(Plot) %>%
mutate(proportion = total_cover / sum(total_cover)) %>%
summarize(Veg_shannon_index = -sum(proportion * log(proportion), na.rm = TRUE))
print(Veg_shannon_diversity)
## # A tibble: 206 × 2
## Plot Veg_shannon_index
## <chr> <dbl>
## 1 BI200 2.75
## 2 BI201 2.70
## 3 BI202 2.59
## 4 BI97 1.61
## 5 BI99 2.97
## 6 BN210 2.97
## 7 BN211 2.43
## 8 BN212 2.22
## 9 BN96 3.05
## 10 BN98 2.79
## # ℹ 196 more rows
Vegetation Height
if (!is.numeric(fuel_data$Height)) {
fuel_data$Height <- as.numeric(as.character(fuel_data$Height))
}
## Warning: NAs introduced by coercion
# Calculate average vegetation height per plot
veg_height <- fuel_data %>%
group_by(Plot) %>%
summarize(avg_veg_height = mean(Height, na.rm = TRUE), .groups = "drop")
Tree Metrics
# Tree density from point-centered quarter data
if (!is.numeric(tree_data$Distance)) {
tree_data$Distance <- as.numeric(as.character(tree_data$Distance))
}
tree_density_data <- tree_data %>%
group_by(Plot) %>%
summarize(Average_Distance = mean(Distance) / 100, # Convert to meters
Tree_Density = 10000 / (Average_Distance^2)) # Convert to trees per hectare
# Average canopy cover from vegetation quadrats
tree_canopy_data <- Veg_Cover %>%
distinct(Plot, Quadrat, .keep_all = TRUE) %>% # Ensure each quadrat counts once per plot
group_by(Plot) %>%
summarize(Avg_Canopy_Cover = mean(Canopy_Cover, na.rm = TRUE), .groups = "drop") # Calculate the average canopy cover per plot
cor(tree_density_data$Tree_Density, tree_canopy_data$Avg_Canopy_Cover)
## [1] 0.2742307
CWD Clean
cwd_pa <- cwd_data %>%
group_by(Plot) %>%
summarise(
CWD_presence = as.integer(any(Line_Start > 0 & Line_End > 0)),
.groups = "drop"
)
Objective 1: Assessing if cogongrass invasion-related factors
influence wildlife presence
Add Covariates
CameraLoc <- CameraLoc %>%
left_join(cwd_pa, by = "Plot") %>%
left_join(Veg_shannon_diversity, by = "Plot") %>%
left_join(avg_cogongrass_cover, by = "Plot") %>%
left_join(shrub_cover_summed %>% dplyr::select(Plot, total_shrub_cover), by = "Plot") %>%
left_join(veg_height, by = "Plot") %>%
left_join(tree_density_data %>% dplyr::select(Plot, Tree_Density), by = "Plot") %>%
left_join(tree_canopy_data %>% dplyr::select(Plot, Avg_Canopy_Cover), by = "Plot") %>%
dplyr::select(-Authority)
Distribution of Covariates
par(mfrow = c(3, 3))
hist(CameraLoc$Cogon_Patch_Size, main = "Cogon Patch Size", xlab = "Size (m²)")
hist(CameraLoc$Veg_shannon_index, main = "Vegetation Shannon Index", xlab = "Shannon Index")
hist(CameraLoc$total_shrub_cover, main = "Total Shrub Cover", xlab = "Shrub Cover (%)")
hist(CameraLoc$Avg_Cogongrass_Cover, main = "Average Cogongrass Cover", xlab = "Cogongrass Cover (%)")
hist(CameraLoc$Tree_Density, main = "Tree Density", xlab = "Trees per ha")
hist(CameraLoc$Avg_Canopy_Cover, main = "Average Canopy Cover", xlab = "Canopy Cover (%)")
hist(CameraLoc$avg_veg_height, main = "Average Vegetation Height", xlab = "Height (cm)")
table(CameraLoc$CWD_presence)
##
## 0 1
## 25 7
par(mfrow = c(1, 1))

Subset to mammals with >10 observations
# Group by Name and count the number of observations
species_counts <- CameraData %>%
filter((Class == "Mammalia" & Name != "Odocoileus_virginianus") | Name == "Meleagris_gallopavo") %>%
group_by(Name) %>%
summarize(count = n(), .groups = "drop")
# Filter for species with count greater than 10
species_subset <- species_counts %>%
filter(count > 10) %>%
pull(Name)
# Filter CameraData to only include species with count > 10
CameraData <- CameraData %>%
filter(Name %in% species_subset)
Create Detection Matrix
# Format Data Weekly
observations_weekly <- CameraData %>%
group_by(Plot, week = format(as.Date(Date), "%Y-%U"), Name) %>%
summarise(observations = n(), .groups = 'drop')
# Merge with Effort Matrix to include only valid weeks
observations_weekly <- effort_matrix %>%
left_join(observations_weekly, by = c("Plot" = "Plot", "week")) %>%
replace_na(list(observations = 0))
# Convert to wide format
observations_species <- observations_weekly %>%
pivot_wider(names_from = Name, values_from = observations, values_fill = list(observations = 0)) %>%
dplyr::select(-"NA")
# Create detection array
site_names <- sort(unique(observations_species$Plot))
species_names <- setdiff(colnames(observations_species), c("Plot", "week"))
num_sites <- length(site_names)
num_weeks <- length(unique(observations_species$week))
num_species <- length(species_names)
detection_array <- array(0, dim = c(num_sites, num_weeks, num_species))
dimnames(detection_array) <- list(site_names, unique(observations_species$week), species_names)
for (s in seq_along(species_names)) {
species_col <- species_names[s]
for (i in seq_along(site_names)) {
site <- site_names[i]
for (j in seq_along(unique(observations_species$week))) {
week <- unique(observations_species$week)[j]
detection_array[i, j, s] <- ifelse(
any(observations_species$Plot == site & observations_species$week == week & observations_species[[species_col]] > 0),
1, 0
)
}
}
}
dim(detection_array) # Should be num_sites x num_weeks x num_species
## [1] 32 36 8
Prepare Site Covariates
# Duplicate CameraLoc to be used in Objective 2
CameraLoc_O2 <- CameraLoc
# Standardize the covariates
CameraLoc <- CameraLoc %>%
dplyr::select(-Plot, -Camera, -Lat, -Long, -Status, - Start_Date)
covariates_matrix <- as.matrix(CameraLoc)
rownames(covariates_matrix) <- site_names
# Standardizing covariates
covariates_matrix <- scale(covariates_matrix)
## Correlation
cor_mat <- cor(covariates_matrix)
# Create week matrix for covariate structure [site x week]
week_vals <- unique(observations_species$week)
week_matrix <- matrix(rep(week_vals, each = num_sites), nrow = num_sites, ncol = num_weeks, byrow = FALSE)
# Create detection covariate list
det.covs <- list(
shrub_cover = covariates_matrix[, "total_shrub_cover"],
veg_height = covariates_matrix[, "avg_veg_height"],
week = week_matrix
)
# Remove dash and convert to numeric
week_numeric <- as.numeric(gsub("-", "", det.covs$week))
## Scale and center week_numeric
week_numeric <- scale(week_numeric)
# Reshape into the original 32x36 matrix
det.covs$week <- matrix(week_numeric, nrow = 32, ncol = 36)
str(det.covs)
## List of 3
## $ shrub_cover: Named num [1:32] 1.526 0.5 -0.153 -1.003 -0.811 ...
## ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## $ veg_height : Named num [1:32] 0.466 -1.044 1.181 0.879 1.684 ...
## ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## $ week : num [1:32, 1:36] -0.613 -0.613 -0.613 -0.613 -0.613 ...
Combine data into a list
This requires combining the presence data and the site covariate data
into a single list. This also means that the presence data is in a 3-d
format.
# Combine into a named list
data_list <- list(
y = detection_array,
occ.covs = covariates_matrix,
det.covs = det.covs
#,
#coords = coords
)
str(data_list)
## List of 3
## $ y : num [1:32, 1:36, 1:8] 0 0 0 0 0 0 0 0 0 0 ...
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## .. ..$ : chr [1:36] "2024-29" "2024-30" "2024-31" "2024-32" ...
## .. ..$ : chr [1:8] "Canis_latrans" "Procyon_lotor" "Dasypus_novemcinctus" "Lynx_rufus" ...
## $ occ.covs: num [1:32, 1:12] -0.237 -0.446 -0.337 -0.308 0.039 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## .. ..$ : chr [1:12] "Cogon_Patch_Size" "VegetationDiversity" "PostTreatmentDensities" "Auth" ...
## ..- attr(*, "scaled:center")= Named num [1:12] 458.388 21.875 0.898 2.844 16.188 ...
## .. ..- attr(*, "names")= chr [1:12] "Cogon_Patch_Size" "VegetationDiversity" "PostTreatmentDensities" "Auth" ...
## ..- attr(*, "scaled:scale")= Named num [1:12] 1027.633 6.871 1.232 0.808 0.397 ...
## .. ..- attr(*, "names")= chr [1:12] "Cogon_Patch_Size" "VegetationDiversity" "PostTreatmentDensities" "Auth" ...
## $ det.covs:List of 3
## ..$ shrub_cover: Named num [1:32] 1.526 0.5 -0.153 -1.003 -0.811 ...
## .. ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## ..$ veg_height : Named num [1:32] 0.466 -1.044 1.181 0.879 1.684 ...
## .. ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## ..$ week : num [1:32, 1:36] -0.613 -0.613 -0.613 -0.613 -0.613 ...
Convert occupancy and detection covariates to a dataframe (from
matrix)
I am unsure why I only had an issue with total shrub cover, but this
should fix the “cannot find” issue.
# Convert occupancy and detection covariates to a dataframe
data_list[["occ.covs"]] <- as.data.frame(data_list[["occ.covs"]])
data_list[["occ.covs"]]$total_shrub_cover <- as.numeric(data_list[["occ.covs"]]$total_shrub_cover)
#data_list[["det.covs"]] <- as.data.frame(data_list[["det.covs"]])
#data_list[["det.covs"]]$total_shrub_cover <- as.numeric(data_list[["det.covs"]]$total_shrub_cover)
# Make species the first dimension
data_list$y <- aperm(data_list$y, c(3, 1, 2))
dimnames(data_list$y) <- list(species = dimnames(data_list$y)[[1]],
site = dimnames(data_list$y)[[2]],
week = dimnames(data_list$y)[[3]])
str(data_list)
## List of 3
## $ y : num [1:8, 1:32, 1:36] 0 0 0 0 0 0 0 0 0 0 ...
## ..- attr(*, "dimnames")=List of 3
## .. ..$ species: chr [1:8] "Canis_latrans" "Procyon_lotor" "Dasypus_novemcinctus" "Lynx_rufus" ...
## .. ..$ site : chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## .. ..$ week : chr [1:36] "2024-29" "2024-30" "2024-31" "2024-32" ...
## $ occ.covs:'data.frame': 32 obs. of 12 variables:
## ..$ Cogon_Patch_Size : num [1:32] -0.237 -0.446 -0.337 -0.308 0.039 ...
## ..$ VegetationDiversity : num [1:32] -0.273 0.455 1.619 -0.273 2.929 ...
## ..$ PostTreatmentDensities: num [1:32] 0.432 -0.729 0.432 2.169 1.13 ...
## ..$ Auth : num [1:32] -2.28 -2.28 -1.04 -1.04 -1.04 ...
## ..$ UTM_Zone : num [1:32] -0.473 -0.473 -0.473 -0.473 -0.473 ...
## ..$ CWD_presence : num [1:32] 1.86 -0.521 -0.521 -0.521 -0.521 ...
## ..$ Veg_shannon_index : num [1:32] 0.6829 0.0427 0.7279 -0.5991 1.1371 ...
## ..$ Avg_Cogongrass_Cover : num [1:32] -0.154 -0.708 0.308 2.045 1.121 ...
## ..$ total_shrub_cover : num [1:32] 1.526 0.5 -0.153 -1.003 -0.811 ...
## ..$ avg_veg_height : num [1:32] 0.466 -1.044 1.181 0.879 1.684 ...
## ..$ Tree_Density : num [1:32] -0.3629 -0.3564 -0.5111 3.5896 0.0958 ...
## ..$ Avg_Canopy_Cover : num [1:32] 0.1362 -0.0252 -0.9132 0.782 -1.9627 ...
## $ det.covs:List of 3
## ..$ shrub_cover: Named num [1:32] 1.526 0.5 -0.153 -1.003 -0.811 ...
## .. ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## ..$ veg_height : Named num [1:32] 0.466 -1.044 1.181 0.879 1.684 ...
## .. ..- attr(*, "names")= chr [1:32] "BI201" "BN211" "EI100" "EI102" ...
## ..$ week : num [1:32, 1:36] -0.613 -0.613 -0.613 -0.613 -0.613 ...
Site Covariates
# Define detection formulas
det.null <- ~ 1
det.full <- ~ shrub_cover + veg_height + week
det.cover <- ~ shrub_cover + veg_height
det.week <- ~ week
det.week.quad <- ~ week + I(week^2)
det.full.quad <- ~ shrub_cover + veg_height + week + I(week^2)
# Define occupancy formulas
occ.null <- ~ 1
occ.full <- ~ #Cogon_Patch_Size +
Veg_shannon_index + total_shrub_cover +
Avg_Cogongrass_Cover +
#Tree_Density +
Avg_Canopy_Cover +
avg_veg_height + CWD_presence + (1 | Auth)
occ.full.quad <- ~ #Cogon_Patch_Size +
Veg_shannon_index + total_shrub_cover +
Avg_Cogongrass_Cover + I(Avg_Cogongrass_Cover^2) +
#Tree_Density +
Avg_Canopy_Cover +
avg_veg_height + CWD_presence + (1 | Auth)
occ.cover <- ~ Avg_Cogongrass_Cover + total_shrub_cover +
avg_veg_height + CWD_presence + (1 | Auth)
occ.canopy <- ~ #Tree_Density +
Avg_Canopy_Cover + Avg_Cogongrass_Cover + (1 | Auth)
occ.canopy.quad <- ~ #Tree_Density +
Avg_Canopy_Cover + Avg_Cogongrass_Cover + I(Avg_Cogongrass_Cover^2) + (1 | Auth)
occ.move <- ~ #Cogon_Patch_Size +
Avg_Cogongrass_Cover + total_shrub_cover + (1 | Auth)
occ.move.quad <- ~ #Cogon_Patch_Size +
Avg_Cogongrass_Cover + I(Avg_Cogongrass_Cover^2) +
total_shrub_cover + (1 | Auth)
occ.forage <- ~ Veg_shannon_index + Avg_Cogongrass_Cover + (1 | Auth)
occ.forage.quad <- ~ Veg_shannon_index + Avg_Cogongrass_Cover +
I(Avg_Cogongrass_Cover^2) + (1 | Auth)
occ.cogon <- ~ Avg_Cogongrass_Cover + (1 | Auth)
occ.cogon.quad <- ~ Avg_Cogongrass_Cover + I(Avg_Cogongrass_Cover^2) + (1 | Auth)
Priors
priors <- list(
# Community-level effects
beta.comm.normal = list(mean = 0, var = 2.73),
alpha.comm.normal = list(mean = 0, var = 2.73),
# Species-level variance (hierarchical shrinkage)
tau.sq.beta.ig = list(2, 1),
tau.sq.alpha.ig = list(2, 1)#,
# Spatial variance
#sigma.sq.ig = list(3, 2),
# Spatial decay
#phi.unif = list(0.05, 3) # 0.05 (5km) local & 3 (300 km) large-scale
)
Run the occupancy model
Model Comparison
waicOcc(ms_full_full, by.sp = FALSE)
## elpd pD WAIC
## -979.78438 99.55512 2158.67899
waicOcc(ms_null_null, by.sp = FALSE)
## elpd pD WAIC
## -1066.58379 27.18168 2187.53094
waicOcc(ms_cover_forage, by.sp = FALSE)
## elpd pD WAIC
## -1014.4453 77.5569 2184.0043
waicOcc(ms_cover_move, by.sp = FALSE)
## elpd pD WAIC
## -1010.44288 78.68746 2178.26068
waicOcc(ms_cover_canopy, by.sp = FALSE)
## elpd pD WAIC
## -1000.9475 73.6221 2149.1392
waicOcc(ms_cover_canopyQ, by.sp = FALSE)
## elpd pD WAIC
## -997.55704 77.17395 2149.46199
waicOcc(ms_weekQ_cogonQ, by.sp = FALSE)
## elpd pD WAIC
## -1036.63692 59.91148 2193.09680
waicOcc(ms_fullQ_moveQ, by.sp = FALSE)
## elpd pD WAIC
## -994.77983 97.45321 2184.46608
waicOcc(ms_fullQ_cover, by.sp = FALSE)
## elpd pD WAIC
## -992.5583 110.7280 2206.5727
waicOcc(ms_fullQ_forage, by.sp = FALSE)
## elpd pD WAIC
## -1003.20793 93.49621 2193.40828
waicOcc(ms_fullQ_fullQ, by.sp = FALSE)
## elpd pD WAIC
## -970.1324 108.8342 2157.9332
Model Validation
This test explains how well the model fits that data at the community
and species level. I believe 0.5 is the target p-value, though how far
from this number is considered adequate, I do not know yet. I believe
this is a good place to check when thinking about which species we
include in the model.
ppc_ft <- ppcOcc(
ms_cover_canopy,
fit.stat = "freeman-tukey",
group = 1
)
## Currently on species 1 out of 8
## Currently on species 2 out of 8
## Currently on species 3 out of 8
## Currently on species 4 out of 8
## Currently on species 5 out of 8
## Currently on species 6 out of 8
## Currently on species 7 out of 8
## Currently on species 8 out of 8
ppc_chisq <- ppcOcc(
ms_cover_canopy,
fit.stat = "chi-square",
group = 1
)
## Currently on species 1 out of 8
## Currently on species 2 out of 8
## Currently on species 3 out of 8
## Currently on species 4 out of 8
## Currently on species 5 out of 8
## Currently on species 6 out of 8
## Currently on species 7 out of 8
## Currently on species 8 out of 8
summary(ppc_ft)
##
## Call:
## ppcOcc(object = ms_cover_canopy, fit.stat = "freeman-tukey",
## group = 1)
##
## Samples per Chain: 10000
## Burn-in: 3000
## Thinning Rate: 2
## Number of Chains: 4
## Total Posterior Samples: 14000
##
## ----------------------------------------
## Community Level
## ----------------------------------------
## Bayesian p-value: 0.3643
##
## ----------------------------------------
## Species Level
## ----------------------------------------
## Canis_latrans Bayesian p-value: 0.5601
## Procyon_lotor Bayesian p-value: 0.0932
## Dasypus_novemcinctus Bayesian p-value: 1e-04
## Lynx_rufus Bayesian p-value: 0.485
## Didelphis_virginiana Bayesian p-value: 0.3475
## Sylvilagus_floridanus Bayesian p-value: 0.4583
## Meleagris_gallopavo Bayesian p-value: 0.6161
## Sciurus_carolinensis Bayesian p-value: 0.3541
## Fit statistic: freeman-tukey
summary(ppc_chisq)
##
## Call:
## ppcOcc(object = ms_cover_canopy, fit.stat = "chi-square", group = 1)
##
## Samples per Chain: 10000
## Burn-in: 3000
## Thinning Rate: 2
## Number of Chains: 4
## Total Posterior Samples: 14000
##
## ----------------------------------------
## Community Level
## ----------------------------------------
## Bayesian p-value: 0.3012
##
## ----------------------------------------
## Species Level
## ----------------------------------------
## Canis_latrans Bayesian p-value: 0.4527
## Procyon_lotor Bayesian p-value: 0.0038
## Dasypus_novemcinctus Bayesian p-value: 0
## Lynx_rufus Bayesian p-value: 0.4317
## Didelphis_virginiana Bayesian p-value: 0.2672
## Sylvilagus_floridanus Bayesian p-value: 0.2292
## Meleagris_gallopavo Bayesian p-value: 0.6874
## Sciurus_carolinensis Bayesian p-value: 0.3377
## Fit statistic: chi-square
Posterior Checks
n.sp <- dim(data_list$y)[1]
sp.names <- dimnames(data_list$y)[[1]]
# ── 1. Run PPC ───────────────────────────────────────────────────────────────
ppc_ft <- ppcOcc(ms_cover_canopy, fit.stat = "freeman-tukey", group = 1)
## Currently on species 1 out of 8
## Currently on species 2 out of 8
## Currently on species 3 out of 8
## Currently on species 4 out of 8
## Currently on species 5 out of 8
## Currently on species 6 out of 8
## Currently on species 7 out of 8
## Currently on species 8 out of 8
ppc_chisq <- ppcOcc(ms_cover_canopy, fit.stat = "chi-square", group = 1)
## Currently on species 1 out of 8
## Currently on species 2 out of 8
## Currently on species 3 out of 8
## Currently on species 4 out of 8
## Currently on species 5 out of 8
## Currently on species 6 out of 8
## Currently on species 7 out of 8
## Currently on species 8 out of 8
summary(ppc_ft)
##
## Call:
## ppcOcc(object = ms_cover_canopy, fit.stat = "freeman-tukey",
## group = 1)
##
## Samples per Chain: 10000
## Burn-in: 3000
## Thinning Rate: 2
## Number of Chains: 4
## Total Posterior Samples: 14000
##
## ----------------------------------------
## Community Level
## ----------------------------------------
## Bayesian p-value: 0.3653
##
## ----------------------------------------
## Species Level
## ----------------------------------------
## Canis_latrans Bayesian p-value: 0.564
## Procyon_lotor Bayesian p-value: 0.095
## Dasypus_novemcinctus Bayesian p-value: 2e-04
## Lynx_rufus Bayesian p-value: 0.4871
## Didelphis_virginiana Bayesian p-value: 0.3489
## Sylvilagus_floridanus Bayesian p-value: 0.4503
## Meleagris_gallopavo Bayesian p-value: 0.6213
## Sciurus_carolinensis Bayesian p-value: 0.3557
## Fit statistic: freeman-tukey
summary(ppc_chisq)
##
## Call:
## ppcOcc(object = ms_cover_canopy, fit.stat = "chi-square", group = 1)
##
## Samples per Chain: 10000
## Burn-in: 3000
## Thinning Rate: 2
## Number of Chains: 4
## Total Posterior Samples: 14000
##
## ----------------------------------------
## Community Level
## ----------------------------------------
## Bayesian p-value: 0.3024
##
## ----------------------------------------
## Species Level
## ----------------------------------------
## Canis_latrans Bayesian p-value: 0.4602
## Procyon_lotor Bayesian p-value: 0.0039
## Dasypus_novemcinctus Bayesian p-value: 0
## Lynx_rufus Bayesian p-value: 0.4361
## Didelphis_virginiana Bayesian p-value: 0.2615
## Sylvilagus_floridanus Bayesian p-value: 0.2352
## Meleagris_gallopavo Bayesian p-value: 0.6906
## Sciurus_carolinensis Bayesian p-value: 0.3319
## Fit statistic: chi-square
# ── 2. Scatter plot: replicated vs. observed fit statistic ───────────────────
# For msPGOcc, fit.y and fit.y.rep are matrices [n.samples x n.species]
par(mfrow = c(2, ceiling(n.sp / 2)), mar = c(4, 4, 2, 1))
for (i in seq_len(n.sp)) {
ppc.df <- data.frame(
fit = ppc_ft$fit.y[ , i],
fit.rep = ppc_ft$fit.y.rep[ , i],
color = "lightskyblue1"
)
ppc.df$color[ppc.df$fit.rep > ppc.df$fit] <- "lightsalmon"
plot(ppc.df$fit, ppc.df$fit.rep,
bg = ppc.df$color, pch = 21,
main = sp.names[i],
xlab = "True", ylab = "Replicate")
lines(ppc.df$fit, ppc.df$fit, col = "black")
}

# ── 3. Site-level discrepancy plot ───────────────────────────────────────────
# fit.y.group.quants and fit.y.rep.group.quants are arrays [5 x n.sites x n.species]
par(mfrow = c(2, ceiling(n.sp / 2)), mar = c(4, 4, 2, 1))
for (i in seq_len(n.sp)) {
diff.fit <- ppc_ft$fit.y.rep.group.quants[3, , i] -
ppc_ft$fit.y.group.quants[3, , i]
plot(diff.fit, pch = 19,
main = sp.names[i],
xlab = "Site ID", ylab = "Replicate - True Discrepancy")
abline(h = 0, col = "red", lty = 2) # reference line at 0
}

par(mfrow = c(1, 1)) # reset layout
Proability of Direction- Occupancy
pd <- function(x) {
max(mean(x > 0), mean(x < 0))
}
pd_table <- apply(
ms_cover_canopyQ$beta.samples,
2,
pd
)
head(pd_table)
## (Intercept)-Canis_latrans (Intercept)-Procyon_lotor
## 0.7796429 0.6215000
## (Intercept)-Dasypus_novemcinctus (Intercept)-Lynx_rufus
## 0.9583571 0.8635714
## (Intercept)-Didelphis_virginiana (Intercept)-Sylvilagus_floridanus
## 0.9881429 0.9174286
beta_means <- colMeans(ms_cover_canopyQ$beta.samples)
beta_samps <- ms_cover_canopyQ$beta.samples
pd_vals <- apply(beta_samps, 2, pd)
beta_means <- colMeans(beta_samps)
results_species <- data.frame(
parameter = colnames(beta_samps),
mean = beta_means,
pd = pd_vals,
pd_95 = pd_vals >= 0.95
)
head(results_species)
## parameter mean
## (Intercept)-Canis_latrans (Intercept)-Canis_latrans -0.4498371
## (Intercept)-Procyon_lotor (Intercept)-Procyon_lotor -0.1725355
## (Intercept)-Dasypus_novemcinctus (Intercept)-Dasypus_novemcinctus -0.9841908
## (Intercept)-Lynx_rufus (Intercept)-Lynx_rufus -0.7861329
## (Intercept)-Didelphis_virginiana (Intercept)-Didelphis_virginiana -1.3910655
## (Intercept)-Sylvilagus_floridanus (Intercept)-Sylvilagus_floridanus -0.8906736
## pd pd_95
## (Intercept)-Canis_latrans 0.7796429 FALSE
## (Intercept)-Procyon_lotor 0.6215000 FALSE
## (Intercept)-Dasypus_novemcinctus 0.9583571 TRUE
## (Intercept)-Lynx_rufus 0.8635714 FALSE
## (Intercept)-Didelphis_virginiana 0.9881429 TRUE
## (Intercept)-Sylvilagus_floridanus 0.9174286 FALSE
beta_comm <- ms_cover_canopyQ$beta.comm.samples
results_comm <- data.frame(
parameter = colnames(beta_comm),
mean = colMeans(beta_comm),
pd = apply(beta_comm, 2, pd)
)
results_comm$pd_95 <- results_comm$pd >= 0.95
Probability of Direction- Detection
pd <- function(x) {
max(mean(x > 0), mean(x < 0))
}
alpha_samps <- ms_cover_canopyQ$alpha.samples
pd_vals_alpha <- apply(alpha_samps, 2, pd)
alpha_means <- colMeans(alpha_samps)
results_species_det <- data.frame(
parameter = colnames(alpha_samps),
mean = alpha_means,
pd = pd_vals_alpha,
pd_95 = pd_vals_alpha >= 0.95
)
head(results_species_det)
## parameter mean
## (Intercept)-Canis_latrans (Intercept)-Canis_latrans -2.778739
## (Intercept)-Procyon_lotor (Intercept)-Procyon_lotor -2.311326
## (Intercept)-Dasypus_novemcinctus (Intercept)-Dasypus_novemcinctus -1.815284
## (Intercept)-Lynx_rufus (Intercept)-Lynx_rufus -3.568876
## (Intercept)-Didelphis_virginiana (Intercept)-Didelphis_virginiana -2.691935
## (Intercept)-Sylvilagus_floridanus (Intercept)-Sylvilagus_floridanus -3.140079
## pd pd_95
## (Intercept)-Canis_latrans 1 TRUE
## (Intercept)-Procyon_lotor 1 TRUE
## (Intercept)-Dasypus_novemcinctus 1 TRUE
## (Intercept)-Lynx_rufus 1 TRUE
## (Intercept)-Didelphis_virginiana 1 TRUE
## (Intercept)-Sylvilagus_floridanus 1 TRUE
alpha_comm <- ms_cover_canopyQ$alpha.comm.samples
results_comm_det <- data.frame(
parameter = colnames(alpha_comm),
mean = colMeans(alpha_comm),
pd = apply(alpha_comm, 2, pd)
)
results_comm_det$pd_95 <- results_comm_det$pd >= 0.95
results_comm_det
## parameter mean pd pd_95
## (Intercept) (Intercept) -2.78173071 1.0000000 TRUE
## shrub_cover shrub_cover 0.33937623 0.8746429 FALSE
## veg_height veg_height 0.08229406 0.6483571 FALSE
Plot Species Occupancy
Batched Parameter Estimates
summary(ms_cover_canopyQ) # Summary of parameter estimates
##
## Call:
## msPGOcc(occ.formula = occ.canopy.quad, det.formula = det.cover,
## data = data_list, priors = priors, n.samples = 10000, n.report = 1000,
## n.burn = 3000, n.thin = 2, n.chains = 4)
##
## Samples per Chain: 10000
## Burn-in: 3000
## Thinning Rate: 2
## Number of Chains: 4
## Total Posterior Samples: 14000
## Run Time (min): 1.4008
##
## ----------------------------------------
## Community Level
## ----------------------------------------
## Occurrence Means (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat ESS
## (Intercept) -0.8165 0.4541 -1.7032 -0.8225 0.0842 1.0045 2937
## Avg_Canopy_Cover 1.2355 0.4329 0.4462 1.2145 2.1593 1.0032 3673
## Avg_Cogongrass_Cover -0.1161 0.4588 -1.0178 -0.1159 0.8021 1.0032 3009
## I(Avg_Cogongrass_Cover^2) 0.7409 0.4278 -0.0262 0.7188 1.6536 1.0056 2271
##
## Occurrence Variances (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat ESS
## (Intercept) 0.7119 0.5558 0.1880 0.5590 2.1047 1.0015 6023
## Avg_Canopy_Cover 0.8128 0.6892 0.1996 0.6260 2.5643 1.0041 4168
## Avg_Cogongrass_Cover 0.5942 0.4601 0.1641 0.4675 1.7862 1.0014 6920
## I(Avg_Cogongrass_Cover^2) 0.6091 0.4866 0.1629 0.4690 1.8509 1.0019 3389
##
## Occurrence Random Effect Variances (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat ESS
## Auth 0.4816 0.5697 0.0437 0.2883 2.0336 1.0056 987
##
## Detection Means (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat ESS
## (Intercept) -2.7817 0.2915 -3.3469 -2.7861 -2.1939 1.0025 7153
## shrub_cover 0.3394 0.3067 -0.2851 0.3424 0.9458 1.0013 6537
## veg_height 0.0823 0.2356 -0.3954 0.0860 0.5392 1.0018 6162
##
## Detection Variances (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat ESS
## (Intercept) 0.6103 0.3605 0.2187 0.5223 1.5295 1.0013 5865
## shrub_cover 0.6493 0.3860 0.2240 0.5523 1.6370 1.0013 5974
## veg_height 0.3670 0.2053 0.1419 0.3162 0.8958 1.0007 10272
##
## ----------------------------------------
## Species Level
## ----------------------------------------
## Occurrence (logit scale):
## Mean SD 2.5% 50%
## (Intercept)-Canis_latrans -0.4498 0.6171 -1.6477 -0.4550
## (Intercept)-Procyon_lotor -0.1725 0.6048 -1.3612 -0.1785
## (Intercept)-Dasypus_novemcinctus -0.9842 0.5816 -2.1663 -0.9724
## (Intercept)-Lynx_rufus -0.7861 0.7430 -2.1795 -0.8057
## (Intercept)-Didelphis_virginiana -1.3911 0.6552 -2.7470 -1.3622
## (Intercept)-Sylvilagus_floridanus -0.8907 0.6523 -2.1796 -0.8869
## (Intercept)-Meleagris_gallopavo -0.3780 0.7367 -1.7190 -0.4119
## (Intercept)-Sciurus_carolinensis -1.7158 0.7242 -3.2374 -1.6892
## Avg_Canopy_Cover-Canis_latrans 0.1349 0.4695 -0.7950 0.1370
## Avg_Canopy_Cover-Procyon_lotor 1.0511 0.4981 0.1304 1.0290
## Avg_Canopy_Cover-Dasypus_novemcinctus 1.2702 0.5385 0.3258 1.2293
## Avg_Canopy_Cover-Lynx_rufus 0.8370 0.7083 -0.4532 0.7963
## Avg_Canopy_Cover-Didelphis_virginiana 1.6716 0.6810 0.5572 1.5942
## Avg_Canopy_Cover-Sylvilagus_floridanus 2.0965 0.8656 0.7198 1.9845
## Avg_Canopy_Cover-Meleagris_gallopavo 1.4646 0.7104 0.2183 1.4128
## Avg_Canopy_Cover-Sciurus_carolinensis 1.7258 0.7185 0.5733 1.6394
## Avg_Cogongrass_Cover-Canis_latrans 0.1129 0.6157 -1.0606 0.1016
## Avg_Cogongrass_Cover-Procyon_lotor -0.1691 0.5945 -1.3138 -0.1725
## Avg_Cogongrass_Cover-Dasypus_novemcinctus 0.2297 0.6099 -0.9305 0.2132
## Avg_Cogongrass_Cover-Lynx_rufus -0.2355 0.7016 -1.6456 -0.2317
## Avg_Cogongrass_Cover-Didelphis_virginiana 0.3630 0.6752 -0.9011 0.3452
## Avg_Cogongrass_Cover-Sylvilagus_floridanus -0.7268 0.7069 -2.2215 -0.6894
## Avg_Cogongrass_Cover-Meleagris_gallopavo -0.5038 0.7945 -2.1538 -0.4692
## Avg_Cogongrass_Cover-Sciurus_carolinensis -0.0265 0.6643 -1.3340 -0.0305
## I(Avg_Cogongrass_Cover^2)-Canis_latrans 1.3545 0.7850 0.1074 1.2533
## I(Avg_Cogongrass_Cover^2)-Procyon_lotor 1.0768 0.6831 0.0446 0.9794
## I(Avg_Cogongrass_Cover^2)-Dasypus_novemcinctus 0.5817 0.4797 -0.2711 0.5505
## I(Avg_Cogongrass_Cover^2)-Lynx_rufus 1.1834 0.6271 0.1429 1.1227
## I(Avg_Cogongrass_Cover^2)-Didelphis_virginiana 0.2532 0.5147 -0.6622 0.2135
## I(Avg_Cogongrass_Cover^2)-Sylvilagus_floridanus 0.5747 0.5512 -0.3951 0.5281
## I(Avg_Cogongrass_Cover^2)-Meleagris_gallopavo 0.2362 0.8255 -1.2129 0.1493
## I(Avg_Cogongrass_Cover^2)-Sciurus_carolinensis 0.8005 0.4828 -0.0777 0.7753
## 97.5% Rhat ESS
## (Intercept)-Canis_latrans 0.8100 1.0019 4007
## (Intercept)-Procyon_lotor 1.0510 1.0011 3716
## (Intercept)-Dasypus_novemcinctus 0.1305 1.0012 4832
## (Intercept)-Lynx_rufus 0.7503 1.0042 2504
## (Intercept)-Didelphis_virginiana -0.1891 1.0002 4588
## (Intercept)-Sylvilagus_floridanus 0.3788 1.0040 4558
## (Intercept)-Meleagris_gallopavo 1.1797 1.0063 3072
## (Intercept)-Sciurus_carolinensis -0.3953 1.0002 3996
## Avg_Canopy_Cover-Canis_latrans 1.0586 1.0016 7592
## Avg_Canopy_Cover-Procyon_lotor 2.0632 1.0003 8544
## Avg_Canopy_Cover-Dasypus_novemcinctus 2.4495 1.0010 5948
## Avg_Canopy_Cover-Lynx_rufus 2.3337 1.0010 3177
## Avg_Canopy_Cover-Didelphis_virginiana 3.2582 1.0065 3637
## Avg_Canopy_Cover-Sylvilagus_floridanus 4.0884 1.0034 2824
## Avg_Canopy_Cover-Meleagris_gallopavo 3.0253 1.0019 3625
## Avg_Canopy_Cover-Sciurus_carolinensis 3.4023 1.0033 3418
## Avg_Cogongrass_Cover-Canis_latrans 1.3580 1.0012 5058
## Avg_Cogongrass_Cover-Procyon_lotor 1.0267 1.0015 5265
## Avg_Cogongrass_Cover-Dasypus_novemcinctus 1.4797 1.0020 5809
## Avg_Cogongrass_Cover-Lynx_rufus 1.1354 1.0042 3806
## Avg_Cogongrass_Cover-Didelphis_virginiana 1.7814 1.0043 4781
## Avg_Cogongrass_Cover-Sylvilagus_floridanus 0.5628 1.0004 4193
## Avg_Cogongrass_Cover-Meleagris_gallopavo 1.0153 1.0074 2290
## Avg_Cogongrass_Cover-Sciurus_carolinensis 1.2816 1.0015 5153
## I(Avg_Cogongrass_Cover^2)-Canis_latrans 3.1481 1.0030 2309
## I(Avg_Cogongrass_Cover^2)-Procyon_lotor 2.7368 1.0017 2461
## I(Avg_Cogongrass_Cover^2)-Dasypus_novemcinctus 1.6336 1.0015 4325
## I(Avg_Cogongrass_Cover^2)-Lynx_rufus 2.6060 1.0028 3200
## I(Avg_Cogongrass_Cover^2)-Didelphis_virginiana 1.4324 1.0077 2871
## I(Avg_Cogongrass_Cover^2)-Sylvilagus_floridanus 1.8050 1.0009 2987
## I(Avg_Cogongrass_Cover^2)-Meleagris_gallopavo 2.1269 1.0241 990
## I(Avg_Cogongrass_Cover^2)-Sciurus_carolinensis 1.8505 1.0018 4752
##
## Detection (logit scale):
## Mean SD 2.5% 50% 97.5% Rhat
## (Intercept)-Canis_latrans -2.7787 0.1836 -3.1514 -2.7721 -2.4313 1.0006
## (Intercept)-Procyon_lotor -2.3113 0.1439 -2.6062 -2.3077 -2.0401 1.0017
## (Intercept)-Dasypus_novemcinctus -1.8153 0.1670 -2.1531 -1.8122 -1.4988 1.0000
## (Intercept)-Lynx_rufus -3.5689 0.3331 -4.2356 -3.5673 -2.9261 1.0054
## (Intercept)-Didelphis_virginiana -2.6919 0.2868 -3.2776 -2.6857 -2.1489 1.0058
## (Intercept)-Sylvilagus_floridanus -3.1401 0.2504 -3.6519 -3.1333 -2.6679 1.0046
## (Intercept)-Meleagris_gallopavo -3.8174 0.4225 -4.6462 -3.8110 -2.9985 1.0181
## (Intercept)-Sciurus_carolinensis -2.7536 0.3092 -3.3855 -2.7484 -2.1635 1.0029
## shrub_cover-Canis_latrans -0.2583 0.2194 -0.6952 -0.2572 0.1650 1.0028
## shrub_cover-Procyon_lotor 0.2474 0.1676 -0.0934 0.2507 0.5718 1.0006
## shrub_cover-Dasypus_novemcinctus 1.0049 0.3043 0.4006 1.0092 1.5878 1.0006
## shrub_cover-Lynx_rufus -0.2128 0.3604 -0.9300 -0.2115 0.4919 1.0016
## shrub_cover-Didelphis_virginiana 1.1217 0.3627 0.4415 1.1068 1.8797 1.0066
## shrub_cover-Sylvilagus_floridanus 0.4942 0.3939 -0.2851 0.5003 1.2514 1.0060
## shrub_cover-Meleagris_gallopavo -0.6132 0.3848 -1.3790 -0.6134 0.1426 1.0109
## shrub_cover-Sciurus_carolinensis 1.0321 0.3953 0.2754 1.0321 1.8321 1.0048
## veg_height-Canis_latrans -0.6424 0.1874 -1.0280 -0.6349 -0.2958 1.0009
## veg_height-Procyon_lotor 0.3580 0.1247 0.1122 0.3575 0.6014 1.0010
## veg_height-Dasypus_novemcinctus 0.2780 0.1406 0.0078 0.2770 0.5573 1.0000
## veg_height-Lynx_rufus 0.0908 0.2550 -0.4305 0.0969 0.5730 1.0023
## veg_height-Didelphis_virginiana 0.5043 0.2614 0.0036 0.5014 1.0319 1.0024
## veg_height-Sylvilagus_floridanus 0.1517 0.2581 -0.3642 0.1539 0.6550 1.0034
## veg_height-Meleagris_gallopavo -0.1925 0.4194 -1.0168 -0.1902 0.6222 1.0226
## veg_height-Sciurus_carolinensis 0.1298 0.2293 -0.3105 0.1249 0.5795 1.0010
## ESS
## (Intercept)-Canis_latrans 3199
## (Intercept)-Procyon_lotor 5387
## (Intercept)-Dasypus_novemcinctus 5599
## (Intercept)-Lynx_rufus 1463
## (Intercept)-Didelphis_virginiana 3221
## (Intercept)-Sylvilagus_floridanus 3291
## (Intercept)-Meleagris_gallopavo 978
## (Intercept)-Sciurus_carolinensis 2965
## shrub_cover-Canis_latrans 3980
## shrub_cover-Procyon_lotor 5921
## shrub_cover-Dasypus_novemcinctus 4947
## shrub_cover-Lynx_rufus 2138
## shrub_cover-Didelphis_virginiana 2953
## shrub_cover-Sylvilagus_floridanus 2654
## shrub_cover-Meleagris_gallopavo 1408
## shrub_cover-Sciurus_carolinensis 2971
## veg_height-Canis_latrans 3759
## veg_height-Procyon_lotor 7115
## veg_height-Dasypus_novemcinctus 8216
## veg_height-Lynx_rufus 2996
## veg_height-Didelphis_virginiana 3926
## veg_height-Sylvilagus_floridanus 3516
## veg_height-Meleagris_gallopavo 1524
## veg_height-Sciurus_carolinensis 4471
names(ms_cover_canopyQ)
## [1] "rhat" "beta.comm.samples" "alpha.comm.samples"
## [4] "tau.sq.beta.samples" "tau.sq.alpha.samples" "beta.samples"
## [7] "alpha.samples" "z.samples" "psi.samples"
## [10] "like.samples" "sigma.sq.psi.samples" "beta.star.samples"
## [13] "re.level.names" "ESS" "X"
## [16] "X.p" "X.p.re" "X.re"
## [19] "y" "call" "n.samples"
## [22] "x.names" "sp.names" "x.p.names"
## [25] "n.post" "n.thin" "n.burn"
## [28] "n.chains" "pRE" "psiRE"
## [31] "run.time"
str(ms_cover_canopyQ$beta.samples)
## 'mcmc' num [1:14000, 1:32] -1.6429 -0.3721 -0.0261 -0.5903 -0.7689 ...
## - attr(*, "mcpar")= num [1:3] 1 14000 1
## - attr(*, "dimnames")=List of 2
## ..$ : NULL
## ..$ : chr [1:32] "(Intercept)-Canis_latrans" "(Intercept)-Procyon_lotor" "(Intercept)-Dasypus_novemcinctus" "(Intercept)-Lynx_rufus" ...
mean(ms_cover_canopyQ$beta.samples[, 5] > 0)
## [1] 0.01185714
MCMCplot(ms_cover_canopyQ$beta.samples, ref_ovl = TRUE, ci = c(50, 95)) # Occupancy

MCMCplot(ms_cover_canopyQ$alpha.samples, ref_ovl = TRUE, ci = c(50, 95)) # Detection

## Occupancy
# Total number of parameters
n_params <- ncol(ms_cover_canopyQ$beta.samples)
# Number of parameters to plot at a time
chunk_size <- 10
# Split and plot
for (i in seq(1, n_params, by = chunk_size)) {
end <- min(i + chunk_size - 1, n_params)
param_names <- colnames(ms_cover_canopyQ$beta.samples)[i:end]
# Set filename
file_name <- paste0("MCMCplot_Occupancy_Params_", i, "_to_", end, ".png")
# Save plot to PNG
png(filename = file_name, width = 1200, height = 800, res = 150)
MCMCplot(ms_cover_canopyQ$beta.samples[, param_names],
ref_ovl = TRUE,
ci = c(50, 95),
main = paste0("Occupancy Parameters: ", i, " to ", end))
dev.off()
}
## Detection
# Number of parameters
n_params <- ncol(ms_cover_canopyQ$alpha.samples)
# Number of parameters to plot at a time
chunk_size <- 10
# Split and plot
for (i in seq(1, n_params, by = chunk_size)) {
end <- min(i + chunk_size - 1, n_params)
param_names <- colnames(ms_cover_canopyQ$alpha.samples)[i:end]
# Set filename
file_name <- paste0("MCMCplot_Detection_Params_", i, "_to_", end, ".png")
# Save plot to PNG
png(filename = file_name, width = 1200, height = 800, res = 150)
MCMCplot(ms_cover_canopyQ$alpha.samples[, param_names],
ref_ovl = TRUE,
ci = c(50, 95),
main = paste0("Detection Parameters: ", i, " to ", end))
dev.off()
}
Facet Plots
plot_mcmc_faceted_groups <- function(samples_matrix,
chunk_size = 8,
title_text = "Parameters",
pd_cutoff = 0.95) {
param_names <- colnames(samples_matrix)
df <- as.data.frame(samples_matrix)
df$Iteration <- 1:nrow(df)
df_long <- df %>%
pivot_longer(-Iteration,
names_to = "Parameter",
values_to = "Value") %>%
mutate(
ParamIndex = match(Parameter, param_names),
Group = ceiling(ParamIndex / chunk_size)
)
# Posterior summaries + probability of direction
summary_df <- df_long %>%
group_by(Parameter, Group) %>%
summarise(
mean = mean(Value),
l95 = quantile(Value, 0.025),
u95 = quantile(Value, 0.975),
l50 = quantile(Value, 0.25),
u50 = quantile(Value, 0.75),
pd = max(mean(Value > 0), mean(Value < 0)),
.groups = "drop"
) %>%
mutate(
Credible = pd >= pd_cutoff
)
summary_df <- summary_df %>%
group_by(Group) %>%
mutate(Parameter = factor(Parameter,
levels = rev(unique(Parameter)))) %>%
ungroup()
ggplot(summary_df,
aes(x = mean, y = Parameter)) +
geom_vline(xintercept = 0, linetype = "dashed") +
# 95% CI
geom_errorbarh(aes(xmin = l95,
xmax = u95,
size = Credible),
height = 0) +
# 50% CI
geom_errorbarh(aes(xmin = l50,
xmax = u50,
size = Credible),
height = 0) +
# Posterior mean
geom_point(aes(fill = Credible),
shape = 21,
size = 3,
stroke = 1) +
facet_wrap(~Group, scales = "free_y", ncol = 2) +
scale_size_manual(values = c("TRUE" = 1.2,
"FALSE" = 0.5),
guide = "none") +
scale_fill_manual(values = c("TRUE" = "black",
"FALSE" = "white"),
guide = "none") +
labs(x = "Parameter Estimate",
y = "",
title = title_text) +
theme_bw() +
theme(
strip.background = element_blank(),
strip.text = element_blank(),
axis.text.y = element_text(size = 10)
)
}
plot_mcmc_faceted_groups(
ms_cover_canopyQ$beta.samples,
chunk_size = 8,
title_text = "",
pd_cutoff = 0.95
)
## Warning: `geom_errobarh()` was deprecated in ggplot2 4.0.0.
## ℹ Please use the `orientation` argument of `geom_errorbar()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the ggplot2 package.
## Please report the issue at <https://github.com/tidyverse/ggplot2/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `height` was translated to `width`.
## `height` was translated to `width`.

ggsave("mcmc_faceted_groups_occupancy.png",
width = 10, height = 8, dpi = 300)
## `height` was translated to `width`.
## `height` was translated to `width`.
plot_mcmc_faceted_groups(
ms_cover_canopyQ$alpha.samples,
chunk_size = 8,
title_text = "",
pd_cutoff = 0.95
)
## `height` was translated to `width`.
## `height` was translated to `width`.

ggsave("mcmc_faceted_groups_detection.png",
width = 10, height = 8, dpi = 300)
## `height` was translated to `width`.
## `height` was translated to `width`.
Plot Species level relationship with cogongrass cover
# Collecting scaled atrributes
cogon.mean <- attr(covariates_matrix, "scaled:center")["Avg_Cogongrass_Cover"]
cogon.sd <- attr(covariates_matrix, "scaled:scale")["Avg_Cogongrass_Cover"]
cogon.mean <- mean(CameraLoc$Avg_Cogongrass_Cover)
cogon.sd <- sd(CameraLoc$Avg_Cogongrass_Cover)
# Unscaled prediction values
cogon.pred.vals <- seq(0, 100, length.out = 100)
# Scale prediction values
cogon.pred.scale <- (cogon.pred.vals - cogon.mean) / cogon.sd
cogon.pred.scale2 <- cogon.pred.scale^2
canopy.mean <- attr(covariates_matrix, "scaled:center")["Avg_Canopy_Cover"]
canopy.sd <- attr(covariates_matrix, "scaled:scale")["Avg_Canopy_Cover"]
pred.df <- as.matrix(data.frame(
intercept = 1,
Avg_Cogongrass_Cover = cogon.pred.scale,
I.Avg_Cogongrass_Cover.2. = cogon.pred.scale2,
Avg_Canopy_Cover = 0
))
out.pred <- predict(
ms_cover_canopyQ,
X.0 = pred.df,
ignore.RE = TRUE
)
# Extract the species-specific betas for Coyote
coyote_index <- which(ms_cover_canopyQ$sp.names == "Canis_latrans")
# Get column indices for the parameters we need
intercept_col <- paste0("(Intercept)-", "Canis_latrans")
cogon_col <- paste0("Avg_Cogongrass_Cover-", "Canis_latrans")
cogon2_col <- paste0("I(Avg_Cogongrass_Cover^2)-", "Canis_latrans")
canopy_col <- paste0("Avg_Canopy_Cover-", "Canis_latrans")
# Extract posterior samples for each coefficient
beta_intercept <- ms_cover_canopyQ$beta.samples[, intercept_col]
beta_cogon <- ms_cover_canopyQ$beta.samples[, cogon_col]
beta_cogon2 <- ms_cover_canopyQ$beta.samples[, cogon2_col]
beta_canopy <- ms_cover_canopyQ$beta.samples[, canopy_col]
# Create prediction gradient (0 to 100%)
cogon.pred.vals <- seq(0, 100, length.out = 1000)
# Scale using same transformation
cogon.pred.scale <- (cogon.pred.vals - cogon.mean) / cogon.sd
cogon.pred.scale2 <- cogon.pred.scale^2
n_samples <- nrow(ms_cover_canopyQ$beta.samples)
n_pred <- length(cogon.pred.vals)
logit_psi <- matrix(NA, nrow = n_samples, ncol = n_pred)
for(i in 1:n_samples) {
logit_psi[i, ] <- beta_intercept[i] +
beta_cogon[i] * cogon.pred.scale +
beta_cogon2[i] * cogon.pred.scale2 +
beta_canopy[i] * 0 # Setting to mean (0 on scaled)
}
# Convert to probability scale
psi_samples <- plogis(logit_psi)
# Calculate quantiles across MCMC samples for each prediction point
psi.coyote.quants <- apply(psi_samples, 2, quantile,
probs = c(0.025, 0.5, 0.975))
psi.coyote.dat <- data.frame(
Avg_Cogongrass_Cover = cogon.pred.vals,
psi.low = psi.coyote.quants[1, ],
psi.med = psi.coyote.quants[2, ],
psi.high = psi.coyote.quants[3, ]
)
ggplot(psi.coyote.dat, aes(x = Avg_Cogongrass_Cover, y = psi.med)) +
geom_ribbon(aes(ymin = psi.low, ymax = psi.high), fill = "grey70", alpha = 0.5) +
geom_line(size = 1.2) +
theme_bw() +
labs(x = "Average Cogongrass Cover (%)",
y = "Occupancy Probability (Canis latrans)") +
scale_y_continuous(limits = c(0, 1)) +
scale_x_continuous(limits = c(0, 100))

summary(psi.coyote.dat)
## Avg_Cogongrass_Cover psi.low psi.med psi.high
## Min. : 0 Min. :0.1597 Min. :0.3878 Min. :0.6769
## 1st Qu.: 25 1st Qu.:0.2400 1st Qu.:0.5267 1st Qu.:0.8324
## Median : 50 Median :0.5635 Median :0.9855 Median :1.0000
## Mean : 50 Mean :0.5581 Mean :0.8087 Mean :0.9222
## 3rd Qu.: 75 3rd Qu.:0.8687 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :100 Max. :0.9733 Max. :1.0000 Max. :1.0000
Species Lookup
SpeciesLookup <- tibble::tribble(
~Name, ~Scientific_Name, ~Common_Name,
"Canis_latrans", "Canis latrans", "Coyote",
"Dasypus_novemcinctus", "Dasypus novemcinctus", "Nine-banded Armadillo",
"Didelphis_virginiana", "Didelphis virginiana", "Virginia Opossum",
"Lynx_rufus", "Lynx rufus", "Bobcat",
"Meleagris_gallopavo", "Meleagris gallopavo", "Wild Turkey",
"Procyon_lotor", "Procyon lotor", "Raccoon",
"Sciurus_carolinensis", "Sciurus carolinensis", "Eastern Gray Squirrel",
"Sylvilagus_floridanus", "Sylvilagus floridanus", "Eastern Cottontail Rabbit",
)
Varying cogongrass and canopy cover
# Prediction gradient for cogongrass
cogon.pred.vals <- seq(0, 100, length.out = 1000)
cogon.pred.scale <- (cogon.pred.vals - cogon.mean) / cogon.sd
cogon.pred.scale2 <- cogon.pred.scale^2
# Define canopy cover scenarios with factor levels
canopy_scenarios <- data.frame(
scenario = factor(c("Low", "Average", "High"),
levels = c("Low", "Average", "High")),
canopy_scaled = c(-1, 0, 1),
color = c("#D55E00", "#009E73", "#0072B2")
)
n_samples <- nrow(ms_cover_canopyQ$beta.samples)
n_pred <- length(cogon.pred.vals)
# Lists for results
species_predictions <- list()
species_plots <- list()
# Loop through all species
for(sp in ms_cover_canopyQ$sp.names) {
# Get column names for this species
intercept_col <- paste0("(Intercept)-", sp)
cogon_col <- paste0("Avg_Cogongrass_Cover-", sp)
cogon2_col <- paste0("I(Avg_Cogongrass_Cover^2)-", sp)
canopy_col <- paste0("Avg_Canopy_Cover-", sp)
# Extract posterior samples
beta_intercept <- ms_cover_canopyQ$beta.samples[, intercept_col]
beta_cogon <- ms_cover_canopyQ$beta.samples[, cogon_col]
beta_cogon2 <- ms_cover_canopyQ$beta.samples[, cogon2_col]
beta_canopy <- ms_cover_canopyQ$beta.samples[, canopy_col]
# Store predictions for each canopy scenario
scenario_predictions <- list()
for(j in 1:nrow(canopy_scenarios)) {
canopy_val <- canopy_scenarios$canopy_scaled[j]
# Calculate linear predictor
logit_psi <- matrix(NA, nrow = n_samples, ncol = n_pred)
for(i in 1:n_samples) {
logit_psi[i, ] <- beta_intercept[i] +
beta_cogon[i] * cogon.pred.scale +
beta_cogon2[i] * cogon.pred.scale2 +
beta_canopy[i] * canopy_val
}
# Convert to probability
psi_samples <- plogis(logit_psi)
# Calculate quantiles
psi.quants <- apply(psi_samples, 2, quantile, probs = c(0.025, 0.5, 0.975))
# Store results
scenario_predictions[[j]] <- data.frame(
species = sp,
scenario = canopy_scenarios$scenario[j],
Avg_Cogongrass_Cover = cogon.pred.vals,
psi.low = psi.quants[1, ],
psi.med = psi.quants[2, ],
psi.high = psi.quants[3, ]
)
}
# Combine all scenarios for this species
species_predictions[[sp]] <- do.call(rbind, scenario_predictions)
all_predictions <- all_predictions %>%
left_join(SpeciesLookup, by = c("species" = "Name"))
# Create plot for this species
species_plots[[sp]] <- ggplot(species_predictions[[sp]],
aes(x = Avg_Cogongrass_Cover, y = psi.med,
color = scenario, fill = scenario)) +
geom_ribbon(aes(ymin = psi.low, ymax = psi.high), alpha = 0.2, color = NA) +
geom_line(size = 1.2) +
scale_color_manual(values = canopy_scenarios$color) +
scale_fill_manual(values = canopy_scenarios$color) +
theme_bw() +
labs(x = "Average Cogongrass Cover (%)",
y = "Occupancy Probability",
title = gsub("_", " ", sp),
color = "Canopy Cover",
fill = "Canopy Cover") +
scale_y_continuous(limits = c(0, 1)) +
scale_x_continuous(limits = c(0, 100)) +
theme(legend.position = "bottom")
}
# View individual plots
species_plots[["Canis_latrans"]]

species_plots[["Procyon_lotor"]]

# Combine all plots into a grid
all_species_plot <- wrap_plots(species_plots, ncol = 2, guides = "collect") &
theme(legend.position = "bottom")
all_species_plot

ggsave("all_species_cogongrass_canopy.png",
all_species_plot,
width = 14, height = 16, dpi = 300)
# Combine all predictions into one dataframe
all_predictions <- do.call(rbind, species_predictions)
all_predictions <- all_predictions %>%
left_join(
SpeciesLookup %>% dplyr::select(Name, Common_Name),
by = c("species" = "Name")
)
all_predictions$scenario <- factor(all_predictions$scenario,
levels = c("Low", "Average", "High"))
# Create faceted plot
faceted_plot <- ggplot(all_predictions,
aes(x = Avg_Cogongrass_Cover, y = psi.med,
color = scenario, fill = scenario)) +
geom_ribbon(aes(ymin = psi.low, ymax = psi.high), alpha = 0.2, color = NA) +
geom_line(size = 1.2) +
facet_wrap(~ Common_Name, ncol = 2) +
scale_color_manual(values = canopy_scenarios$color) +
scale_fill_manual(values = canopy_scenarios$color) +
theme_bw() +
labs(x = "Average Cogongrass Cover (%)",
y = "Occupancy Probability",
color = "Canopy Cover",
fill = "Canopy Cover") +
scale_y_continuous(limits = c(0, 1)) +
scale_x_continuous(limits = c(0, 100)) +
theme(strip.text = element_text(face = "bold"),
legend.position = "bottom")
faceted_plot

ggsave("all_species_faceted_canopy.png",
faceted_plot,
width = 12, height = 14, dpi = 600)
Community Occupancy Plot- Cogongrass and Canopy Cover
# Prediction gradient for cogongrass
cogon.pred.vals <- seq(0, 100, length.out = 1000)
cogon.pred.scale <- (cogon.pred.vals - cogon.mean) / cogon.sd
cogon.pred.scale2 <- cogon.pred.scale^2
# Define canopy cover scenarios
canopy_scenarios <- data.frame(
scenario = c("Low", "Average", "High"),
canopy_scaled = c(-1, 0, 1),
color = c("#D55E00", "#009E73", "#0072B2")
)
n_pred <- length(cogon.pred.vals)
# Get community-level parameters
comm_intercept <- ms_cover_canopyQ$beta.comm.samples[, "(Intercept)"]
comm_cogon <- ms_cover_canopyQ$beta.comm.samples[, "Avg_Cogongrass_Cover"]
comm_cogon2 <- ms_cover_canopyQ$beta.comm.samples[, "I(Avg_Cogongrass_Cover^2)"]
comm_canopy <- ms_cover_canopyQ$beta.comm.samples[, "Avg_Canopy_Cover"]
# Store predictions for each canopy scenario
community_predictions <- list()
for(j in 1:nrow(canopy_scenarios)) {
canopy_val <- canopy_scenarios$canopy_scaled[j]
# Calculate linear predictor
logit_psi_comm <- matrix(NA,
nrow = nrow(ms_cover_canopyQ$beta.comm.samples),
ncol = n_pred)
for(i in 1:nrow(ms_cover_canopyQ$beta.comm.samples)) {
logit_psi_comm[i, ] <- comm_intercept[i] +
comm_cogon[i] * cogon.pred.scale +
comm_cogon2[i] * cogon.pred.scale2 +
comm_canopy[i] * canopy_val
}
# Convert to probability
psi_comm_samples <- plogis(logit_psi_comm)
# Calculate quantiles
psi.comm.quants <- apply(psi_comm_samples, 2, quantile,
probs = c(0.025, 0.5, 0.975))
# Store results
community_predictions[[j]] <- data.frame(
scenario = canopy_scenarios$scenario[j],
Avg_Cogongrass_Cover = cogon.pred.vals,
psi.low = psi.comm.quants[1, ],
psi.med = psi.comm.quants[2, ],
psi.high = psi.comm.quants[3, ]
)
}
# Combine all scenarios
psi.comm.dat <- do.call(rbind, community_predictions)
# Reorder the scenario factor levels
psi.comm.dat$scenario <- factor(psi.comm.dat$scenario,
levels = c("Low", "Average", "High"))
community_plot <- ggplot(psi.comm.dat,
aes(x = Avg_Cogongrass_Cover, y = psi.med,
color = scenario, fill = scenario)) +
geom_ribbon(aes(ymin = psi.low, ymax = psi.high), alpha = 0.2, color = NA) +
geom_line(size = 1.5) +
scale_color_manual(values = canopy_scenarios$color) +
scale_fill_manual(values = canopy_scenarios$color) +
theme_bw(base_size = 14) +
labs(x = "Average Cogongrass Cover (%)",
y = "Community Occupancy Probability",
color = "Canopy Cover",
fill = "Canopy Cover") +
scale_y_continuous(limits = c(0, 1)) +
scale_x_continuous(limits = c(0, 100)) +
theme(legend.position = "bottom",
plot.title = element_text(hjust = 0.5))
community_plot

ggsave("community_cogongrass_canopy.png",
community_plot,
width = 10, height = 7, dpi = 300)
write.csv(psi.comm.dat, "community_predictions_by_canopy.csv", row.names = FALSE)