library(dplyr)
library(trajr)
library(sf)
library(ggmap)
library(tidyverse)
library(rstanarm)
library(ggspatial)
library(gridExtra)
library(cowplot)
library(readr)
library(purrr)
library(lubridate)
library(zoo)
library(mapview)
library(emmeans)
library(geosphere)
library(slider)
library(scales)
library(ctmm)
mapviewOptions(fgb=F)
habitats <- st_read('/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/habitats_test4.geojson')
## Reading layer `habitats_test4' from data source
## `/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/habitats_test4.geojson'
## using driver `GeoJSON'
## Simple feature collection with 6 features and 1 field
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 103.0052 ymin: 5.812754 xmax: 103.0104 ymax: 5.81758
## Geodetic CRS: WGS 84
mapview(habitats)
contour_level <- 0.5
# Lets plot out what we have produced
cont_50_ars <- st_read('/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/50_ars_contours.geojson')
## Reading layer `50_contours' from data source
## `/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/50_ars_contours.geojson'
## using driver `GeoJSON'
## Simple feature collection with 36 features and 2 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 103.0068 ymin: 5.812596 xmax: 103.0099 ymax: 5.814514
## Geodetic CRS: WGS 84
mapview(cont_50_ars, zcol = "id", legend = F)
Overlap the 50% KUD
overlaps_ars <-
st_intersection(cont_50_ars, habitats) %>%
mutate(area = as.numeric(st_area(.)))
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
overlaps_ars %>%
mapview(zcol = "habitat")
overlaps_ars %>%
st_drop_geometry() %>%
group_by(id, habitat) %>%
summarise(area = sum(area)) %>%
ggplot(aes(x = area, y = id, fill = habitat)) +
geom_col(position = "fill", col = "white", lwd = 0.1) +
labs(x = "Proportion of ARS activity space", fill = "Reef\nHabitat") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(expand = c(0,0)) +
theme_bw()
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by id and habitat.
## ℹ Output is grouped by id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(id, habitat))` for per-operation grouping
## (`?dplyr::dplyr_by`) instead.
# Input wandering KUD files
cont_50_wandering <- st_read('/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/50__wandering_contours.geojson')
## Reading layer `50_contours' from data source
## `/Users/larryzheng/Desktop/Fulbright/Research/Drone Tracking/50__wandering_contours.geojson'
## using driver `GeoJSON'
## Simple feature collection with 36 features and 2 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 103.0055 ymin: 5.811821 xmax: 103.0102 ymax: 5.815312
## Geodetic CRS: WGS 84
mapview(cont_50_wandering, zcol = "id", legend = F)
Overlap the 50% KUD
overlaps_wandering <-
st_intersection(cont_50_wandering, habitats) %>%
mutate(area = as.numeric(st_area(.)))
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
overlaps_wandering %>%
mapview(zcol = "habitat")
overlaps_wandering %>%
st_drop_geometry() %>%
group_by(id, habitat) %>%
summarise(area = sum(area)) %>%
ggplot(aes(x = area, y = id, fill = habitat)) +
geom_col(position = "fill", col = "white", lwd = 0.1) +
labs(x = "Proportion of Wandering activity space", fill = "Reef\nHabitat") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(expand = c(0,0)) +
theme_bw()
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by id and habitat.
## ℹ Output is grouped by id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(id, habitat))` for per-operation grouping
## (`?dplyr::dplyr_by`) instead.
overlaps_ars_test <- overlaps_ars %>%
group_by(id) %>%
mutate(total_area = sum(area),
prop = area / total_area) %>%
ungroup()
overlaps_wandering_test <- overlaps_wandering %>%
group_by(id) %>%
mutate(total_area = sum(area),
prop = area / total_area) %>%
ungroup()
overlaps_ars_test$movement_type <- "ARS"
overlaps_wandering_test$movement_type <- "wandering"
overlaps_all <- bind_rows(overlaps_ars_test, overlaps_wandering_test)
hist(overlaps_all$prop)
Fill missing habitats’ proportions with 0
overlaps_all_test <- overlaps_all %>%
complete(id, habitat, fill = list(area = 0, prop = 0))
Proportions aren’t normal, so we must logit transform
epsilon <- 0.0001
overlaps_all_test <- overlaps_all_test %>%
mutate(prop_adj = pmin(pmax(prop, epsilon), 1 - epsilon),
prop_logit = log(prop_adj / (1 - prop_adj)))
habitats <- c("lagoon", "reef_crest", "fore_reef", "sand_flat")
df_clean <- overlaps_all_test %>%
mutate(
habitat = tolower(habitat),
habitat = gsub(" ", "_", habitat),
prop = ifelse(is.na(prop), 0, prop)
) %>%
# keep only real habitat rows
filter(habitat %in% c("lagoon", "reef_crest", "fore_reef", "sand_flat")) %>%
group_by(id, movement_type, habitat, total_area) %>%
summarise(prop = sum(prop, na.rm = TRUE), .groups = "drop") %>%
pivot_wider(
names_from = habitat,
values_from = prop,
values_fill = 0
)
df_final <- df_clean %>%
mutate(
total = lagoon + reef_crest + fore_reef + sand_flat,
across(c(lagoon, reef_crest, fore_reef, sand_flat),
~ .x / total)
)
Summary dataframe of means
df_final <- df_final %>%
filter(!is.na(movement_type))
df_final %>%
group_by(movement_type) %>%
summarise(across(all_of(habitats), mean))
## # A tibble: 2 × 5
## movement_type lagoon reef_crest fore_reef sand_flat
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 ARS 0.887 0.0193 0.0159 0.0777
## 2 wandering 0.575 0.0418 0.139 0.245
Summary dataframe of both means and sd
df_summary <- df_final %>%
group_by(movement_type) %>%
summarise(
across(
all_of(habitats),
list(
mean = ~ mean(.x, na.rm = TRUE),
sd = ~ sd(.x, na.rm = TRUE)
),
.names = "{.col}_{.fn}"
)
)
df_summary
## # A tibble: 2 × 9
## movement_type lagoon_mean lagoon_sd reef_crest_mean reef_crest_sd
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 ARS 0.887 0.232 0.0193 0.0495
## 2 wandering 0.575 0.420 0.0418 0.0527
## # ℹ 4 more variables: fore_reef_mean <dbl>, fore_reef_sd <dbl>,
## # sand_flat_mean <dbl>, sand_flat_sd <dbl>
library(vegan)
## Loading required package: permute
adonis2(df_final[, habitats] ~ movement_type,
data = df_final,
method = "bray",
permutations = 9999)
## Permutation test for adonis under reduced model
## Permutation: free
## Number of permutations: 9999
##
## adonis2(formula = df_final[, habitats] ~ movement_type, data = df_final, permutations = 9999, method = "bray")
## Df SumOfSqs R2 F Pr(>F)
## Model 1 1.5910 0.15156 12.504 8e-04 ***
## Residual 70 8.9069 0.84844
## Total 71 10.4980 1.00000
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Bray-Curtis dissimilarity
d <- vegdist(df_final[, habitats], method = "bray")
# PERMANOVA model
adonis_model <- adonis2(d ~ movement_type, data = df_final)
# check variable contributions via envfit
fit <- envfit(prcomp(df_final[, habitats]), df_final$movement_type)
fit
##
## ***FACTORS:
##
## Centroids:
## PC1 PC2
## df_final.movement_typeARS -0.1856 -0.0291
## df_final.movement_typewandering 0.1856 0.0291
##
## Goodness of fit:
## r2 Pr(>r)
## df_final.movement_type 0.1519 0.001 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Permutation: free
## Number of permutations: 999
SIMPER Test (finds that lagoon (reef flat) is biggest contributor to difference)
simper(df_final[, habitats], df_final$movement_type)
## cumulative contributions of most influential species:
##
## $ARS_wandering
## lagoon sand_flat
## 0.4813165 0.7825026
adonis2(df_final[, habitats] ~ movement_type,
data = df_final,
method = "bray",
permutations = 9999,
strata = df_final$id)
## Permutation test for adonis under reduced model
## Blocks: strata
## Permutation: free
## Number of permutations: 9999
##
## adonis2(formula = df_final[, habitats] ~ movement_type, data = df_final, permutations = 9999, method = "bray", strata = df_final$id)
## Df SumOfSqs R2 F Pr(>F)
## Model 1 1.5910 0.15156 12.504 0.2017
## Residual 70 8.9069 0.84844
## Total 71 10.4980 1.00000
library(glmmTMB)
## Warning in check_dep_version(dep_pkg = "TMB"): package version mismatch:
## glmmTMB was built with TMB package version 1.9.19
## Current TMB package version is 1.9.21
## Please re-install glmmTMB from source or restore original 'TMB' package (see '?reinstalling' for more information)
epsilon <- 0.001
overlaps_all_test2 <- overlaps_all_test %>%
mutate(prop_adj = pmin(pmax(prop, epsilon), 1 - epsilon))
glmm_model <- glmmTMB(prop_adj ~ movement_type * habitat + (1 | id),
family = beta_family(), data = overlaps_all_test2)
emmeans(glmm_model, pairwise ~ movement_type | habitat)
## $emmeans
## habitat = fore reef:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS -1.186 0.462 Inf -2.091 -0.2805
## wandering -1.280 0.214 Inf -1.699 -0.8609
##
## habitat = lagoon:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS 2.048 0.203 Inf 1.651 2.4456
## wandering 1.229 0.207 Inf 0.823 1.6360
##
## habitat = reef crest:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS -1.424 0.399 Inf -2.207 -0.6416
## wandering -1.599 0.212 Inf -2.015 -1.1827
##
## habitat = sand flat:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS -0.759 0.380 Inf -1.504 -0.0143
## wandering -0.316 0.249 Inf -0.804 0.1721
##
## Results are given on the logit (not the response) scale.
## Confidence level used: 0.95
##
## $contrasts
## habitat = fore reef:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.0945 0.501 Inf 0.188 0.8505
##
## habitat = lagoon:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.8186 0.271 Inf 3.022 0.0025
##
## habitat = reef crest:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.1743 0.441 Inf 0.396 0.6924
##
## habitat = sand flat:
## contrast estimate SE df z.ratio p.value
## ARS - wandering -0.4427 0.453 Inf -0.978 0.3282
##
## Results are given on the log odds ratio (not the response) scale.
emmeans(glmm_model, ~ movement_type * habitat, type = "response")
## movement_type habitat response SE df asymp.LCL asymp.UCL
## ARS fore reef 0.234 0.0828 Inf 0.1100 0.430
## wandering fore reef 0.218 0.0364 Inf 0.1545 0.297
## ARS lagoon 0.886 0.0205 Inf 0.8390 0.920
## wandering lagoon 0.774 0.0363 Inf 0.6948 0.837
## ARS reef crest 0.194 0.0624 Inf 0.0991 0.345
## wandering reef crest 0.168 0.0297 Inf 0.1177 0.235
## ARS sand flat 0.319 0.0825 Inf 0.1819 0.496
## wandering sand flat 0.422 0.0608 Inf 0.3091 0.543
##
## Confidence level used: 0.95
## Intervals are back-transformed from the logit scale
Inputted values from above manually
# Create data
df <- data.frame(
Habitat = rep(c("Fore reef", "Lagoon", "Reef crest", "Sand flat"), each = 2),
Movement = rep(c("ARS", "Wandering"), times = 4),
Mean = c(0.234, 0.218,
0.886, 0.774,
0.194, 0.168,
0.319, 0.422),
CI_low = c(0.110, 0.155,
0.839, 0.695,
0.099, 0.118,
0.182, 0.309),
CI_high = c(0.430, 0.297,
0.920, 0.837,
0.345, 0.235,
0.496, 0.543),
p_value = rep(c(0.8505, 0.0025, 0.6924, 0.3282), each = 2)
)
# Flag lagoon for highlighting (only lagoon has statistically significant p value)
df <- df %>%
mutate(Highlight = ifelse(Habitat == "Lagoon", "Lagoon", "Other"))
ggplot(df, aes(x = Habitat, y = Mean, color = Movement)) +
# Points
geom_point(aes(alpha = Highlight),
size = 3.5,
stroke = 1,
position = position_dodge(width = 0.5)) +
# Error bars
geom_errorbar(aes(ymin = CI_low, ymax = CI_high, alpha = Highlight),
width = 0.2,
linewidth = 1,
position = position_dodge(width = 0.5)) +
# Custom colors
scale_color_manual(
name = "Movement Type",
values = c("ARS" = "#d73027", "Wandering" = "#4575b4")
) +
# Highlight lagoon via alpha
scale_alpha_manual(values = c("Lagoon" = 1, "Other" = 0.5), guide = "none") +
labs(y = "Mean Proportion", x = "Habitat") +
theme_minimal() +
theme(
axis.line.x = element_line(color = "black", linewidth = 0.6),
axis.line.y = element_line(color = "black", linewidth = 0.6),
panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major.y = element_line(color = "gray80", linewidth = 0.3),
axis.title.x = element_text(size = 17),
axis.title.y = element_text(size = 17),
axis.text.x = element_text(size = 16),
axis.text.y = element_text(size = 16),
legend.title = element_text(size = 16),
legend.text = element_text(size = 16),
legend.position = c(0.85, 0.85),
legend.background = element_rect(fill = "white", color = NA)
)
library(betareg)
glm_model <- betareg(prop_adj ~ movement_type * habitat,
data = overlaps_all_test2)
emmeans(glm_model, pairwise ~ movement_type | habitat)
## $emmeans
## habitat = fore reef:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS 0.234 0.0828 Inf 0.0718 0.396
## wandering 0.218 0.0364 Inf 0.1462 0.289
##
## habitat = lagoon:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS 0.886 0.0205 Inf 0.8455 0.926
## wandering 0.774 0.0363 Inf 0.7025 0.845
##
## habitat = reef crest:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS 0.194 0.0624 Inf 0.0716 0.316
## wandering 0.168 0.0297 Inf 0.1100 0.226
##
## habitat = sand flat:
## movement_type emmean SE df asymp.LCL asymp.UCL
## ARS 0.319 0.0825 Inf 0.1572 0.481
## wandering 0.422 0.0608 Inf 0.3025 0.541
##
## Confidence level used: 0.95
##
## $contrasts
## habitat = fore reef:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.0165 0.0891 Inf 0.185 0.8530
##
## habitat = lagoon:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.1120 0.0394 Inf 2.847 0.0044
##
## habitat = reef crest:
## contrast estimate SE df z.ratio p.value
## ARS - wandering 0.0258 0.0675 Inf 0.383 0.7021
##
## habitat = sand flat:
## contrast estimate SE df z.ratio p.value
## ARS - wandering -0.1027 0.1020 Inf -1.006 0.3144