Last updated: 2021-05-24
Checks: 7 0
Knit directory: SD_sexual_selection/
This reproducible R Markdown analysis was created with workflowr (version 1.6.2). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.
Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.
Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.
The command set.seed(20200925)
was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.
Great job! Recording the operating system, R version, and package versions is critical for reproducibility.
Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.
Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.
Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.
The results in this page were generated with repository version 3565002. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.
Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish
or wflow_git_commit
). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:
Ignored files:
Ignored: .DS_Store
Ignored: .Rproj.user/
Ignored: analysis/.DS_Store
Untracked files:
Untracked: Figure_1.pdf
Untracked: Figure_4.pdf
Untracked: SD_ESM_references.bib
Untracked: SD_crossing_scheme.png
Untracked: Supplementary_material.Rmd
Untracked: Supplementary_material.pdf
Untracked: data/Drive_test.csv
Untracked: data/SD_mating_data.csv
Untracked: data/two_choice_test_data.csv
Untracked: fits/
Untracked: mating_success_plot.pdf
Untracked: my_output.rds
Untracked: plotting_mating_duration_P1_P2.R
Untracked: simulation_results/
Unstaged changes:
Modified: README.md
Modified: _workflowr.yml
Modified: analysis/_site.yml
Deleted: analysis/about.Rmd
Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.
These are the previous versions of the repository in which changes were made to the R Markdown (analysis/Empirical_analysis.Rmd
) and HTML (docs/Empirical_analysis.html
) files. If you’ve configured a remote Git repository (see ?wflow_git_remote
), click on the hyperlinks in the table below to view the files as they were in that past version.
File | Version | Author | Date | Message |
---|---|---|---|---|
Rmd | 3565002 | tkeaney | 2021-05-24 | Publish the initial files for myproject |
library(png) # to load images
library(grid) # to plot images
library(tidyverse) # for data wrangling and plotting
library(reshape2) # for the melt function
library(brms) # for Bayesian models
library(tidybayes) # pretty Bayesian stuff
library(pander) # for slick tables
library(kableExtra) # for scrolling tables
library(lubridate) # working with times and durations
library(rcartocolor) # for nice colour palettes
library(patchwork) # for creating figures with multiple panels
library(workflowr) # for website building
library(DT) # for search- and saveable tables
img <- readPNG("SD_crossing_scheme.png")
grid.raster(img)
Figure S1. Crossing scheme used to standardise the genetic background across the SD-5/+, SD-72/+, SD-Mad/+ and SD+/+ lines. The SD+/+ line was created in identical fashion except that we subsituted the SD bearing chromosome with chromosome 2 from the w1118 isogenic line. Note that at step four there are three possible options 1) the leftmost genotype can be backcrossed to maintain it in the laboratory, 2) the leftmost genotype can be crossed to a bw stock to produce the experimental flies used in Experiment 1 or 3) the leftmost genotype can be crossed to w1118 to create the experimental flies used in Experiments 2 and 3.
\(~\)
Table S1: Recipe for food medium used in our experiment. The provided quantities make ~ 1 litre of food.
tibble("Ingredients" = c("Soy flour", "Cornmeal", "Yeast", "Dextrose", "Agar", "Water", "Tegosept", "Acid mix (4ml orthophosphoric acid, 41ml propionic acid, 55mL water to make 100mls)"),
"Quantity" = c("20 g", "73 g", "35 g", "75 g", "6 g", "1000 mls", "17 mls", "14 mls")) %>%
pander(split.cell = 40, split.table = Inf)
Ingredients | Quantity |
---|---|
Soy flour | 20 g |
Cornmeal | 73 g |
Yeast | 35 g |
Dextrose | 75 g |
Agar | 6 g |
Water | 1000 mls |
Tegosept | 17 mls |
Acid mix (4ml orthophosphoric acid, 41ml propionic acid, 55mL water to make 100mls) | 14 mls |
\(~\)
\(~\)
Load in the data from each of the experiments and prepare for analysis
# load the drive test data and create the total_female column
drive_test_data <-
read.csv("data/Drive_test.csv") %>%
mutate(total_female = SD.FEMALE + bw.FEMALE) %>%
as_tibble()
# reorder the factor levels for the SD variable, in ascending order of drive.
drive_test_data$SD <-
factor(drive_test_data$SD, levels = c("W1118", "MAD", "72", "5"))
drive_test_data <-
drive_test_data %>%
mutate(SEX = as.factor(SEX),
ID = as.factor(ID))
# Load in the data from Experiment 2. Remove unrequired columns, convert durations to a format that R can read, expressed in seconds, and log transform copulation latency.
precop_data <-
read.csv("data/two_choice_test_data.csv") %>%
select(-c(Mating_start_time, Mating_end_time)) %>%
mutate(Enter_time = as.numeric(hms(Enter_time)),
Copulation_latency = as.numeric(hms(Copulation_latency)),
Copulation_duration = as.numeric(hms(Copulation_duration)),
Rearing_vial = as.factor(Rearing_vial)) %>%
as_tibble()
# reorder the factor levels for the SD variable, in ascending order of drive
precop_data$SD <-
factor(precop_data$SD, levels = c("W1118", "MAD", "72"))
precop_duration_data <-
precop_data %>%
filter(Successful_male == "SD")
# read in the data from Experiment 3 and remove unrequired columns
postcop_data <-
read.csv("data/SD_mating_data.csv") %>%
select(- c(Start_1, Mating.start_1, Mating.end_1, Start_2, Mating.start_2, Mating.end_2)) %>%
as_tibble()
# reorder the factor levels for the SD variable, in ascending order of drive
postcop_data$SD <-
factor(postcop_data$SD, levels = c("W1118", "MAD", "72", "5"))
# First convert time data into readiable seconds. Then change the mating order variable so that SD is replaced with 'first' and LHM is replaced with 'second'. Add a censoring column, where 0 = the female remated and 1 = the female did not remate, which can be used in survival models. Finaly, create the total_offspring column and a mating duration difference column.
postcop_data <-
postcop_data %>%
# convert into readable seconds
mutate(Latency_1 = as.numeric(hms(Latency_1)),
Duration_1 = as.numeric(hms(Duration_1)),
Latency_2 = as.numeric(hms(Latency_2)),
Duration_2 = as.numeric(hms(Duration_2))) %>%
mutate(Mating.Order = if_else(Mating.Order == "SD", "first", "second"),
Censored = ifelse(Remating == "Y", 0, 1),
Block = as.factor(Block),
ID = as.factor(ID),
Rearing_vial = as.factor(Vial),
total_offspring = GFP.progeny + Treatment.progeny,
Mating_duration_diff = if_else(Mating.Order == "first", -1 * (Duration_2 - Duration_1), Duration_2 - Duration_1))
# create sperm comp data
# remove rows where we could not measure GFP progeny/treatment progeny or total offspring. This occurred when the female failed to mate with the 2nd male (hence there was no sperm competition), when a female escaped or died between mating opportunities, and rarely due to a data recording error. When the total number of offspring produced by a female = 0, we were unable to measure sperm competitive ability.
sperm_comp_data <-
postcop_data %>%
select(ID, Block, Mating.Order, Rearing_vial, SD, Treatment.progeny, GFP.progeny, total_offspring, Duration_1, Duration_2, Mating_duration_diff) %>%
filter(!is.na(GFP.progeny)) %>%
filter(!(total_offspring == 0))
# create remating data
# remove NAs corresponding to females that didn't mate in the first mating trial or that escaped or died and only include females that mated with SD males first
remating_data <-
postcop_data %>%
filter(!is.na(Remating),
Mating.Order == "first") %>%
select(ID, Block, Mating.Order, Rearing_vial, SD, Latency_1, Duration_1, Remating, Latency_2, Duration_2, Censored)
\(~\)
\(~\)
# Create a function to build HTML searchable tables
my_data_table <- function(df){
datatable(
df, rownames=FALSE,
autoHideNavigation = TRUE,
extensions = c("Scroller", "Buttons"),
options = list(
dom = 'Bfrtip',
deferRender=TRUE,
scrollX=TRUE, scrollY=400,
scrollCollapse=TRUE,
buttons =
list('pageLength', 'colvis', 'csv', list(
extend = 'pdf',
pageSize = 'A4',
orientation = 'landscape',
filename = 'Keaney_2021_Exp1_data')),
pageLength = 119
)
)
}
my_data_table(drive_test_data)
\(~\)
Column explanations
ID: individual flies.
Day: offspring counts were conducted over two days.
Sex: the sex of the experimental individual.
SD: the variant of the SD allele (or control allele) the experimental individual carried, homologous to the second chromosome carrying the bw mutation.
SD.FEMALE: the number of adult female offspring with red eyes - indicating inheritance of the SD allele
SD.MALE: the number of adult male offspring with red eyes - indicating inheritance of the SD allele
bw.FEMALE: the number of adult female offspring with brown eyes - indicating inheritance of the bw allele
bw.MALE: the number of adult male offspring with brown eyes - indicating inheritance of the bw allele
white.MALE: the number of adult male offspring with white eyes (inheritance of the SD allele is unkown for these offspring when the experimental individual was female)
total_female: the total number of female offspring sired by the experimental individual.
\(~\)
Parameter estimates for the fixed effects in the model are displayed on the log-odds scale.
drive_model <-
brm(SD.FEMALE | trials(total_female) ~ SEX * SD + (1|ID),
data = drive_test_data,
family = binomial,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
iter = 8000,
warmup = 2000,
chains = 4,
cores = 4,
seed = 2,
file = "fits/drive_model")
fixef(drive_model) %>%
pander(emphasize.strong.rows = c(6:8))
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | -0.01973 | 0.2122 | -0.4401 | 0.39 |
SEXM | 0.1677 | 0.3015 | -0.4168 | 0.7622 |
SDMAD | -0.1516 | 0.3058 | -0.7506 | 0.46 |
SD72 | -0.07362 | 0.3059 | -0.668 | 0.5179 |
SD5 | -0.08937 | 0.3001 | -0.6763 | 0.5048 |
SEXM:SDMAD | 1.734 | 0.4387 | 0.8901 | 2.624 |
SEXM:SD72 | 2.235 | 0.4541 | 1.352 | 3.135 |
SEXM:SD5 | 2.747 | 0.4451 | 1.888 | 3.628 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(drive_model, type = "hist", nsamples = 11, binwidth = 2) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get model predictions
# Define new data for prediction
new <-
expand.grid(total_female = 100, SD = unique(drive_test_data$SD), SEX = unique(drive_test_data$SEX)) %>%
mutate(Var1 = 1:8)
# Define new data for prediction with posteriors
new_posterior <-
expand.grid(total_female = 100, SD = unique(drive_test_data$SD), SEX = unique(drive_test_data$SEX)) %>%
mutate(id = paste("V", 1:8, sep = "")) %>%
as_tibble()
# Get the summarised predicted means
predictions <-
left_join(
melt(fitted(drive_model, newdata = new, re_formula = NA)) %>%
spread(Var2, value), new, by = "Var1") %>%
rename(Sex = SEX, `k - the estimated inheritance (%)` = Estimate) %>%
select(-c(Var1, total_female)) %>%
select(SD, Sex, `k - the estimated inheritance (%)`, Est.Error, Q2.5, Q97.5)
# Now get the predicted posterior
posterior_prediction <-
as_tibble(fitted(drive_model, newdata = new_posterior, re_formula = NA, summary = FALSE)) %>% # 8 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
tidyr::gather(key = id, value = percent_focal_offspring, -posterior_draw) %>%
left_join(new_posterior %>% select(-total_female), by = "id") %>%
select(-id) %>%
as_tibble()
\(~\)
Table S2: The percentage of female offspring that inherited an SD allele from a heterozygous parent k, split by the sex of the parent. SD only drives when present in males. n = 14-15 for all groups.
predictions %>%
mutate(`Variant carried` = recode(predictions$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5"),
Sex = recode(predictions$Sex, "F" = "Female", "M" = "Male")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
Variant carried | Sex | k - the estimated inheritance (%) | Est.Error | Q2.5 | Q97.5 |
---|---|---|---|---|---|
SD-5 | Female | 47.31 | 5.39 | 36.99 | 58.11 |
SD-72 | Female | 47.7 | 5.47 | 36.96 | 58.38 |
SD-Mad | Female | 45.78 | 5.46 | 35.26 | 56.7 |
W1118 | Female | 49.51 | 5.25 | 39.17 | 59.63 |
SD-5 | Male | 94.13 | 1.47 | 90.88 | 96.61 |
SD-72 | Male | 90.72 | 2.24 | 85.7 | 94.54 |
SD-Mad | Male | 84.7 | 3.1 | 77.96 | 90.04 |
W1118 | Male | 53.65 | 5.5 | 42.72 | 64.28 |
posterior_prediction %>%
mutate(SD = recode(posterior_prediction$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5"),
SEX = recode(posterior_prediction$SEX, "F" = "Females", "M" = "Males")) %>%
ggplot(aes(SD, percent_focal_offspring)) +
stat_eye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
geom_hline(yintercept = 50, linetype = 2) +
facet_wrap( ~ SEX) +
scale_y_continuous("Estimated inheritance (%)",
breaks = c(40, 50, 60, 70, 80, 90)) +
xlab("Variant carried (homologous to bw chromosome)") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Figure S2: The estimated percentage of female offspring that inherited an SD allele from a heterozygous parent, split by the sex of the parent. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution. The dotted line indicates 50% inheritance; the expectation in the absence of segregation distortion.
\(~\)
From these predictions we can calculate viability corrected estimates of k using the formula presented in Temin (1991, Genetics).
kc = number of SD progeny / (number of SD progeny + (number of bw progeny / W ))
Where W represents the transmission of the SD allele through females and is defined by:
W = number of bw progeny / number of SD progeny
kc values are presented in the table below
\(~\)
Table S3: the viability corrected inheritance (following Temin, 1991) of each SD variant and the control allele from a SD/+ (or +/+) male.
post <- posterior_samples(drive_model) %>%
as_tibble()
k_c_SD5 <-
post %>%
select(b_Intercept, b_SD5, `b_SEXM:SD5`) %>%
mutate(Female_SD_progeny = inv_logit_scaled(b_Intercept + b_SD5) * 100,
Female_bw_progeny = 100 - Female_SD_progeny,
W_SD5 = Female_bw_progeny / Female_SD_progeny,
Male_SD_progeny = inv_logit_scaled(b_Intercept + `b_SEXM:SD5`) * 100,
Male_bw_progeny = 100 - Male_SD_progeny) %>%
select(Male_SD_progeny, Male_bw_progeny, W_SD5) %>%
mutate(k_c = Male_SD_progeny / (Male_SD_progeny + (Male_bw_progeny / W_SD5))) %>%
summarise(`k corrected for viability costs` = median(k_c),
`Q2.5%` = quantile(k_c, probs = 0.025),
`Q97.5%` = quantile(k_c, probs = 0.975)) %>%
mutate(`Variant carried` = "SD-5")
k_c_SD72 <-
post %>%
select(b_Intercept, b_SD72, `b_SEXM:SD72`) %>%
mutate(Female_SD_progeny = inv_logit_scaled(b_Intercept + b_SD72) * 100,
Female_bw_progeny = 100 - Female_SD_progeny,
W_SD72 = Female_bw_progeny / Female_SD_progeny,
Male_SD_progeny = inv_logit_scaled(b_Intercept + `b_SEXM:SD72`) * 100,
Male_bw_progeny = 100 - Male_SD_progeny) %>%
select(Male_SD_progeny, Male_bw_progeny, W_SD72) %>%
mutate(k_c = Male_SD_progeny / (Male_SD_progeny + (Male_bw_progeny / W_SD72))) %>%
summarise(`k corrected for viability costs` = median(k_c),
`Q2.5%` = quantile(k_c, probs = 0.025),
`Q97.5%` = quantile(k_c, probs = 0.975)) %>%
mutate(`Variant carried` = "SD-72")
k_c_SDMAD <-
post %>%
select(b_Intercept, b_SDMAD, `b_SEXM:SDMAD`) %>%
mutate(Female_SD_progeny = inv_logit_scaled(b_Intercept + b_SDMAD) * 100,
Female_bw_progeny = 100 - Female_SD_progeny,
W_SDMAD = Female_bw_progeny / Female_SD_progeny,
Male_SD_progeny = inv_logit_scaled(b_Intercept + `b_SEXM:SDMAD`) * 100,
Male_bw_progeny = 100 - Male_SD_progeny) %>%
select(Male_SD_progeny, Male_bw_progeny, W_SDMAD) %>%
mutate(k_c = Male_SD_progeny / (Male_SD_progeny + (Male_bw_progeny / W_SDMAD))) %>%
summarise(`k corrected for viability costs` = median(k_c),
`Q2.5%` = quantile(k_c, probs = 0.025),
`Q97.5%` = quantile(k_c, probs = 0.975)) %>%
mutate(`Variant carried` = "SD-Mad")
k_c_W1118 <-
post %>%
select(b_Intercept, b_SEXM) %>%
mutate(Female_W1118_progeny = inv_logit_scaled(b_Intercept) * 100,
Female_bw_progeny = 100 - Female_W1118_progeny,
W_W1118 = Female_bw_progeny / Female_W1118_progeny,
Male_W1118_progeny = inv_logit_scaled(b_Intercept + b_SEXM) * 100,
Male_bw_progeny = 100 - Male_W1118_progeny) %>%
select(Male_W1118_progeny, Male_bw_progeny, W_W1118) %>%
mutate(k_c = Male_W1118_progeny / (Male_W1118_progeny + (Male_bw_progeny / W_W1118))) %>%
summarise(`k corrected for viability costs` = median(k_c),
`Q2.5%` = quantile(k_c, probs = 0.025),
`Q97.5%` = quantile(k_c, probs = 0.975)) %>%
mutate(`Variant carried` = "W1118")
bind_rows(k_c_SD5, k_c_SD72, k_c_SDMAD, k_c_W1118) %>%
select(`Variant carried`, everything()) %>%
pander(round = 3)
Variant carried | k corrected for viability costs | Q2.5% | Q97.5% |
---|---|---|---|
SD-5 | 0.944 | 0.818 | 0.985 |
SD-72 | 0.909 | 0.724 | 0.975 |
SD-Mad | 0.868 | 0.634 | 0.963 |
W1118 | 0.542 | 0.397 | 0.682 |
\(~\)
# Create a function to build HTML searchable tables
my_data_table <- function(df){
datatable(
df, rownames=FALSE,
autoHideNavigation = TRUE,
extensions = c("Scroller", "Buttons"),
options = list(
dom = 'Bfrtip',
deferRender=TRUE,
scrollX=TRUE, scrollY=400,
scrollCollapse=TRUE,
buttons =
list('pageLength', 'colvis', 'csv', list(
extend = 'pdf',
pageSize = 'A4',
orientation = 'landscape',
filename = 'Keaney_2021_Exp2_data')),
pageLength = 130
)
)
}
my_data_table(precop_data)
\(~\)
Column explanations
ID: individual flies.
SD: the variant of the SD allele (or control allele) the experimental individual carried, homologous to the second chromosome carrying the bw mutation.
Rearing_vial: the vial the individual male developed in. This variable is included to capture variation explained by the rearing enviornment e.g. small differences in food moisture content or quantity.
Experiment_start: I started data collection for Experiment 2 on the 10/12/2020 at 09:42 AEDT - lights were turned on at 07:00 AEDT.
Enter_time: the time in seconds after the start of the experiment that a female was introduced into a vial containing a SD/+ or control (w1118) male and a LBw male e.g. a value of 270 indicates that the female was added to the vial 270 seconds after the start of the experiment.
Copulation_latency: the time in seconds from a females entrance to a vial to the onset of copulation (Mating_start_time - Enter_time).
Copulation_duration: the time in seconds that the female mated with one of the two males (Mating_end_time - Mating_start_time).
Successful_male: the male that successfully mated with the female
Mating_occurred: did the female mate with one of the two males before the experiment ended?
Finish_time: the time in hms at which the Experiment ended.
\(~\)
Parameter estimates for the fixed effects in the model are displayed on the log-odds scale.
precop_success_model <-
brm(Successful_male ~ SD + (1 | Rearing_vial),
data = precop_data,
family = bernoulli,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
iter = 8000,
warmup = 2000,
chains = 4,
cores = 4,
seed = 2,
file = "fits/precop_success_model")
fixef(precop_success_model) %>%
pander()
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | -0.5512 | 0.3672 | -1.282 | 0.1607 |
SDMAD | 0.2022 | 0.5175 | -0.8132 | 1.201 |
SD72 | 0.2804 | 0.5311 | -0.7346 | 1.325 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(precop_success_model, type = "hist", nsamples = 11, binwidth = 0.5) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_precop_success <-
expand.grid(SD = unique(precop_data$SD)) %>%
mutate(id = paste("V", 1:3, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_precop_success <-
as.data.frame(fitted(precop_success_model, newdata = new_posterior_precop_success, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = prop_SD_success, -posterior_draw) %>%
left_join(new_posterior_precop_success, by = "id") %>%
select(-id) %>%
mutate(percent_SD_success = prop_SD_success * 100) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated % of males mating` = median(percent_SD_success),
`2.5%` = quantile(percent_SD_success, probs = 0.025),
`97.5%` = quantile(percent_SD_success, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_precop_success <-
as.data.frame(fitted(precop_success_model, newdata = new_posterior_precop_success, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = prop_SD_success, -posterior_draw) %>%
left_join(new_posterior_precop_success, by = "id") %>%
select(-id) %>%
mutate(percent_SD_success = prop_SD_success * 100) %>%
as_tibble()
\(~\)
Table S4a: The estimated percentage of SD/+ or control males that successfully mated with a female when competing with a single LBw male.
predictions_precop_success %>%
mutate(`Variant carried` = recode(predictions_precop_success$SD, "MAD" = "SD-Mad", "72" = "SD-72")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf)
Variant carried | Estimated % of males mating | 2.5% | 97.5% |
---|---|---|---|
W1118 | 36.6 | 21.73 | 54.01 |
SD-Mad | 41.5 | 25.18 | 58.79 |
SD-72 | 43.28 | 26.04 | 62.52 |
# get posterior samples
post_ms <- posterior_samples(precop_success_model)
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_ms <- post_ms %>%
mutate(p_w1118 = inv_logit_scaled(b_Intercept),
p_SD_MAD = inv_logit_scaled(b_SDMAD + b_Intercept),
p_SD_72 = inv_logit_scaled(b_SD72 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118) %>%
gather(key = `difference comparison`, value = `% difference`) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72"))
Table S4b: the mating success of SD/+ males relative to the w1118 control males. A value of 2 means that SD/+ males were twice as successful as w1118 control males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_ms %>%
group_by(`difference comparison`) %>%
summarise(`Mating success relative to w1118` = mean(`% difference`),
`2.5%` = quantile(`% difference`, probs = 0.025),
`97.5%` = quantile(`% difference`, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
SD-variant | Mating success relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-72 | 1.24 | 0.64 | 2.25 |
SD-Mad | 1.19 | 0.61 | 2.08 |
\(~\)
Creating Figure 1a and b
\(~\)
Panel a
mean_mating_success_plot <-
posterior_prediction_precop_success %>%
mutate(SD = recode(posterior_prediction_precop_success$SD, "MAD" = "SD-Mad", "72" = "SD-72")) %>%
ggplot(aes(SD, percent_SD_success)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b")) +
coord_flip() +
ylab("Estimated mating success (%) when competing\n against a single standard competitor") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
Mating_success_logodds <-
post_ms %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous(breaks = c(-1, 0, 1, 2, 3)) +
xlab(NULL) +
ylab("Log-odds difference from w1118") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
\(~\)
Here we take a more in depth look at mating success by looking at mating latency - the duration between a female’s introduction to the competing males’ vial and the onset of copulation.
Fit the model using mating latency as the response variable and specifiyng a weibull distribution (a time-to-event model).
Fixed effects are shown on the odds scale (the weibull model uses a log-link function rather than a logit-link function)
# Fit the model
mating_latency_survival_model<-
brm(Copulation_latency ~ SD + (1|Rearing_vial),
data = precop_duration_data,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
family = weibull, inits = 0,
cores = 4, chains = 4, iter = 8000, warmup = 2000,
control = list(adapt_delta = 0.99, max_treedepth = 10),
seed = 1, file = "fits/mating_latency_survival_model")
# display model results
fixef(mating_latency_survival_model) %>%
pander()
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 7.946 | 0.2658 | 7.426 | 8.484 |
SDMAD | -0.6498 | 0.3659 | -1.364 | 0.08986 |
SD72 | -0.4926 | 0.3682 | -1.218 | 0.2387 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(mating_latency_survival_model, type = "hist", nsamples = 11, binwidth = 1500) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_mating_latency <-
expand.grid(SD = unique(precop_duration_data$SD)) %>%
mutate(id = paste("V", 1:3, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_mating_latency <-
as.data.frame(fitted(mating_latency_survival_model, newdata = new_posterior_mating_latency, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_mating_latency, -posterior_draw) %>%
mutate(mean_mating_latency = mean_mating_latency / 60) %>%
left_join(new_posterior_mating_latency, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated time to mating (mins)` = median(mean_mating_latency),
`2.5%` = quantile(mean_mating_latency, probs = 0.025),
`97.5%` = quantile(mean_mating_latency, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_mating_latency <-
as.data.frame(fitted(mating_latency_survival_model, newdata = new_posterior_mating_latency, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_mating_latency, -posterior_draw) %>%
mutate(mean_mating_latency = mean_mating_latency / 60) %>% # if we want to express in secs then delete this
left_join(new_posterior_mating_latency, by = "id") %>%
select(-id) %>%
as_tibble()
\(~\)
Table S5a: The mean time taken for SD/+ or control males to mate with a LHm female.
predictions_mating_latency %>%
mutate(`Variant carried` = recode(predictions_mating_latency$SD, "MAD" = "SD-Mad", "72" = "SD-72")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 0)
Variant carried | Estimated time to mating (mins) | 2.5% | 97.5% |
---|---|---|---|
W1118 | 47 | 28 | 81 |
SD-Mad | 24 | 15 | 41 |
SD-72 | 29 | 17 | 49 |
# get posterior samples
post_ml <- posterior_samples(mating_latency_survival_model) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the exp() function converts the posterior draws onto the response scale
post_diff_ml <- post_ml %>%
mutate(p_w1118 = exp(b_Intercept),
p_SD_MAD = exp(b_SDMAD + b_Intercept),
p_SD_72 = exp(b_SD72 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118) %>%
gather(key = `difference comparison`, value = proportion) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72"))
Table S5b: the mean mating latency of males that successfully mated with LHm females when competing against Lbw males, relative to the w1118 control males. A value of 0.5 means that males mated in half the time of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_ml %>%
group_by(`difference comparison`) %>%
summarise(`Mean mating latency relative to w1118` = mean(proportion),
`2.5%` = quantile(proportion, probs = 0.025),
`97.5%` = quantile(proportion, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
SD-variant | Mean mating latency relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-72 | 0.66 | 0.3 | 1.27 |
SD-Mad | 0.56 | 0.26 | 1.09 |
\(~\)
Creating Figure S3
\(~\)
Panel a
mean_mating_latency_plot <-
posterior_prediction_mating_latency %>%
mutate(SD = recode(posterior_prediction_mating_latency$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, mean_mating_latency)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(0, 100)) +
ylab("Estimated time to mating\n (minutes)") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
mating_latency_odds <-
post_ml %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Odds difference from w1118",
breaks = c(-2, -1, 0, 1)) +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Combine panels into a single plot
mean_mating_latency_plot /
mating_latency_odds + plot_annotation(tag_levels = "a")
Figure S3: The duration required for an SD/+ male to mate with a LHm female and how this compares to w1118 control males. Panel a shows the estimated mating latency for SD/+ and control males. Panel b shows effect sizes on the odds scale for the SD variants. Negative values indicate that SD/+ males mated faster than w1118 control males. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution.
\(~\)
Here we take a more in depth look at mating success by looking at copulation duration - an indicator of male investment in mating.
Fit the model using copulation duration as the response variable and specifiyng a weibull distribution (a time-to-event model).
Fixed effects are shown on the odds scale (the weibull model uses a log-link function rather than a logit-link function)
copulation_duration_survival_model<-
brm(Copulation_duration ~ SD + (1|Rearing_vial),
data = precop_duration_data,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
family = weibull, inits = 0,
cores = 4, chains = 4, iter = 8000, warmup = 2000,
control = list(adapt_delta = 0.99, max_treedepth = 10),
seed = 1, file = "fits/copulation_duration_survival_model")
# display model results
fixef(copulation_duration_survival_model) %>%
pander()
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 7.176 | 0.05795 | 7.062 | 7.289 |
SDMAD | -0.008013 | 0.07812 | -0.1619 | 0.1437 |
SD72 | -0.009322 | 0.07935 | -0.1649 | 0.1495 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(copulation_duration_survival_model, type = "hist", nsamples = 11, binwidth = 200) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_copulation_duration <-
expand.grid(SD = unique(precop_duration_data$SD)) %>%
mutate(id = paste("V", 1:3, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_copulation_duration <-
as.data.frame(fitted(copulation_duration_survival_model, newdata = new_posterior_copulation_duration, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>%
left_join(new_posterior_copulation_duration, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated copulation duration (mins)` = median(mean_copulation_duration),
`2.5%` = quantile(mean_copulation_duration, probs = 0.025),
`97.5%` = quantile(mean_copulation_duration, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_copulation_duration <-
as.data.frame(fitted(copulation_duration_survival_model, newdata = new_posterior_copulation_duration, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>% # if we want to express in secs then delete this
left_join(new_posterior_copulation_duration, by = "id") %>%
select(-id) %>%
as_tibble()
\(~\)
Table S6a: The mean copulation duration when a SD/+ or control male mated with a LHm female.
predictions_copulation_duration %>%
mutate(`Variant carried` = recode(predictions_copulation_duration$SD, "MAD" = "SD-Mad", "72" = "SD-72")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 1)
Variant carried | Estimated copulation duration (mins) | 2.5% | 97.5% |
---|---|---|---|
W1118 | 21.8 | 19.5 | 24.4 |
SD-Mad | 21.6 | 19.3 | 24.1 |
SD-72 | 21.6 | 19.4 | 24.2 |
# get posterior samples
post_cd <- posterior_samples(copulation_duration_survival_model) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_cd <- post_cd %>%
mutate(p_w1118 = exp(b_Intercept),
p_SD_MAD = exp(b_SDMAD + b_Intercept),
p_SD_72 = exp(b_SD72 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118) %>%
gather(key = `difference comparison`, value = proportion) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72"))
Table S6b: the mean copulation duration for SD/+ males that successfully mated with LHm females when competing against Lbw males, relative to the w1118 control males. A value of 0.5 means that males mated in half the time of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_cd %>%
group_by(`difference comparison`) %>%
summarise(`Mean copulation duration relative to w1118` = mean(proportion),
`2.5%` = quantile(proportion, probs = 0.025),
`97.5%` = quantile(proportion, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 3)
SD-variant | Mean copulation duration relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-72 | 0.993 | 0.849 | 1.162 |
SD-Mad | 0.995 | 0.851 | 1.157 |
\(~\)
Creating Figure S4
\(~\)
Panel a
mean_copulation_duration_plot <-
posterior_prediction_copulation_duration %>%
mutate(SD = recode(posterior_prediction_copulation_duration$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, mean_copulation_duration)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(15, 30)) +
ylab("Estimated copulation duration\n (minutes)") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
copulation_duration_odds <-
post_cd %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72) %>%
gather(key = parameter, value = odds) %>%
filter(parameter == c("SD-Mad", "SD-72")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72"))) %>%
ggplot(aes(parameter, odds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Odds difference from w1118",
breaks = c(-0.25, 0, 0.25)) +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Combine panels into a single plot
mean_copulation_duration_plot /
(copulation_duration_odds) + plot_annotation(tag_levels = "a")
Figure S4: No difference in the duration of mating between a SD/+ male and a LHm female, compared to w1118 control males. Panel a shows the estimated copulation duration for SD/+ and control males. Panel b shows effect sizes on the odds scale for the SD variants. Positive values indicate that SD/+ males mated for longer than w1118 control males. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution.
\(~\)
# Create a function to build HTML searchable tables
my_data_table <- function(df){
datatable(
df, rownames=FALSE,
autoHideNavigation = TRUE,
extensions = c("Scroller", "Buttons"),
options = list(
dom = 'Bfrtip',
deferRender=TRUE,
scrollX=TRUE, scrollY=400,
scrollCollapse=TRUE,
buttons =
list('pageLength', 'colvis', 'csv', list(
extend = 'pdf',
pageSize = 'A4',
orientation = 'landscape',
filename = 'Keaney_2021_Exp3_data')),
pageLength = 442
)
)
}
my_data_table(postcop_data %>%
filter(Remating != "NA") %>%
select(ID, Block, Mating.Order, Rearing_vial, SD, Latency_1, Duration_1, Remating, Latency_2, Duration_2, Mating_duration_diff, Treatment.progeny, GFP.progeny, total_offspring, Censored))
\(~\)
Column explanations
ID: identifier for individual flies.
Block: the experiment was replicated on three consecutive generations of flies, over a 6 week period.
Mating.Order: did the SD/+ male mate with the female first or second?
Rearing_vial: the vial within which the individual developed in.
SD: the variant of the SD allele (or control allele) the experimental individual carried.
Latency_1: the time in seconds between the male entering the mating vial and the onset of the first mating.
Duration_1: the time in seconds between the onset and end of the first mating.
Remating: did the female remate within the three hour period, four days after her first mating.
Latency_2: the time in minutes between the male entering the mating vial and the onset of the second mating.
Duration_2: the time in minutes between the onset and end of the second mating.
Treatment.progeny: the number of larvae sired by the SD/+ or control male. These larvae did not inherit the UBI GFP construct and therefore did not express green fluorescence.
GFP.progeny: the number of larvae sired by the LHmUBU competitor male. These larvae inherited the UBI GFP construct and therefore expressed green fluorescence.
total_offspring: the total number of offspring counted in each vial.
Censored: Identical to remating column except in a 1/0 format for survival model right censoring. 1 = the female remated within three hours, 0 = the female did not remate.
\(~\)
Take a glance at how mating order affected fertilisation success
sperm_comp_data %>%
group_by(Mating.Order) %>%
summarise(total_SD = sum(Treatment.progeny),
total_GFP = sum(GFP.progeny),
total = sum(total_offspring),
`Number of mating trios` = n()) %>%
mutate(Mating.Order = if_else(Mating.Order == "first", "SD/+ male mated first", "LHm-UBI male mated first")) %>%
mutate(`% offspring sired by first mate` = if_else(Mating.Order == "SD/+ male mated first", total_SD / total * 100, total_GFP / total * 100)) %>%
rename(`Mating Order` = Mating.Order,
`Offspring sired by SD/+ males` = total_SD,
`Offspring sired by LHm males` = total_GFP,
`Total offspring sired` = total) %>%
select(`Mating Order`, `Number of mating trios`, everything()) %>%
pander(split.cell = 40, split.table = Inf)
Mating Order | Number of mating trios | Offspring sired by SD/+ males | Offspring sired by LHm males | Total offspring sired | % offspring sired by first mate |
---|---|---|---|---|---|
SD/+ male mated first | 97 | 294 | 2545 | 2839 | 10.36 |
LHm-UBI male mated first | 130 | 4011 | 308 | 4319 | 7.131 |
\(~\)
Fit the model.
Parameter estimates for the fixed effects in the model are displayed on the log-odds scale.
# filter so that only trials where the SD male mated with the female first are included
sperm_comp_data_P1 <-
sperm_comp_data %>%
filter(Mating.Order == "first") %>%
mutate(Mating_duration_diff = as.numeric(Mating_duration_diff))
# fit the model
sperm_comp_model_P1 <- brm(Treatment.progeny | trials(total_offspring) ~ SD + Block + (1|Rearing_vial) + (1|ID),
data = sperm_comp_data_P1,
family = binomial,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
iter = 8000,
warmup = 2000,
chains = 4,
cores = 4,
seed = 2,
control = list(adapt_delta = 0.99, max_treedepth = 15),
file = "fits/sperm_comp_model_P1")
# display model results
fixef(sperm_comp_model_P1) %>%
pander(emphasize.strong.rows = 4)
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | -1.802 | 1.006 | -3.806 | 0.1443 |
SDMAD | -1.572 | 1.071 | -3.671 | 0.5493 |
SD72 | -1.421 | 1.025 | -3.453 | 0.5931 |
SD5 | -2.465 | 0.9809 | -4.462 | -0.5663 |
Block2 | -0.3604 | 1.024 | -2.349 | 1.659 |
Block3 | -1.482 | 0.952 | -3.396 | 0.3335 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(sperm_comp_model_P1, type = "hist", nsamples = 11, binwidth = 2) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the P1 model
# Define new data for prediction with posteriors
new_posterior_P1 <-
expand.grid(total_offspring = 100, SD = c("W1118", "MAD", "72", "5"), Block = unique(sperm_comp_data_P1$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
predictions_P1 <-
as.data.frame(fitted(sperm_comp_model_P1, newdata = new_posterior_P1, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = percent_focal_offspring, -posterior_draw) %>%
left_join(new_posterior_P1, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated % of offspring sired` = median(percent_focal_offspring),
`2.5%` = quantile(percent_focal_offspring, probs = 0.025),
`97.5%` = quantile(percent_focal_offspring, probs = 0.975))
# Now get the predicted posterior but don't summarise
posterior_prediction_P1 <-
as.data.frame(fitted(sperm_comp_model_P1, newdata = new_posterior_P1, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = percent_focal_offspring, -posterior_draw) %>%
left_join(new_posterior_P1, by = "id") %>%
select(-id) %>%
as_tibble()
\(~\)
Table S7a: The estimated percentage of offspring sired by SD/+ or control males, when they mated first.
predictions_P1 %>%
mutate(`Variant carried` = recode(predictions_P1$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
Variant carried | Estimated % of offspring sired | 2.5% | 97.5% |
---|---|---|---|
W1118 | 8.17 | 0.99 | 44.4 |
SD-Mad | 1.83 | 0.17 | 16.28 |
SD-72 | 2.15 | 0.21 | 16.97 |
SD-5 | 0.78 | 0.08 | 5.79 |
# get posterior samples
post_P1 <-
posterior_samples(sperm_comp_model_P1) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_P1 <-
post_P1 %>%
mutate(p_w1118 = inv_logit_scaled(b_Intercept),
p_SD_MAD = inv_logit_scaled(b_SDMAD + b_Intercept),
p_SD_72 = inv_logit_scaled(b_SD72 + b_Intercept),
p_SD_5 = inv_logit_scaled(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = `% difference`) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S7b: the mean proportion of offspring sired by each SD variant relative to the w1118 control, when these males mated with LHm females first. A value of 0.5 means that the proportion of offspring sired was half that of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_P1 %>%
group_by(`difference comparison`) %>%
summarise(`Mean proportion of offspring sired relative to w1118` = mean(`% difference`),
`2.5%` = quantile(`% difference`, probs = 0.025),
`97.5%` = quantile(`% difference`, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
SD-variant | Mean proportion of offspring sired relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 0.15 | 0.02 | 0.6 |
SD-72 | 0.43 | 0.05 | 1.65 |
SD-Mad | 0.39 | 0.04 | 1.7 |
\(~\)
Creating Figure 1c and d
\(~\)
Panel c
mean_P1_plot <-
posterior_prediction_P1 %>%
mutate(SD = recode(posterior_prediction_P1$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, percent_focal_offspring)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-5" = "#4c9b82", "SD-72" = "#6cc08b", "SD-Mad" = "#97e196", "W1118" = "#d3f2a3")) +
coord_flip(ylim = c(0, 50)) +
ylab("Estimated % of offspring sired when mating first (P1)") +
xlab("Variant carried") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel d
P1_logodds <-
post_P1 %>%
mutate(`SD-5` = b_SD5,
`SD-72` = b_SD72,
`SD-Mad` = b_SDMAD) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous(breaks = c(-4, -2, 0, 2)) +
xlab(NULL) +
ylab("Log-odds difference from w1118") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
\(~\)
\(~\)
Fit the model.
Parameter estimates for the fixed effects in the model are displayed on the log-odds scale.
# Filter so that only trials where the SD male mated with the female second are included
sperm_comp_data_P2 <-
sperm_comp_data %>%
filter(Mating.Order == "second")
# fit the model
sperm_comp_model_P2 <- brm(Treatment.progeny | trials(total_offspring) ~ SD + Block + (1|Rearing_vial) + (1|ID),
data = sperm_comp_data_P2,
family = binomial,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
iter = 8000,
warmup = 2000,
chains = 4,
cores = 4,
seed = 2,
control = list(adapt_delta = 0.999, max_treedepth = 15),
file = "fits/sperm_comp_model_P2")
# display model results
fixef(sperm_comp_model_P2) %>%
pander(emphasize.strong.rows = c(2, 4))
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 3.828 | 0.4994 | 2.875 | 4.84 |
SDMAD | 1.498 | 0.6331 | 0.289 | 2.764 |
SD72 | -0.1273 | 0.5419 | -1.201 | 0.924 |
SD5 | -1.247 | 0.5699 | -2.378 | -0.1209 |
Block2 | -0.7958 | 0.4789 | -1.727 | 0.1544 |
Block3 | 1.101 | 0.5725 | -0.01236 | 2.23 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(sperm_comp_model_P2, type = "hist", nsamples = 11, binwidth = 2) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the P2 model
# Define new data for prediction with posteriors
new_posterior_P2 <-
expand.grid(total_offspring = 100, SD = c("W1118", "MAD", "72", "5"), Block = unique(sperm_comp_data_P2$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_P2 <-
as.data.frame(fitted(sperm_comp_model_P2, newdata = new_posterior_P2, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = percent_focal_offspring, -posterior_draw) %>%
left_join(new_posterior_P2, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated % of offspring sired` = median(percent_focal_offspring),
`2.5%` = quantile(percent_focal_offspring, probs = 0.025),
`97.5%` = quantile(percent_focal_offspring, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_P2 <-
as.data.frame(fitted(sperm_comp_model_P2, newdata = new_posterior_P2, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = percent_focal_offspring, -posterior_draw) %>%
left_join(new_posterior_P2, by = "id") %>%
select(-id) %>%
as_tibble()
\(~\)
Table S8a: The estimated percentage of offspring sired by SD/+ or control males, when they mated second.
predictions_P2 %>%
mutate(`Variant carried` = recode(predictions_P2$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
Variant carried | Estimated % of offspring sired | 2.5% | 97.5% |
---|---|---|---|
W1118 | 97.9 | 91.61 | 99.7 |
SD-Mad | 99.52 | 97.61 | 99.94 |
SD-72 | 97.63 | 90.34 | 99.65 |
SD-5 | 93.16 | 74.51 | 98.91 |
\(~\)
# get posterior samples
post_P2 <- posterior_samples(sperm_comp_model_P2)
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_P2 <- post_P2 %>%
mutate(p_w1118 = inv_logit_scaled(b_Intercept),
p_SD_MAD = inv_logit_scaled(b_SDMAD + b_Intercept),
p_SD_72 = inv_logit_scaled(b_SD72 + b_Intercept),
p_SD_5 = inv_logit_scaled(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = `% difference`) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S8b: the mean proportion of offspring sired by each SD variant relative to the w1118 control, when these males mated with LHm females second. A value of 0.5 means that the proportion of offspring sired was half that of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_P2 %>%
group_by(`difference comparison`) %>%
summarise(`Mean proportion of offspring sired relative to w1118` = mean(`% difference`),
`2.5%` = quantile(`% difference`, probs = 0.025),
`97.5%` = quantile(`% difference`, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 3)
SD-variant | Mean proportion of offspring sired relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 0.945 | 0.855 | 0.997 |
SD-72 | 0.997 | 0.969 | 1.027 |
SD-Mad | 1.019 | 1.002 | 1.049 |
\(~\)
Creating Figure 1e and f
\(~\)
Panel e
mean_P2_plot <-
posterior_prediction_P2 %>%
mutate(SD = recode(posterior_prediction_P2$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, percent_focal_offspring)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(70, 100)) +
ylab("Estimated % of offspring sired when mating second (P2)") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel f
P2_logodds <-
post_P2 %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72,
`SD-5` = b_SD5) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous(breaks = c(-3, -2, -1, 0, 1, 2, 3)) +
xlab(NULL) +
ylab("Log-odds difference from w1118") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
\(~\)
\(~\)
Fit the model - here we model remating as a binary YES/NO response.
remating_model <-
brm(Remating ~ SD + Block + (1|Rearing_vial),
data = remating_data,
family = bernoulli,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
iter = 8000,
warmup = 2000,
chains = 4,
cores = 4,
seed = 2,
control = list(adapt_delta = 0.99),
file = "fits/remating_model")
# display model results
fixef(remating_model) %>%
pander(emphasize.strong.rows = 4)
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | -0.8325 | 0.4563 | -1.737 | 0.04211 |
SDMAD | 0.5413 | 0.4877 | -0.419 | 1.511 |
SD72 | 0.5333 | 0.479 | -0.3908 | 1.493 |
SD5 | 1.968 | 0.4963 | 1.027 | 2.978 |
Block2 | -0.8197 | 0.4633 | -1.733 | 0.08466 |
Block3 | 0.7758 | 0.4474 | -0.09273 | 1.672 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The outcome variable in this model is binary, hence why only 0 and 1 values appear. The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(remating_model, type = "hist", nsamples = 11, binwidth = 0.5) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_remating <-
expand.grid(SD = unique(remating_data$SD), Block = unique(remating_data$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_remating <-
as.data.frame(fitted(remating_model, newdata = new_posterior_remating, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = prop_females_remating, -posterior_draw) %>%
left_join(new_posterior_remating, by = "id") %>%
select(-id) %>%
mutate(percent_females_remating = prop_females_remating * 100) %>%
as_tibble() %>%
group_by(SD, Block) %>%
summarise(`Estimated % of mates remating` = median(percent_females_remating),
`2.5%` = quantile(percent_females_remating, probs = 0.025),
`97.5%` = quantile(percent_females_remating, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_remating <-
as.data.frame(fitted(remating_model, newdata = new_posterior_remating, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = prop_females_remating, -posterior_draw) %>%
left_join(new_posterior_remating, by = "id") %>%
select(-id) %>%
mutate(percent_females_remating = prop_females_remating * 100) %>%
as_tibble()
\(~\)
Table S9a: The estimated percentage of females that remated that were first mated to a SD/+ or control male.
predictions_remating %>%
mutate(`Variant carried` = SD) %>%
mutate(`Variant carried` = recode(SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ungroup() %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf)
Variant carried | Block | Estimated % of mates remating | 2.5% | 97.5% |
---|---|---|---|---|
W1118 | 1 | 30.36 | 14.97 | 51.05 |
W1118 | 2 | 16.22 | 7.473 | 29.98 |
W1118 | 3 | 48.62 | 30.85 | 66.63 |
SD-Mad | 1 | 42.93 | 22.99 | 65.09 |
SD-Mad | 2 | 24.9 | 11.75 | 43.87 |
SD-Mad | 3 | 61.8 | 41.97 | 78.74 |
SD-72 | 1 | 42.54 | 23.09 | 64.14 |
SD-72 | 2 | 24.72 | 11.72 | 43.59 |
SD-72 | 3 | 61.53 | 41.95 | 78.84 |
SD-5 | 1 | 75.46 | 55.54 | 89.24 |
SD-5 | 2 | 57.68 | 39.09 | 75.63 |
SD-5 | 3 | 86.97 | 74.02 | 94.58 |
# get posterior samples
post_r <- posterior_samples(remating_model)
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_r <- post_r %>%
mutate(p_w1118 = inv_logit_scaled(b_Intercept),
p_SD_MAD = inv_logit_scaled(b_SDMAD + b_Intercept),
p_SD_72 = inv_logit_scaled(b_SD72 + b_Intercept),
p_SD_5 = inv_logit_scaled(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = `% difference`) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S9b: the mean proportion of mates remating with LHmUBI males for SD/+ males relative to the w1118 control males. A value of 2 means that remating was twice as likely when females intially mated with a SD/+ male compared with a w1118 male.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_r %>%
group_by(`difference comparison`) %>%
summarise(`Mean proportion of mates remating relative to w1118` = mean(`% difference`),
`2.5%` = quantile(`% difference`, probs = 0.025),
`97.5%` = quantile(`% difference`, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
SD-variant | Mean proportion of mates remating relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 2.61 | 1.52 | 4.66 |
SD-72 | 1.47 | 0.79 | 2.65 |
SD-Mad | 1.48 | 0.77 | 2.65 |
\(~\)
Creating Figure 1g and h
\(~\)
Panel g
mean_remating_plot <-
posterior_prediction_remating %>%
mutate(SD = recode(posterior_prediction_remating$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
filter(Block == "1") %>% # here we select data from the first block to make the plot look nicer - this doesn't affect the differences between each SD variant and the control, it just shrinks the uncertainty that occurs because of differences between blocks
ggplot(aes(SD, percent_females_remating)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
ylab("Estimated % of females that mated twice") +
xlab("Male variant mated to first") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel h
Remating_logodds <-
post_r %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72,
`SD-5` = b_SD5) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Log-odds difference from w1118",
breaks = c(-1, 0, 1, 2, 3)) +
xlab(NULL) +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
\(~\)
\(~\)
Here we take a more in depth look at remating by looking at remating latency within the three hour remating period.
Fit the model - this time using remating latency as the response variable and specifiyng a weibull distribution (a time-to-event model).
Fixed effects are shown on the odds scale (the weibull model uses a log-link function rather than a logit-link function)
# Fit the model with right censoring
remating_survival_model_censored <-
brm(Latency_2 | cens(Censored) ~ SD + Block + (1|Rearing_vial),
data = remating_data,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
family = weibull, inits = 0,
cores = 4, chains = 4, iter = 8000, warmup = 2000,
control = list(adapt_delta = 0.95, max_treedepth = 10),
seed = 1, file = "fits/remating_survival_model_censored")
# display model results
fixef(remating_survival_model_censored) %>%
pander(emphasize.strong.rows = 4:5)
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 8.846 | 0.3019 | 8.265 | 9.455 |
SDMAD | -0.215 | 0.3167 | -0.8382 | 0.4079 |
SD72 | -0.1452 | 0.3137 | -0.7689 | 0.4681 |
SD5 | -0.6866 | 0.2866 | -1.263 | -0.1422 |
Block2 | 0.645 | 0.2874 | 0.08167 | 1.217 |
Block3 | -0.09898 | 0.2633 | -0.6337 | 0.4039 |
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(remating_survival_model_censored, type = "hist", nsamples = 11, binwidth = 600) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_remating_latency <-
expand.grid(SD = unique(remating_data$SD), Block = unique(remating_data$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_remating_latency <-
as.data.frame(fitted(remating_survival_model_censored, newdata = new_posterior_remating_latency, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = avg_remating_latency, -posterior_draw) %>%
left_join(new_posterior_remating_latency, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD, Block) %>%
summarise(`Estimated time to remating (mins)` = median(avg_remating_latency) / 60,
`2.5%` = quantile(avg_remating_latency, probs = 0.025) / 60,
`97.5%` = quantile(avg_remating_latency, probs = 0.975) / 60)
# Now get the predicted posterior
posterior_prediction_remating_latency <-
as.data.frame(fitted(remating_survival_model_censored, newdata = new_posterior_remating_latency, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = avg_remating_latency, -posterior_draw) %>%
left_join(new_posterior_remating_latency, by = "id") %>%
mutate(avg_remating_latency = avg_remating_latency / 60) %>%
select(-id) %>%
as_tibble()
\(~\)
Table S10a: The estimated time taken for females to remate, when provided with an opportunity, four days after initially mating with a SD/+ or control male.
predictions_remating_latency %>%
#mutate(`Variant carried` = SD) %>%
mutate(`Variant carried` = fct_recode(SD, "SD-Mad" = "MAD", "SD-72" = "72", "SD-5" = "5")) %>%
ungroup() %>%
select(`Variant carried`, Block, `Estimated time to remating (mins)`, `2.5%`, `97.5%`) %>%
pander(split.cell = 40, split.table = Inf, round = 1)
Variant carried | Block | Estimated time to remating (mins) | 2.5% | 97.5% |
---|---|---|---|---|
W1118 | 1 | 115 | 64.8 | 212.8 |
W1118 | 2 | 218.3 | 127.4 | 402.6 |
W1118 | 3 | 104.6 | 65.5 | 169.7 |
SD-Mad | 1 | 92.4 | 53.6 | 172.1 |
SD-Mad | 2 | 176.1 | 100.4 | 332.5 |
SD-Mad | 3 | 84.3 | 53.2 | 136.9 |
SD-72 | 1 | 99.4 | 57.4 | 182.4 |
SD-72 | 2 | 188.9 | 114.9 | 332.9 |
SD-72 | 3 | 90.5 | 55.2 | 149.7 |
SD-5 | 1 | 58.1 | 36.6 | 94.6 |
SD-5 | 2 | 110.6 | 71.2 | 176 |
SD-5 | 3 | 52.9 | 33.6 | 81.9 |
# get posterior samples
post_l <- posterior_samples(remating_survival_model_censored) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the inv_logit_scaled() function converts the posterior draws onto the response scale
post_diff_l <- post_l %>%
mutate(p_w1118 = exp(b_Intercept),
p_SD_MAD = exp(b_SDMAD + b_Intercept),
p_SD_72 = exp(b_SD72 + b_Intercept),
p_SD_5 = exp(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = proportion) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S10b: the mean remating latency of females that initially mated with SD/+ males, relative to the w1118 control males. A value of 0.5 means that females remated in half the time of females that intially mated with a w1118 male.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_l %>%
group_by(`difference comparison`) %>%
summarise(`Mean remating latency relative to w1118` = mean(proportion),
`2.5%` = quantile(proportion, probs = 0.025),
`97.5%` = quantile(proportion, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 2)
SD-variant | Mean remating latency relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 0.52 | 0.28 | 0.88 |
SD-72 | 0.91 | 0.47 | 1.6 |
SD-Mad | 0.85 | 0.43 | 1.5 |
\(~\)
Creating Figure S5
\(~\)
Panel a
mean_latency_plot <-
posterior_prediction_remating_latency %>%
mutate(SD = recode(posterior_prediction_remating_latency$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
filter(Block != "2") %>%
ggplot(aes(SD, avg_remating_latency)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(0, 250)) +
ylab("Estimated time to remating\n (minutes)") +
xlab("Male variant mated to first") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
latency_odds <-
post_l %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72,
`SD-5` = b_SD5) %>%
gather(key = parameter, value = logodds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, logodds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Odds difference from w1118",
breaks = c(-2, -1, 0, 1)) +
xlab("Male variant mated to first") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Combine panels into a single plot
mean_latency_plot /
(latency_odds) + plot_annotation(tag_levels = "a")
Figure S5: The effect that SD/+ males have on female remating latency and how this compares to w1118 control males. Panel a shows the estimated remating latency for females exposed to LHmUBI males over a three-hour period, that had mated with a SD/+ or control male four days earlier. Block 2 produced very different results to Blocks 1 and 3, so we exclude it from panel a to aid visual clarity (differences between the SD variants and the control do not change). Panel b shows effect sizes on the odds scale for the SD variants. Negative values indicate that the mates of SD/+ males remated faster than the mates of w1118 control males. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution.
\(~\)
\(~\)
Here we look at copulation duration - an indicator of male investment in mating.
Fit the model using copulation duration as the response variable and specifiyng a weibull distribution (a time-to-event model).
Fixed effects are shown on the odds scale (the weibull model uses a log-link function rather than a logit-link function)
# Filter so that only trials where the SD male mated with the female second are included
copulation_data_1 <-
sperm_comp_data %>%
filter(Mating.Order == "first")
copulation_duration_survival_model_1 <-
brm(Duration_1 ~ SD + Block + (1|Rearing_vial),
data = copulation_data_1,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
family = weibull, inits = 0,
cores = 4, chains = 4, iter = 8000, warmup = 2000,
control = list(adapt_delta = 0.99, max_treedepth = 10),
seed = 1, file = "fits/copulation_duration_survival_model_1")
# display model results
fixef(copulation_duration_survival_model_1) %>%
pander()
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 7.084 | 0.08901 | 6.914 | 7.266 |
SDMAD | 0.03873 | 0.09641 | -0.1529 | 0.2254 |
SD72 | 0.1313 | 0.09418 | -0.05449 | 0.316 |
SD5 | 0.1076 | 0.08616 | -0.06409 | 0.2748 |
Block2 | -0.09988 | 0.08873 | -0.2755 | 0.07346 |
Block3 | 0.008328 | 0.08041 | -0.154 | 0.1611 |
\(~\)
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(copulation_duration_survival_model_1, type = "hist", nsamples = 11, binwidth = 200) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_copulation_duration_1 <-
expand.grid(SD = unique(copulation_data_1$SD), Block = unique(copulation_data_1$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_copulation_duration_1 <-
as.data.frame(fitted(copulation_duration_survival_model_1, newdata = new_posterior_copulation_duration_1, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>%
left_join(new_posterior_copulation_duration_1, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated copulation duration (mins)` = median(mean_copulation_duration),
`2.5%` = quantile(mean_copulation_duration, probs = 0.025),
`97.5%` = quantile(mean_copulation_duration, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_copulation_duration_1 <-
as.data.frame(fitted(copulation_duration_survival_model_1, newdata = new_posterior_copulation_duration_1, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>% # if we want to express in secs then delete this
left_join(new_posterior_copulation_duration_1, by = "id") %>%
select(-id) %>%
as_tibble()
Table S11a: The mean copulation duration when a SD/+ male mated with a LHm female in the first mating role.
predictions_copulation_duration_1 %>%
mutate(`Variant carried` = recode(predictions_copulation_duration_1$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 1)
Variant carried | Estimated copulation duration (mins) | 2.5% | 97.5% |
---|---|---|---|
W1118 | 19.3 | 16 | 23.2 |
SD-Mad | 20.1 | 16.4 | 24 |
SD-72 | 22 | 18.3 | 26.5 |
SD-5 | 21.5 | 18 | 25.3 |
# get posterior samples
post_cd_1 <- posterior_samples(copulation_duration_survival_model_1) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the exp() function converts the posterior draws onto the response scale
post_diff_cd_1 <- post_cd_1 %>%
mutate(p_w1118 = exp(b_Intercept),
p_SD_MAD = exp(b_SDMAD + b_Intercept),
p_SD_72 = exp(b_SD72 + b_Intercept),
p_SD_5 = exp(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = proportion) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S11b: the mean copulation duration for SD/+ males that mated with a LHm female in the second mating role, relative to the w1118 control males. A value of 0.5 means that males mated for half the time of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_cd_1 %>%
group_by(`difference comparison`) %>%
summarise(`Mean copulation duration relative to w1118` = mean(proportion),
`2.5%` = quantile(proportion, probs = 0.025),
`97.5%` = quantile(proportion, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 3)
SD-variant | Mean copulation duration relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 1.118 | 0.936 | 1.314 |
SD-72 | 1.145 | 0.946 | 1.371 |
SD-Mad | 1.044 | 0.857 | 1.253 |
\(~\)
Creating Figure S6
\(~\)
Panel a
mean_copulation_duration_plot_1 <-
posterior_prediction_copulation_duration_1 %>%
mutate(SD = recode(posterior_prediction_copulation_duration_1$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, mean_copulation_duration)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(10, 35)) +
ylab("Estimated copulation duration in\nfirst mating role (minutes)") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
copulation_duration_odds_1 <-
post_cd_1 %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72,
`SD-5` = b_SD5) %>%
gather(key = parameter, value = odds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, odds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Odds difference from w1118",
breaks = c(-0.25, 0, 0.25, 0.5)) +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Combine panels into a single plot
mean_copulation_duration_plot_1 /
(copulation_duration_odds_1) + plot_annotation(tag_levels = "a")
Figure S6: No difference in the duration of mating between a SD/+ male and a LHm female, compared to w1118 control males, when the SD/+ (or control) male mated first. Panel a shows the estimated copulation duration for SD/+ and control males. Panel b shows effect sizes on the odds scale for the SD-variants. Positive values indicate that SD/+ males mated for longer than w1118 control males. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution.
\(~\)
\(~\)
Here we look at copulation duration - an indicator of male investment in mating.
Fit the model using copulation duration as the response variable and specifiyng a weibull distribution (a time-to-event model).
Fixed effects are shown on the odds scale (the weibull model uses a log-link function rather than a logit-link function)
# Filter so that only trials where the SD male mated with the female second are included
copulation_data_2 <-
sperm_comp_data %>%
filter(Mating.Order == "second")
copulation_duration_survival_model_2 <-
brm(Duration_2 ~ SD + Block + (1|Rearing_vial),
data = copulation_data_2,
prior = c(prior(normal(0, 5), class = Intercept),
prior(normal(0, 3), class = b)),
family = weibull, inits = 0,
cores = 4, chains = 4, iter = 8000, warmup = 2000,
control = list(adapt_delta = 0.99, max_treedepth = 10),
seed = 1, file = "fits/copulation_duration_survival_model_2")
# display model results
fixef(copulation_duration_survival_model_2) %>%
pander(emphasize.strong.rows = 3)
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
Intercept | 7.047 | 0.1219 | 6.811 | 7.29 |
SDMAD | 0.1024 | 0.1436 | -0.1853 | 0.3842 |
SD72 | 0.2894 | 0.1426 | 0.005337 | 0.5686 |
SD5 | 0.05127 | 0.1415 | -0.2255 | 0.3313 |
Block2 | -0.05703 | 0.1236 | -0.2994 | 0.1879 |
Block3 | -0.06026 | 0.1265 | -0.3065 | 0.193 |
\(~\)
Evaluate model fit using a posterior predictive check
A posterior predictive check works by using the model to predict the dataset upon which it was trained. Here, 11 draws are shown from the posterior predictive distribution (light blue), as well as the real dataset (dark blue). The datasets predicted from the model are reasonably similar to the real dataset, indicating that the model is a reasonably good fit.
pp_check(copulation_duration_survival_model_2, type = "hist", nsamples = 11, binwidth = 200) +
theme_minimal() +
theme(panel.background = element_blank())
\(~\)
Get predictions from the model
# Define new data for prediction with posteriors
new_posterior_copulation_duration_2 <-
expand.grid(SD = unique(copulation_data_2$SD), Block = unique(copulation_data_2$Block)) %>%
mutate(id = paste("V", 1:12, sep = "")) %>%
as_tibble()
# Get predicted means
predictions_copulation_duration_2 <-
as.data.frame(fitted(copulation_duration_survival_model_2, newdata = new_posterior_copulation_duration_2, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>%
left_join(new_posterior_copulation_duration_2, by = "id") %>%
select(-id) %>%
as_tibble() %>%
group_by(SD) %>%
summarise(`Estimated copulation duration (mins)` = median(mean_copulation_duration),
`2.5%` = quantile(mean_copulation_duration, probs = 0.025),
`97.5%` = quantile(mean_copulation_duration, probs = 0.975))
# Now get the predicted posterior
posterior_prediction_copulation_duration_2 <-
as.data.frame(fitted(copulation_duration_survival_model_2, newdata = new_posterior_copulation_duration_2, re_formula = NA, summary = FALSE)) %>% # 12 cols, 8000ish rows
mutate(posterior_draw = 1:n()) %>%
gather(key = id, value = mean_copulation_duration, -posterior_draw) %>%
mutate(mean_copulation_duration = mean_copulation_duration / 60) %>% # if we want to express in secs then delete this
left_join(new_posterior_copulation_duration_2, by = "id") %>%
select(-id) %>%
as_tibble()
Table S12a: The mean copulation duration when a SD/+ male mated with a LHm female in the second mating role.
predictions_copulation_duration_2 %>%
mutate(`Variant carried` = recode(predictions_copulation_duration_2$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
select(`Variant carried`, everything(), -SD) %>%
pander(split.cell = 40, split.table = Inf, round = 1)
Variant carried | Estimated copulation duration (mins) | 2.5% | 97.5% |
---|---|---|---|
W1118 | 18.4 | 14.5 | 23.7 |
SD-Mad | 20.4 | 15.8 | 26.4 |
SD-72 | 24.6 | 19.1 | 31.6 |
SD-5 | 19.4 | 15.1 | 25.1 |
# get posterior samples
post_cd_2 <- posterior_samples(copulation_duration_survival_model_2) %>%
as_tibble()
# now find the differences between the reference level (w1118) and the remaining levels (the SD lines)
# the exp() function converts the posterior draws onto the response scale
post_diff_cd_2 <- post_cd_2 %>%
mutate(p_w1118 = exp(b_Intercept),
p_SD_MAD = exp(b_SDMAD + b_Intercept),
p_SD_72 = exp(b_SD72 + b_Intercept),
p_SD_5 = exp(b_SD5 + b_Intercept),
`SD-Mad` = p_SD_MAD / p_w1118,
`SD-72` = p_SD_72 / p_w1118,
`SD-5` = p_SD_5 / p_w1118) %>%
gather(key = `difference comparison`, value = proportion) %>%
filter(`difference comparison` == c("SD-Mad", "SD-72", "SD-5"))
Table S12b: the mean copulation duration for SD/+ males that mated with a LHm female in the second mating role, relative to the w1118 control males. A value of 0.5 means that males mated for half the time of w1118 males.
# Create a table summarising the proportions of offspring sired relative to w1118
post_diff_cd_2 %>%
group_by(`difference comparison`) %>%
summarise(`Mean copulation duration relative to w1118` = mean(proportion),
`2.5%` = quantile(proportion, probs = 0.025),
`97.5%` = quantile(proportion, probs = 0.975)) %>%
rename(`SD-variant` = `difference comparison`) %>%
pander(split.cell = 40, split.table = Inf, round = 3)
SD-variant | Mean copulation duration relative to w1118 | 2.5% | 97.5% |
---|---|---|---|
SD-5 | 1.063 | 0.795 | 1.392 |
SD-72 | 1.351 | 1.002 | 1.764 |
SD-Mad | 1.119 | 0.833 | 1.47 |
\(~\)
Creating Figure S7
\(~\)
Panel a
mean_copulation_duration_plot_2 <-
posterior_prediction_copulation_duration_2 %>%
mutate(SD = recode(posterior_prediction_copulation_duration_2$SD, "MAD" = "SD-Mad", "72" = "SD-72", "5" = "SD-5")) %>%
ggplot(aes(SD, mean_copulation_duration)) +
stat_halfeye(aes(fill = SD), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("W1118" = "#d3f2a3", "SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip(ylim = c(10, 35)) +
ylab("Estimated copulation duration in\nsecond mating role (minutes)") +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Panel b
copulation_duration_odds_2 <-
post_cd_2 %>%
mutate(`SD-Mad` = b_SDMAD,
`SD-72` = b_SD72,
`SD-5` = b_SD5) %>%
gather(key = parameter, value = odds) %>%
filter(parameter == c("SD-Mad", "SD-72", "SD-5")) %>%
as_tibble() %>%
mutate(parameter =factor(parameter, levels=c("SD-Mad", "SD-72", "SD-5"))) %>%
ggplot(aes(parameter, odds)) +
stat_halfeye(aes(fill = parameter), .width = c(0.66, 0.95)) + # width indicates the uncertainty intervals: here we have 66% and 95% intervals
scale_fill_manual(values = c("SD-Mad" = "#97e196", "SD-72" = "#6cc08b", "SD-5" = "#4c9b82")) +
coord_flip() +
geom_hline(yintercept = 0, linetype = 2) +
scale_y_continuous("Odds difference from w1118",
breaks = c(-0.25, 0, 0.25, 0.5)) +
xlab("Variant carried by male") +
theme_bw() +
theme(legend.position = "none",
panel.grid.minor = element_blank())
Combine panels into a single plot
mean_copulation_duration_plot_2 /
(copulation_duration_odds_2) + plot_annotation(tag_levels = "a")
Figure S7: mating duration between a SD/+ male and a LHm female, compared to w1118 control males, when the SD/+ (or control) male mated second. Panel a shows the estimated copulation duration for SD/+ and control males. Panel b shows effect sizes on the odds scale for the SD variants. Positive values indicate that SD/+ males mated for longer than w1118 control males. Black points indicate the estimated mean with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution.
\(~\)
mean_mating_success_plot + Mating_success_logodds +
mean_P1_plot + P1_logodds +
mean_P2_plot + P2_logodds +
mean_remating_plot + Remating_logodds +
plot_annotation(tag_levels = "a", tag_suffix = " ") +
plot_layout(widths = c(2, 1))
Figure 1: The effect of SD on male mating and fertilisation success. Black points indicate the estimated mean, with associated 66 and 95% uncertainty intervals, while coloured area shows the posterior distribution. Panels a, c, e and g show results on the response scale, while panels b, d, f, and h show the distribution of the log-odds differences between the SD variants and the control allele; 95% uncertainty intervals that do not overlap zero indicate a significant effect. Panel a shows the estimated percentage of males that successfully mated with a LHm female, when competing against a single Lbw male. Panel b shows effect sizes on the log-odds scale for the SD variants; positive values indicate that SD/+ males were more likely to mate than the +/+ control males. Panel c shows the estimated percentage of offspring sired by males carrying SD or the control allele, when these males mated first (P1). Panel d shows effect sizes on the log-odds scale for the SD variants; negative values indicate that SD/+ males had lower P1 values than the +/+ control males. Panel e shows the estimated percentage of offspring sired by males carrying SD or the control allele, when these males mated second (P2). Panel f shows effect sizes on the log-odds scale for the SD variants; negative values indicate that SD/+ males had lower P2 values than the +/+ control males. Panel g shows the estimated percentage of females remating with LHmUBI males in a three-hour period, that had mated with a SD/+ or control male four days earlier. Data was quite variable between blocks for this response, so we plot model estimates based upon Block 1 to aid visual clarity (differences between the SD variants and the control do not change). Panel h shows effect sizes on the log-odds scale for the SD variants; positive values indicate that the mates of SD/+ males were more likely to remate than the mates of +/+ control males, four days after the first mating.
\(~\)
sessionInfo()
R version 4.0.5 (2021-03-31)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 10.16
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
locale:
[1] en_AU.UTF-8/en_AU.UTF-8/en_AU.UTF-8/C/en_AU.UTF-8/en_AU.UTF-8
attached base packages:
[1] grid stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] DT_0.18 patchwork_1.1.1 rcartocolor_2.0.0 lubridate_1.7.10
[5] kableExtra_1.3.4 pander_0.6.3 tidybayes_2.3.1 brms_2.15.0
[9] Rcpp_1.0.6 reshape2_1.4.4 forcats_0.5.1 stringr_1.4.0
[13] dplyr_1.0.5 purrr_0.3.4 readr_1.4.0 tidyr_1.1.3
[17] tibble_3.1.1 ggplot2_3.3.3 tidyverse_1.3.1 png_0.1-7
[21] workflowr_1.6.2
loaded via a namespace (and not attached):
[1] readxl_1.3.1 backports_1.2.1 systemfonts_1.0.1
[4] plyr_1.8.6 igraph_1.2.6 splines_4.0.5
[7] svUnit_1.0.6 crosstalk_1.1.1 rstantools_2.1.1
[10] inline_0.3.17 digest_0.6.27 htmltools_0.5.1.1
[13] rsconnect_0.8.17 fansi_0.4.2 magrittr_2.0.1
[16] modelr_0.1.8 RcppParallel_5.1.2 matrixStats_0.58.0
[19] svglite_2.0.0 xts_0.12.1 prettyunits_1.1.1
[22] colorspace_2.0-0 rvest_1.0.0 ggdist_2.4.0
[25] haven_2.4.0 xfun_0.22 callr_3.7.0
[28] crayon_1.4.1 jsonlite_1.7.2 lme4_1.1-26
[31] zoo_1.8-9 glue_1.4.2 gtable_0.3.0
[34] emmeans_1.5.5-1 webshot_0.5.2 V8_3.4.0
[37] distributional_0.2.2 pkgbuild_1.2.0 rstan_2.21.2
[40] abind_1.4-5 scales_1.1.1 mvtnorm_1.1-1
[43] DBI_1.1.1 miniUI_0.1.1.1 viridisLite_0.4.0
[46] xtable_1.8-4 stats4_4.0.5 StanHeaders_2.21.0-7
[49] htmlwidgets_1.5.3 httr_1.4.2 threejs_0.3.3
[52] arrayhelpers_1.1-0 ellipsis_0.3.1 farver_2.1.0
[55] pkgconfig_2.0.3 loo_2.4.1 sass_0.3.1
[58] dbplyr_2.1.1 utf8_1.2.1 labeling_0.4.2
[61] tidyselect_1.1.0 rlang_0.4.10 later_1.1.0.1
[64] munsell_0.5.0 cellranger_1.1.0 tools_4.0.5
[67] cli_2.4.0 generics_0.1.0 broom_0.7.6
[70] ggridges_0.5.3 evaluate_0.14 fastmap_1.1.0
[73] yaml_2.2.1 processx_3.5.1 knitr_1.32
[76] fs_1.5.0 nlme_3.1-152 whisker_0.4
[79] mime_0.10 projpred_2.0.2 xml2_1.3.2
[82] compiler_4.0.5 bayesplot_1.8.0 shinythemes_1.2.0
[85] rstudioapi_0.13 gamm4_0.2-6 curl_4.3
[88] reprex_2.0.0 statmod_1.4.35 bslib_0.2.4
[91] stringi_1.5.3 highr_0.9 ps_1.6.0
[94] Brobdingnag_1.2-6 lattice_0.20-41 Matrix_1.3-2
[97] nloptr_1.2.2.2 markdown_1.1 shinyjs_2.0.0
[100] vctrs_0.3.7 pillar_1.6.0 lifecycle_1.0.0
[103] jquerylib_0.1.3 bridgesampling_1.1-2 estimability_1.3
[106] httpuv_1.5.5 R6_2.5.0 promises_1.2.0.1
[109] gridExtra_2.3 codetools_0.2-18 boot_1.3-27
[112] colourpicker_1.1.0 MASS_7.3-53.1 gtools_3.8.2
[115] assertthat_0.2.1 rprojroot_2.0.2 withr_2.4.2
[118] shinystan_2.5.0 mgcv_1.8-35 parallel_4.0.5
[121] hms_1.0.0 coda_0.19-4 minqa_1.2.4
[124] rmarkdown_2.7 git2r_0.28.0 shiny_1.6.0
[127] base64enc_0.1-3 dygraphs_1.1.1.6