Calculates all stats for song sound pressure level (i.e. amplitude) covariates
2 Next steps
Make analysis for vegetation density
Change randome slope analysis with absolute differences in before and after treatments (both in interactions and coordinations) -See if eliminating interaction videos allows a cleaner analysis (leave only calibrated songs)
# functions and parametersknitr::opts_knit$set(root.dir =normalizePath(".."))knitr::opts_chunk$set(dpi =50, fig.width =12, warning =FALSE, message =FALSE)# ggplot2 theme theme_set(theme_classic(base_size = 20))cut_path <-"./data/raw/cuts"treatments <-c("Calibration", "Regular_singing", "Coordination","After_chase", "Before_playback", "After_playback", "Before_interaction","After_interaction", "Before_noise", "After_noise")# iterations for MCMCglmm modelsitrns <-1e+05# functions from https://rdrr.io/rforge/rptR/src/R/rpt.mcmcLMM.Rrpt.mcmcLMM <-function(y, groups, CI =0.95, prior =NULL, verbose =FALSE, ...) {# initial checksif (length(y) !=length(groups))stop("y and group are of unequal length")# preparation groups <-factor(groups)if (is.null(prior)) prior <-list(R =list(V =1, n =0.1), G =list(G1 =list(V =1,n =0.1)))# point estimation according to model 8 and equation 9 mod <-MCMCglmm(y ~1, random =~groups, family ="gaussian",data =data.frame(y = y, groups = groups), prior = prior,verbose = verbose, ...) var.a <- mod$VCV[, "groups"] var.e <- mod$VCV[, "units"] postR <- var.a/(var.a + var.e)# point estimate R <-posterior.mode(postR)# credibility interval estimation from paterior distribution CI.R <- coda::HPDinterval(postR, CI)[1, ] se <-sd(postR)# 'significance test' P <-NA res =list(call =match.call(), datatype ="Gaussian", method ="LMM.MCMC",CI = CI, R = R, CI.R = CI.R, se = se, P = P)# class(res) <- 'rpt'return(res)}## print Gelman-Rubin convergence statistics, plots traces and## autocorrelationsmcmc_diagnostics <-function(rep_mods_list) {for (w in1:length(rep_mods_list)) { mod_name <-names(rep_mods_list)[w]if (mod_name =="1") mod_name <-"Null"print(paste("model:", mod_name)) Y <-lapply(rep_mods_list[[w]], "[[", "Sol")## add global plots and gelman test gelman_diagnostic gel_diag <-as.data.frame(gelman.diag(mcmc.list(Y))$psrf)# add estimate as column gel_diag$estimate <-rownames(gel_diag)# reorder columns gel_diag <- gel_diag[, c(3, 1, 2)]par(mfrow =c(1, 1))# plot tablegrid.newpage()grid.draw(tableGrob(gel_diag, rows =NULL, theme =ttheme_default(base_size =25)))par(mfrow =c(1, 4))traceplot(Y, col =adjustcolor(c("yellow", "blue", "red"),alpha.f =0.6))autocorr.plot(x = Y[[1]], auto.layout =FALSE, lwd =4, col ="red") }}
Code
amp <-read.csv("./output/calibrated_amplitude_all_songs.csv")amp$Treatment[amp$Treatment =="Regular_sining"] <-"Regular_singing"sound_meter_calib <- readxl::read_excel("./data/raw/calibrate_sound_meter_based_on_new_sound_meter.xlsx")# calibrate SPL based on new sound metermean_spl_diff <-mean(sound_meter_calib$nuevo[1:19] - sound_meter_calib$viejo[1:19])amp$cal.spl <- amp$cal.spl + mean_spl_diff# extrapolate SPL at 1m using the inverse square law functionamp$cal.spl.1m <- amp$cal.spl +20*log10(40/100)
3 Repeatability
We estimated repeatability using linear mixed models:
Code
# keep only morning recs, only regular singing and only from# 2021amp_morn <- amp[amp$period =="morning"& amp$year ==2021& amp$Treatment =="Regular_singing", ]# keep only males recorded at least twicecount_sf <-table(amp_morn$ID[!duplicated(amp_morn$org.sound.file)])amp_morn_rep <- amp_morn[amp_morn$ID %in%names(count_sf)[count_sf >1], ]max_quantile <-c(0, 1, seq(10, 90, by =10))/100outliers <-c(0, 1, 2, 5, 10, 20)/100rep_grid <-expand.grid(max_quantile = max_quantile, outliers = outliers,only.low.outliers =c(TRUE, FALSE))repts <-pblapply(1:nrow(rep_grid), cl =6, function(x) { quant_subet <-lapply(unique(amp_morn_rep$org.sound.file), function(y) { X <- amp_morn_rep[amp_morn_rep$org.sound.file == y, ]# remove outliers outlier_quant <-quantile(X$cal.spl.1m, c(rep_grid$outliers[x],1- rep_grid$outliers[x]))if (!rep_grid$only.low.outliers[x]) X <- X[X$cal.spl.1m >= outlier_quant[1] & X$cal.spl.1m <= outlier_quant[2], ] else X <- X[X$cal.spl.1m >= outlier_quant[1], ]# quantlie for each max quantile quant <-quantile(X$cal.spl.1m, probs =1- rep_grid$max_quantile[x])# subset X <- X[X$cal.spl.1m >= quant, ] }) quant_subet <-do.call(rbind, quant_subet)# frequentist out <-rpt(cal.spl.1m ~ (1| ID), grname ="ID", data = quant_subet,datatype ="Gaussian", npermut =0, nboot =100)# bayesian out <- rpt.mcmcLMM(y = quant_subet$cal.spl.1m,# groups = quant_subet$ID, nitt = itrns) out_df <-data.frame(rep_grid$max_quantile[x], rep_grid$outliers[x], rep_grid$only.low.outliers[x], out$R, out$CI_emp[1], out$CI_emp[2])return(out_df)})repts_df <-do.call(rbind, repts)names(repts_df) <-c("max_quantile", "outliers", "only.low.outliers","repeatability", "lowCI", "hiCI")# write.csv(repts_df, './output/repeatability_optimization.csv',# row.names = FALSE)
Different subsets of data for upper quantiles (x axis)
Total number of observations: 16153 (16110 excluding unidentified individuals)
Median and range of observations per individual: 178 (5-2242)
Code
id.count
ID
n
0
43
9
5
36
9
54
7
108
8
124
24
146
12
176
19
178
8
179
8
384
209
390
151
391
175
395
123
397
1507
398
449
400
919
402
85
403
474
413
881
415
977
416
705
417
1706
419
2242
422
607
423
744
424
1427
432
414
433
53
435
45
437
442
438
981
444
181
446
77
448
332
449
104
5 Hypothesis testing analysis
We use bayesian generalized linear models with individual ID and song type as random factors (random intercepts) except indicated. An intercept-only (null) model is also included in the analyses. A loop is used to run these models. Each model is replicated three times with starting values sampled from a Z-distribution (“start” argument in MCMCglmm()). Diagnostic plots for MCMC model performance are shown at the end of this report.
6 SPL vs day period (morning / afternoon)
Only for individuals recorded in both periods
Period of the day as predictor: \[SPL \sim + period + (1 | individual) + (1 | song\ type)\]
Null model with no predictor (intercept only): \[SPL \sim + 1 + (1 | individual) + (1 | song\ type)\]
Code
iter <-1000chains <-4priors <-c(prior(normal(0, 2), class ="b"))# subset dataperiod_data <- rm_outlier_amp[rm_outlier_amp$Treatment =="Regular_singing"& rm_outlier_amp$year ==2021& rm_outlier_amp$ID %in%unique(rm_outlier_amp$ID[rm_outlier_amp$period =="afternoon"]), ]# model SPL by body sizeperiod_formulas <-c("cal.spl.1m ~ 1 + (1|ID) + (1|songtype)", "cal.spl.1m ~ period + (1|ID) + (1|songtype)")period_mods <-pblapply(period_formulas, function(x) { mod <-brm(as.formula(x), data = period_data, iter = iter, chains = chains,cores = chains)return(mod)})names(period_mods) <-gsub("cal.spl.1m ~ ", "", period_formulas)saveRDS(list(period_formulas = period_formulas, period_mods = period_mods),"./output/brms_period_models.RDS")
# simplify namescolnames(best_mod_period$Sol) <-c("Intercept", "Morning vs afternoon")# stack posteriorsY <-stack(as.data.frame(best_mod_period$Sol[, -1]))Y$ind <-"Morning vs afternoon"# plot posteriorsggplot(Y, aes(y = values, x = ind)) +geom_hline(yintercept =0, col ="red",lty =2) +geom_violin(color ="gray40", fill =viridis(n =1,alpha =0.25, begin =0.4)) +labs(y ="Effect size posterior distribution",x ="Predictor") +theme_classic(base_size =24) +theme(axis.text.x =element_text(angle =45,vjust =0.9, hjust =1))
Morning songs have significantly higher SPL than afternoon songs within individuals
7 SPL vs condition and habitat structure
Only for individuals recorded in the morning (afternoon recordings were excluded)
Condition parameters: body size (PC1 from a PCA on total culmen, flattened wing length, body mass and central rectrix), lifting power and vegetation density
Body size as predictor: \[SPL \sim + body\ size + (1 | individual) + (1 | song\ type)\]
Lifting power as predictor: \[SPL \sim + lifting\ power + (1 | individual) + (1 | song\ type)\]
Vegetation density as predictor: \[SPL \sim + vegetation\ density + (1 | individual) + (1 | song\ type)\]
Body size and vegetation density as predictors: \[SPL \sim + body\ size + vegetation\ density + (1 | individual) + (1 | song\ type)\]
Lifting power and vegetation density as predictors: \[SPL \sim + lifting\ power + vegetation\ density + (1 | individual) + (1 | song\ type)\]
Lifting power and body size as predictors: \[SPL \sim + lifting\ power + body\ size + (1 | individual) + (1 | song\ type)\]
Lifting power, body size and vegetation density as predictors: \[SPL \sim + lifting\ power + body\ size + vegetation\ density + (1 | individual) + (1 | song\ type)\]
Interaction of lifting power and body size as predictor: \[SPL \sim + lifting\ power * body\ size + (1 | individual) + (1 | song\ type)\]
Interaction of lifting power, body size and vegetation density as predictor: \[SPL \sim + lifting\ power * body\ size * vegetation\ density + (1 | individual) + (1 | song\ type)\]
Null model with no predictor (intercept only): \[SPL \sim + 1 + (1 | individual) + (1 | song\ type)\]
7.0.1 Correlation between body size parameters included in the PCA
Code
caps <-read_excel("./data/raw/LBH captures data.xlsx")# same variables as in paper on spatial memory and body sizesize_vars <- caps[caps$`Bird ID`%in% amp$ID, c("Bird ID", "Total culmen","Flattened wing length", "Weight", "Central rectriz")]# replace 419 central rectriz with meansize_vars$`Central rectriz`[size_vars$`Bird ID`==419] <-mean(size_vars$`Central rectriz`,na.rm =TRUE)cor(size_vars[, c("Total culmen", "Flattened wing length", "Weight","Central rectriz")], use ="pairwise.complete.obs")
Total culmen Flattened wing length Weight
Total culmen 1.000000000 -0.13969106 0.14426785
Flattened wing length -0.139691059 1.00000000 0.08646515
Weight 0.144267849 0.08646515 1.00000000
Central rectriz 0.007536892 0.34107800 0.10235729
Central rectriz
Total culmen 0.007536892
Flattened wing length 0.341077996
Weight 0.102357292
Central rectriz 1.000000000
The random slope model is significantly better than the random intercept model (p = < 0.0001), the interaction with body size model (p = < 0.0001), the interaction with lifting power model (p = < 0.0001) and the null model (p = < 0.0001)
The playback stimuli elicits a significant change in SPL but the direction of the response varies between individuals
Direction of the response doesn’t seem to be affected by individual condition (neither body size nor lifting power)
9 SPL vs coordination
Random slope models, using data from 2019 and 2021
Only for individuals in which coordination was observed when recorded
SPL of songs sang during coordinated singing were compared to regular singing songs from the same recording session
No song type random intercept was included
Coordination as predictor and random slope: \[SPL \sim + coordination + (coordination | individual)\]
Coordination as predictor and random intercept only: \[SPL \sim + coordination + (1 | individual)\]
Null model with no predictor (random intercept only): \[SPL \sim + 1 + (1 | individual)\]
Aggressive interaction but not SPL affects the spectrographic consistency of songs
11.2 Song gap duration
-Gaps: silent time intervals between songs - Mean SPL has been mean-centered to facilitate interpretation
interaction as predictor and gap duration as response: \[gap\ duration \sim + interaction + (1 | individual) + (1 | song\ type)\]
interaction and SPL as predictors and gap duration as response: \[gap\ duration \sim + interaction + SPL + (1 | individual) + (1 | song\ type)\]
interaction between “interaction” and SPL as predictor and gap duration as response: \[gap\ duration \sim + interaction * SPL + (1 | individual) + (1 | song\ type)\]
Null model with no predictor (intercept only): \[gap\ duration \sim + 1 + (1 | individual) + (1 | song\ type)\]
# stack posteriorsY <-stack(as.data.frame(best_mod_gap$Sol[, -1]))# plot posteriorsggplot(Y, aes(y = values, x = ind)) +geom_hline(yintercept =0, col ="red",lty =2) +geom_violin(color ="gray40", fill =viridis(n =1,alpha =0.25, begin =0.4)) +labs(y ="Effect size posterior distribution",x ="Predictor") +theme_classic(base_size =24) +theme(axis.text.x =element_text(angle =45,vjust =0.9, hjust =1))# zoom inY <-stack(as.data.frame(best_mod_gap$Sol[, -c(1, 2)]))ggplot(Y, aes(y = values, x = ind)) +geom_hline(yintercept =0, col ="red",lty =2) +geom_violin(color ="gray40", fill =viridis(n =1,alpha =0.25, begin =0.4)) +labs(y ="Effect size posterior distribution",x ="Predictor") +theme_classic(base_size =24) +theme(axis.text.x =element_text(angle =45,vjust =0.9, hjust =1))
Code
# extract mcmcsmcmcs <- best_mod_gap$Sol# make empty listmcmc_l <-list()## after interaction 0 splmcmc_l[[length(mcmc_l) +1]] <-data.frame(spl = mcmcs[, "(Intercept)"],treatment ="After", type ="0 spl")## before interaction 0 splmcmc_l[[length(mcmc_l) +1]] <-data.frame(spl = mcmcs[, "(Intercept)"] + mcmcs[, "treatmentBefore"], treatment ="Before", type ="0 spl")## before interaction 1 splmcmc_l[[length(mcmc_l) +1]] <-data.frame(spl = mcmcs[, "(Intercept)"] + mcmcs[, "treatmentBefore"] + mcmcs[, "mean.spl"], treatment ="Before",type ="1 spl")## after interaction 1 splmcmc_l[[length(mcmc_l) +1]] <-data.frame(spl = mcmcs[, "(Intercept)"] + mcmcs[, "mean.spl"], treatment ="After", type ="1 spl")mcmc_df <-do.call(rbind, mcmc_l)colnames(mcmc_df)[1] <-"spl"mcmc_df$treatment_type <-paste(mcmc_df$treatment, mcmc_df$type, sep ="-")# p-valuescombs <-combn(x =unique(mcmc_df$treatment_type), m =2)pvals_l <-lapply(1:ncol(combs), function(x) { mcmc1 <- mcmc_df$spl[mcmc_df$treatment_type == combs[1, x]] mcmc2 <- mcmc_df$spl[mcmc_df$treatment_type == combs[2, x]] p <-if (mean(mcmc2) >mean(mcmc1))sum(mcmc1 > mcmc2)/length(mcmc1) elsesum(mcmc2 > mcmc1)/length(mcmc1) out <-data.frame(mcmc1 = combs[1, x], mcmc2 = combs[2, x], p = p)return(out)})pvals <-do.call(rbind, pvals_l)mcmc_df$treatment <-factor(mcmc_df$treatment, levels =c("Before","After"))mcmc_df$type <-factor(mcmc_df$type, levels =c("Perch interaction","Chase"))# plot posteriorsggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + ggplot(mcmc_df,ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + aes(yggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + =ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + spl,ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + xggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + =ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + treatment))ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + +ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + #ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + geom_hline(yinterceptggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + =ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + 0,ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + colggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + =ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + 'red',ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + ltyggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + =ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + 2)ggplot(mcmc_df, aes(y = spl, x = treatment)) +# geom_hline(yintercept = 0, col = 'red', lty = 2) + +geom_violin(color ="gray40", fill =viridis(n =1, alpha =0.25,begin =0.4)) +labs(y ="SPL posterior distribution", x ="Predictor") +theme_classic(base_size =24) +facet_wrap(~type) +theme(axis.text.x =element_text(angle =45,vjust =0.9, hjust =1))kable(pvals)
No detectable effect on gap duration
11.3 Diagnostic plots for MCMCglmm models
Include Gelman/Rubin’s convergence diagnostic, MCMC chain trace (all tree replicates in a single plot: yellow, blue and red colors) and autocorrelation plots: