library(pacman); p_load(ggplot2)
my_theme <- function(){
theme(
legend.background = element_rect(colour = "black", linewidth = 0.5),
legend.key.width = unit(0.5, "cm"),
legend.key.height = unit(0.4, "cm"),
panel.background = element_rect(fill = "white"),
plot.background = element_rect(fill = "white"),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_line(colour ="gray80",linewidth = 1,linetype="dashed"),
panel.grid.minor.y = element_blank(),
axis.text.x = element_text(colour ="black",size=rel(1.5),hjust=0.5),
axis.text.y = element_text(colour ="black",size=rel(1.5),vjust=0.5),
axis.title.x = element_text(colour ="black",size=rel(1.2)),
axis.title.y = element_text(colour ="black",size=rel(1.2)),
plot.margin=unit(c(1,0.5,0.5,0.5), "cm"),
plot.title=element_text(hjust=0.5,vjust=5,size=rel(1.5),face="bold"),
plot.caption=element_text(hjust=0,size=rel(1.2)),
axis.line.x=element_line(color="gray80",linewidth = 1,linetype="dashed"),
axis.line.y=element_line(color="gray80",linewidth = 1,linetype="dashed"),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank())}
# Function to calculate critical Qst/Fst values
calculate_critical_values <- function(n_d, alpha) {
# Critical value for the upper tail (Qst > Fst)
chi_square_upper = qchisq(1 - alpha / 2, df = n_d - 1)
delta_upper = chi_square_upper / (n_d - 1)
# Critical value for the lower tail (Qst < Fst)
chi_square_lower = qchisq(alpha / 2, df = n_d - 1)
delta_lower = chi_square_lower / (n_d - 1)
return(c(upper = delta_upper, lower = delta_lower))}
# Function to simulate power
simulate_power <- function(n_d, Fst_bar, Qst_Fst_ratio, num_simulations = 10000, alpha = 0.05) {
critical_values <- calculate_critical_values(n_d, alpha)
# Simulate Qst values under the alternative hypothesis
simulated_Qst = rchisq(num_simulations, df = n_d - 1) * (Fst_bar / (n_d - 1)) * Qst_Fst_ratio
# Calculate power
if (Qst_Fst_ratio > 1) {
# For Qst > Fst, test if significantly higher
power = mean(simulated_Qst > critical_values['upper'] * Fst_bar)
} else {
# For Qst < Fst, test if significantly lower
power = mean(simulated_Qst < critical_values['lower'] * Fst_bar)
}
return(power)}
Walsh & Lynch (2018, pp. 455-457) described how tests of whether \(Q_{st}\) exceeds or is smaller than \(F_{st}\) have very low power without large numbers of demes or extreme levels of differentiation. Here’s an illustration in the vein of Whitlock & Guillaume (2009), using the parameters mentioned by Walsh & Lynch.
set.seed(123)
# Set Qst/Fst ratios to simulate
Qst_Fst_ratios = c(1.15, 1.30, 1.50,
1/1.15, 1/1.30, 1/1.50)
# Set parameters
n_demes_min = 2
n_demes_max = 50
Fst_bar = 0.1 # Average Fst
# Initialize data frame for results
power_results <- expand.grid(n_d = n_demes_min:n_demes_max, Qst_Fst_ratio = Qst_Fst_ratios)
power_results$power <- NA
power_results$critical_value_upper <- NA
power_results$critical_value_lower <- NA
for (i in 1:nrow(power_results)) {
n_d = power_results$n_d[i]
critical_values <- calculate_critical_values(n_d, 0.05)
power_results$power[i] <- simulate_power(n_d, Fst_bar, power_results$Qst_Fst_ratio[i])
power_results$critical_value_upper[i] <- critical_values['upper']
power_results$critical_value_lower[i] <- critical_values['lower']}
# Rename columns for clarity
names(power_results) <- c("Number of Demes", "Qst/Fst Ratio", "Power", "Critical Value (Qst > Fst)", "Critical Value (Qst < Fst)")
power_results
ggplot(power_results, aes(x = `Number of Demes`, y = Power, color = as.factor(`Qst/Fst Ratio`))) +
geom_line(linewidth = 1.5) +
labs(
title = "Power to Detect Selection Over Different Numbers of Demes",
x = "Number of Demes",
y = "Power",
color = "Qst/Fst Ratio") +
my_theme() +
scale_color_manual(values = c("orangered", "#1874cd", "#67238a", "#B33F62", "#F3C677", "darkblue"),
labels = function(x) round(as.numeric(x), 3)) +
theme(
legend.position = c(.10, .5)) +
ylim(0, .8)
When it comes to detecting directional or stabilizing selection, the demands of this test can be extreme. In realistic scenarios involving human populations, it is unlikely that this test will be sufficiently powered.
Walsh, B., & Lynch, M. (2018). Evolution and Selection of Quantitative Traits. Oxford University Press. https://doi.org/10.1093/oso/9780198830870.001.0001
Whitlock, M. C., & Guillaume, F. (2009). Testing for Spatially Divergent Selection: Comparing QST to FST. Genetics, 183(3), 1055–1063. https://doi.org/10.1534/genetics.108.099812
sessionInfo()
## R version 4.3.1 (2023-06-16 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=English_United States.utf8
## [2] LC_CTYPE=English_United States.utf8
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.utf8
##
## time zone: America/Chicago
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggplot2_3.4.4 pacman_0.5.1
##
## loaded via a namespace (and not attached):
## [1] vctrs_0.6.3 cli_3.6.1 knitr_1.45 rlang_1.1.1
## [5] xfun_0.39 highr_0.10 generics_0.1.3 jsonlite_1.8.7
## [9] labeling_0.4.3 glue_1.6.2 colorspace_2.1-0 htmltools_0.5.7
## [13] sass_0.4.7 fansi_1.0.4 scales_1.2.1 rmarkdown_2.25
## [17] grid_4.3.1 evaluate_0.23 munsell_0.5.0 jquerylib_0.1.4
## [21] tibble_3.2.1 fastmap_1.1.1 yaml_2.3.7 lifecycle_1.0.4
## [25] compiler_4.3.1 dplyr_1.1.2 pkgconfig_2.0.3 rstudioapi_0.15.0
## [29] farver_2.1.1 digest_0.6.33 R6_2.5.1 tidyselect_1.2.0
## [33] utf8_1.2.3 pillar_1.9.0 magrittr_2.0.3 bslib_0.6.0
## [37] withr_2.5.2 tools_4.3.1 gtable_0.3.4 cachem_1.0.8