#Merge alkaloid data with alkaloid metadata
alk_data <- alk_data %>% left_join(metadata %>% select(Sample, ID), by = "Sample")
#Normalize using alkaloid standard
alk_data_normalized <- alk_data %>%
mutate(across(12:38, ~ ifelse(.x < 150000, 0, .x)))%>% # convert to 0 all values less than 150000
mutate(across(12:38, ~ .x / `476`))%>%
mutate(across(13:38, ~ .x * 10)) #convert to nanograms
#Count the number of features per sample
counts <- alk_data_normalized %>%
mutate(n_features = rowSums(across(13:38, ~ . > 0), na.rm = TRUE))
#Dataset including only samples with alkaloids
samples_alkaloids <- counts %>% filter(n_features >0)
#Quantities - abundance and richness per sample
frog_summary <- samples_alkaloids %>%
mutate(
total_ng = rowSums(select(., 13:38), na.rm = TRUE),
alkaloid_richness = rowSums(select(., 13:38) > 0, na.rm = TRUE))
#Count number of features per species and season
alk_cols <- c("552", "619", "621", "655", "689", "709", "788", "811", "812", "822",
"838", "845", "886", "895", "899", "907", "921", "939", "944", "982",
"998", "999", "1003", "1011", "1012", "1015")
# Number of alkaloids/features per species and season
population_richness <- frog_summary %>%
group_by(ATTRIBUTE_season_species) %>%
summarise(
n_alkaloids = sum(colSums(across(all_of(alk_cols)) > 0) > 0),
.groups = "drop")
# Sample size per species and season
sample_size_species_season <-frog_summary%>%
group_by(ATTRIBUTE_season_species)%>%
summarise(n())
# Number of alkaloids per species
population_richness_sp <- frog_summary %>%
group_by(ATTRIBUTE_species) %>%
summarise(
n_unique_alkaloids = sum(colSums(across(all_of(alk_cols)) > 0) > 0),
.groups = "drop")
# Mean chemical load per species and season
species_season_summary <- frog_summary %>%
group_by(ATTRIBUTE_species, ATTRIBUTE_season) %>%
summarise(
mean_total_ng = mean(total_ng, na.rm = TRUE),
sd_total_ng = sd(total_ng, na.rm = TRUE),
mean_richness = mean(alkaloid_richness, na.rm = TRUE),
sd_richness = sd(alkaloid_richness, na.rm = TRUE),
n_frogs = n(),
.groups = "drop")
#===========================================================
# Seasonal comparison between alkaloid families
season_abund_feature <- samples_alkaloids %>% #samples_alkaloids includes only samples with alkaloids
select(-c(3,5:12,40)) %>%
pivot_longer(
cols = where(is.numeric),
names_to = "alkaloid",
values_to = "abundance") %>%
mutate(alkaloid = as.numeric(alkaloid)) %>%
left_join(
select_alk %>% select(alkaloid, Mabel_family),
by = "alkaloid") %>%
mutate(
season = ATTRIBUTE_season,
species = ATTRIBUTE_species)
season_abund_feature
#===========================================================
# Prepare data for analysis
# 1) Collapse alkaloid families within each frog
# Some frogs may contain multiple alkaloids belonging to the same structural family.
# Here, we sum them WITHIN each frog so that each frog has one abundance value per alkaloid family.
profile_summary_ng_collapsed <- season_abund_feature %>%
group_by(Sample,
ATTRIBUTE_season,
ATTRIBUTE_species,
Mabel_family) %>%
summarise(
abundance_fam = sum(abundance, na.rm = TRUE),
.groups = "drop")
# 2) Create a wide frog x family matrix
# Each row = one frog
# Each column = one alkaloid family
# Values = abundance (ng)
frog_wide <- profile_summary_ng_collapsed %>%
pivot_wider(names_from = Mabel_family,
values_from = abundance_fam,
values_fill = 0)
# 3) Extract metadata
metadata <- frog_wide %>%
select(Sample, ATTRIBUTE_species, ATTRIBUTE_season) %>%
mutate(ATTRIBUTE_season = factor(ATTRIBUTE_season,levels = c("dry", "wet")),
ATTRIBUTE_species = factor(ATTRIBUTE_species),
sp_season = paste(ATTRIBUTE_species, ATTRIBUTE_season, sep = "_"))
# 4) Abundance matrix
# Remove metadata columns and keep only alkaloid abundances
alk_matrix <- frog_wide %>% select(-Sample, -ATTRIBUTE_species, -ATTRIBUTE_season) %>%
as.data.frame()
rownames(alk_matrix) <- frog_wide$Sample
# 5) Remove frogs with no alkaloids
# Frogs with all zeros cannot be used in Bray-Curtis analyses
keep <- rowSums(alk_matrix) > 0
alk_matrix <- alk_matrix[keep, ]
metadata <- metadata[keep, ]
#===========================================================
# Statistical test
#=======================================================================================
# Table S14
# Species-specific PERMANOVA testing for seasonal differences in alkaloid composition
# using Bray–Curtis dissimilarities of alkaloid family abundances (ng)
#=======================================================================================
# Permanova (within species), all my analysis are done by species
species_list <- unique(metadata$ATTRIBUTE_species)
permanova_results <- lapply(species_list, function(sp) {
samples_sp <- metadata %>%
filter(ATTRIBUTE_species == sp) %>%
pull(Sample)
adonis2(
alk_matrix[samples_sp, ] ~ ATTRIBUTE_season,
data = metadata %>% filter(Sample %in% samples_sp),
method = "bray",
permutations = 999)
})
names(permanova_results) <- species_list
# Table results
table_results_s14 <- bind_rows(
lapply(names(permanova_results), function(sp) {
res <- permanova_results[[sp]]
tibble(
Species = paste0("*", sp, "*"), # markdown italics
Df = res["Model", "Df"],
F = res["Model", "F"],
R2 = res["Model", "R2"],
P_value = res["Model", "Pr(>F)"]
)}))
table_results_s14 <- table_results_s14 %>%
mutate(Species = factor(Species,
levels = c(
"*Ameerega trivittata*",
"*Ameerega macero*",
"*Ameerega shihuemoy*"))) %>% arrange(Species)
table_results_s14 %>%
gt() %>%
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()) %>%
fmt_markdown(columns = Species) %>%
fmt_number(
columns = c(F, R2, P_value),
decimals = 3) %>%
cols_label(
Species = "Species",
Df = "Df",
F = "F",
R2 = html("R<sup>2</sup>"),
P_value = "P-value")
#=======================================================================================
# Table S15
# Seasonal comparisons of alkaloid family abundance within each species. Differences between
# dry and wet seasons were assessed using pairwise Wilcoxon rank-sum tests. Reported values
# include sample sizes (n), test statistic (W), raw and Benjamini–Hochberg-adjusted p-values
#=======================================================================================
# Univariate analysis (families)
# Wilcoxon tests
family_tests <- profile_summary_ng_collapsed %>%
group_by(ATTRIBUTE_species, Mabel_family) %>%
filter(sum(abundance_fam) > 0) %>% # remove all-zero groups
wilcox_test(
abundance_fam ~ ATTRIBUTE_season) %>%
adjust_pvalue(method = "BH") %>%
add_significance("p.adj")
# Table results
table_results_s15 <- family_tests %>%
transmute(
Species = paste0("*", ATTRIBUTE_species, "*"),
`Alkaloid family` = Mabel_family,
Dry = n1,
Wet = n2,
W = statistic,
`P-value` = round(p, 3),
`P-adjusted` = round(p.adj, 3))
table_results_s15 <- table_results_s15 %>%
mutate(Species = factor(Species,
levels = c(
"*Ameerega trivittata*",
"*Ameerega macero*",
"*Ameerega shihuemoy*"))) %>% arrange(Species)
table_results_s15 %>%
gt() %>%
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()) %>%
fmt_markdown(columns = Species) #this is for italics
#=======================================================================================
# Figure 5B
# Mean abundance (ng) of alkaloid families within each species–season combination (sample sizes indicated as samples with detectable alkaloids/total sampled).
#=======================================================================================
# Plot settings
species_levels <- c("Ameerega trivittata",
"Ameerega macero",
"Ameerega shihuemoy")
family_levels <- c("HTX", "DHQ", "N-methyl-DHQ",
"5,8-I", "5,6,8-I", "3,5-P",
"Pyr", "Tricyclic", "Unclass", "Unknown")
species_colors <- c("Ameerega trivittata" = "#CBC3E3",
"Ameerega macero" = "#b5d1a1",
"Ameerega shihuemoy" = "#b5d1a1")
season_colors <- c(dry = "#E7B800", wet = "#0072B2")
season_alpha <- c(dry = 0.4, wet = 0.9)
# Family palette
family_palette <- wes_palette("Zissou1", length(family_levels), type = "continuous")
#=================================================================
# Family-level summary statistics (mean abundance and 95% confidence intervals)
profile_summary_ng <- profile_summary_ng_collapsed %>%
group_by(ATTRIBUTE_season, ATTRIBUTE_species, Mabel_family) %>%
summarise(n = n_distinct(Sample),
mean_ng = mean(abundance_fam, na.rm = TRUE),
sd_ng = sd(abundance_fam,na.rm = TRUE),
se_ng = sd_ng / sqrt(n),
ci_ng = qt(0.975, df = n - 1) * sd_ng / sqrt(n),
.groups = "drop")
#=================================================================
# Factor ordering
profile_summary_ng <- profile_summary_ng %>%
mutate(Mabel_family = factor(Mabel_family, levels = family_levels),
ATTRIBUTE_species = factor(ATTRIBUTE_species, levels = species_levels))
profile_summary_ng_collapsed <- profile_summary_ng_collapsed %>%
mutate(ATTRIBUTE_species = factor(ATTRIBUTE_species, levels = species_levels))
# Individual observations shown as jittered points
raw_points <- profile_summary_ng_collapsed %>% filter(abundance_fam > 0)
#=================================================================
# Plot mean abundance by alkaloid family
plot_families <- ggplot(
profile_summary_ng,
aes(x = Mabel_family, y = mean_ng, fill = Mabel_family)) +
# mean abundance bars
geom_col(aes(alpha = ATTRIBUTE_season), position = position_dodge(width = 0.9),
color = "black") +
# family colors
scale_fill_manual(values = family_palette, guide = "none") +
ggnewscale::new_scale_fill() +
# season aesthetics
scale_fill_manual(values = season_colors, name = "Season")+
scale_alpha_manual(values = season_alpha, name = "Season") +
# 95% confidence intervals
geom_errorbar(aes(group = ATTRIBUTE_season,
ymin = pmax(0, mean_ng - ci_ng),
ymax = mean_ng + ci_ng),
position = position_dodge(width = 0.9),
width = 0.25, color = "black") +
# Individual sample abundances
geom_jitter(data = raw_points,
aes(x = Mabel_family, y = abundance_fam, fill = ATTRIBUTE_season),
position = position_jitterdodge(jitter.width = 0.15,
dodge.width = 0.9),
shape = 21, color = "black", stroke = 0.4, alpha = 0.5, size = 2.2,
inherit.aes = FALSE) +
# Species panels
ggh4x::facet_wrap2(~ ATTRIBUTE_species, ncol = 1,scales = "fixed",
strip = ggh4x::strip_themed(
background_x = elem_list_rect(fill = species_colors,
alpha = 0.5),
text_x = elem_list_text(face = "italic", size = 14))) +
# Axes
scale_y_sqrt(limits = c(0, 70), labels = comma) +
labs(
x = "Alkaloid Family",
y = "Mean abundance (ng)") +
theme_bw() +
theme(
axis.title = element_text(size = 16),
axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),
axis.text.y = element_text(size = 12),
strip.text = element_text(face = "italic"),
legend.title = element_text(size = 14),
legend.text = element_text(size = 12),
panel.grid.minor = element_blank())
#=======================================================================================
# Figure S4
# Heatmap showing the prevalence (%) of alkaloid families
#=======================================================================================
# Calculate prevalence among ALL sampled frogs
# Species-season labels (positive samples / total samples)
axis_labels <- c(
"dry_Ameerega trivittata" = "<span style='color:#E6B800;'><i>Ameerega trivittata</i><br>n=12/13</span>",
"wet_Ameerega trivittata" = "<span style='color:#0072B2;'><i>Ameerega trivittata</i><br>n=22/26</span>",
"dry_Ameerega macero" = "<span style='color:#E6B800;'><i>Ameerega macero</i><br>n=3/19</span>",
"wet_Ameerega macero" = "<span style='color:#0072B2;'><i>Ameerega macero</i><br>n=8/26</span>",
"dry_Ameerega shihuemoy" = "<span style='color:#E6B800;'><i>Ameerega shihuemoy</i><br>n=4/6</span>",
"wet_Ameerega shihuemoy" = "<span style='color:#0072B2;'><i>Ameerega shihuemoy</i><br>n=7/16</span>"
)
# Order of species-season combinations and alkaloid families
species_levels <- rev(c(
"dry_Ameerega trivittata",
"wet_Ameerega trivittata",
"dry_Ameerega macero",
"wet_Ameerega macero",
"dry_Ameerega shihuemoy",
"wet_Ameerega shihuemoy"
))
family_levels <- c(
"HTX", "DHQ", "N-methyl-DHQ", "5,8-I", "5,6,8-I",
"3,5-P", "Pyr", "Tricyclic", "Unclass", "Unknown"
)
# Calculate prevalence (% frogs containing each alkaloid family)
prevalence_summary <- counts %>%
select(-c(3, 5:12, 40)) %>%
pivot_longer(
cols = where(is.numeric),
names_to = "alkaloid",
values_to = "abundance") %>%
mutate(alkaloid = as.numeric(alkaloid)) %>%
left_join(
select(select_alk, alkaloid, Mabel_family),
by = "alkaloid") %>%
group_by(
Sample,
ATTRIBUTE_species,
ATTRIBUTE_season,
Mabel_family) %>%
summarise(
abundance_fam = sum(abundance),
.groups = "drop") %>%
filter(ATTRIBUTE_species != "Allobates femoralis") %>%
group_by(
Sample,
ATTRIBUTE_species,
ATTRIBUTE_season,
Mabel_family) %>%
summarise(
present = any(abundance_fam > 0),
.groups = "drop") %>%
group_by(
ATTRIBUTE_species,
ATTRIBUTE_season,
Mabel_family) %>%
summarise(
prevalence = mean(present) * 100,
n_positive = sum(present),
n_total = n(),
.groups = "drop") %>%
mutate(
species_season = factor(
paste(ATTRIBUTE_season, ATTRIBUTE_species, sep = "_"),
levels = species_levels),
Mabel_family = factor(
Mabel_family,
levels = family_levels))
# Heatmap
plot_prevalence <- ggplot(prevalence_summary, aes(x = Mabel_family,
y = species_season,
fill = prevalence)) +
geom_tile(color = "white") +
geom_text(aes(label = round(prevalence, 1)), size = 3) +
scale_y_discrete(labels = axis_labels) +
scale_fill_gradient(
low = "white",
high = "#0072B2",
limits = c(0, 100),
name = "% frogs\nwith alkaloid") +
labs(
x = "Alkaloid Family",
y = NULL) +
theme_bw() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
axis.text.y = ggtext::element_markdown(),
panel.grid = element_blank(),
axis.title = element_text(size = 14),
legend.title = element_text(size = 12),
legend.text = element_text(size = 10))