OSCR

Multiple modes of selection underlie repeated and human-mediated adaptation in a formerly migratory fish.

Code ↔ Paper

8 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 8 matches
  1. [1] § Methods › Data processing ↔ scripts/wgs_pipeline_variables.sh, lines 1–45 · score 0.89 · bwa mem2, FastQC, American shad, pipeline, MarkDuplicates, alignment
  2. [2] § Methods › Data analysis › Calculating expression levels of protocadherins in American shad ↔ Rscripts/all_analyses_figure_table_creation.Rmd, lines 2710–2853 · score 0.78 · featureCounts, CPM, RNAseq, fastp, liver, ovary
  3. [3] § Methods › Data analysis › Calculating expression levels of protocadherins in American shad ↔ scripts/wgs_pipeline_variables.sh, lines 1–45 · score 0.66 · FastQC, American shad, MultiQC, raw, trim, alewife
  4. [4] § Methods › Data analysis › Testing for selection in candidate osmoregulatory genes ↔ Rscripts/all_analyses_figure_table_creation.Rmd, lines 2329–2369 · score 0.65 · GeneID, American shad annotation, osmoregulatory gene, LOC
  5. [5] § Methods › Data analysis › Calculating genetic diversity and measures of demographic history across populations ↔ Rscripts/popgen_bootstrap_permutation.R, lines 113–199 · score 0.59 · confidence intervals, population comparisons, bootstrapping
  6. [6] § Methods › Data analysis › Calculating genetic diversity and measures of demographic history across populations ↔ Rscripts/all_analyses_figure_table_creation.Rmd, lines 646–726 · score 0.55 · way ANOVA, FIS, FROH, demographic, LD, HSD
  7. [7] § Results › Osmoregulatory genes exhibit moderately repeatable signatures of selection across landlocked populations ↔ Rscripts/all_analyses_figure_table_creation.Rmd, lines 2620–2707 · score 0.51 · candidate osmoregulatory genes, UpSet, histograms, quantiles, LLR, outlier
  8. [8] § Results › Landlocked populations have lower genetic diversity than anadromous alewives ↔ Rscripts/all_analyses_figure_table_creation.Rmd, lines 370–417 · score 0.51 · Pat Quon, Long Amos, Ho, HSD, Tukey, heterozygosity

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R Markdown · 2,960 lines · 164 KB · MIT · 5 matches

  1. ---
  2. title: "manuscript"
  3. author: "RC"
  4. date: "`r Sys.Date()`"
  5. output: html_document
  6. ---
  7. ```{r setup, include=FALSE}
  8. knitr::opts_chunk$set(echo = TRUE)
  9. setwd("/Users/corcorri/Documents/Denver/Manuscript")
  10. sessionInfo()
  11. ```
  12. ``` {r Libraries}
  13. suppressPackageStartupMessages({
  14. library(tidyr)
  15. library(dplyr)
  16. library(ggplot2)
  17. library(cowplot)
  18. library(tidyverse)
  19. library(plotly)
  20. library(plyr)
  21. library(data.table)
  22. library(gprofiler2)
  23. library(stats)
  24. })
  25. ```
  26. ``` {r Commonly used Colors and Variables}
  27. ## Colors
  28. amos_col <- "#1D3203"
  29. bride_col <- "#1855F2"
  30. long_col <- "#496E12"
  31. pat_col <- "#A0E72F"
  32. quon_col <- "#74AB20"
  33. colors <- c(bride_col, amos_col, long_col, quon_col, pat_col)
  34. l_colors <- c(amos_col, long_col, quon_col, pat_col)
  35. chrom1 <- "grey75"
  36. chrom2 <- "grey25"
  37. two_lakes_col <- "#E5A4CB"
  38. three_lakes_col <- "#B53084"
  39. four_lakes_col <- "#45062E"
  40. ## Folders
  41. out_dir <- "./../Manuscript/r_figures/"
  42. ## Misc
  43. chroms <- data.frame(
  44. chr=c("NC_014690.1", "NC_055980.1", "NC_055979.1", "NC_055978.1", "NC_055977.1", "NC_055976.1", "NC_055975.1", "NC_055974.1", "NC_055973.1", "NC_055972.1", "NC_055971.1", "NC_055970.1", "NC_055969.1", "NC_055968.1", "NC_055967.1", "NC_055966.1", "NC_055965.1", "NC_055964.1", "NC_055963.1", "NC_055962.1", "NC_055961.1", "NC_055960.1", "NC_055959.1", "NC_055958.1", "NC_055957.1"),
  45. chr_num=seq(25,1))
  46. chrom_length <- read.table("./chrom_length.tsv", header=TRUE, sep="\t")
  47. all_lakes <- c("bride", "amos", "long", "quon", "pat")
  48. l_lakes <- c("amos", "long", "quon", "pat")
  49. ```
  50. ``` {r Figure 1 - Map, PCA, FST corrplot}
  51. #### Map ####
  52. library(sf)
  53. library(ggspatial)
  54. NE_outline <- map_data('state', region=c("Connecticut", "New York", "Massachusetts", "Rhode Island", "Pennsylvania", "New Jersey", "Vermont", "New Hampshire", "Maine", "Maryland", "Delaware")) %>%
  55. select(lon=long, lat, group, id=subregion)
  56. USLakes <- read_sf("USA_Detailed_Water_Bodies.shp")
  57. rivers <- subset(USLakes, FTYPE == "Stream/River")
  58. mylakes <- subset(USLakes, FTYPE == "Lake/Pond" & NAME == "Bride Lake" | NAME == "Pattagansett Lake" | NAME == "Amos Lake" | NAME == "Long Pond" | NAME == "Quonnipaug Lake" | NAME == "Rogers Lake")
  59. sf_use_s2(TRUE)
  60. Conn_shp <- st_read('statect_37800_0000_1995_s250_ctdep_1_shp_wgs84.shp')
  61. Conn_sf <- st_as_sf(Conn_shp)
  62. Conn_sf$geometry <- Conn_sf$geometry %>%
  63. s2::s2_rebuild() %>%
  64. sf::st_as_sfc() %>%
  65. sf::st_make_valid()
  66. rivers_sf <- st_as_sf(rivers)
  67. sf_use_s2(FALSE)
  68. rivers_sf <- st_make_valid(rivers_sf, NA_on_exception=TRUE)
  69. Conn_lakes <- st_intersection(mylakes, Conn_sf)
  70. Conn_rivers <- st_intersection(rivers_sf, Conn_sf)
  71. Conn_water <- st_union(Conn_lakes, Conn_rivers)
  72. ## Make the zoom into Connecticut sampling lakes
  73. Connecticut_zoom <- ggplot() +
  74. geom_sf(data=Conn_sf, fill="white") +
  75. geom_sf(data=Conn_water) +
  76. scale_x_continuous(limits=c(-72.8, -71.9)) +
  77. scale_y_continuous(limits=c(41.25, 41.55)) +
  78. theme_void() +
  79. theme(panel.border=element_rect(colour="black", fill=NA)) +
  80. annotation_scale() +
  81. annotation_north_arrow(location="tl", which_north="true", style=north_arrow_orienteering)
  82. ## Make the whole plot, including the NE states
  83. NE_map <- ggplot(data=NE_outline, aes(x=lon, y=lat, group=group)) +
  84. geom_polygon(fill="#e2ecdf", color="black", linewidth=0.3) +
  85. scale_x_continuous(limits=c(-80.54, -59)) +
  86. scale_y_continuous(limits=c(37.5, 47.46956)) +
  87. theme_void() +
  88. annotate("rect", xmin=-72.8, xmax=-71.9, ymin=41.25, ymax=41.55, color="black", linewidth=0.5, alpha=0) +
  89. annotate("segment", x=-72.8, y=41.25, xend=-80, yend=37.5, linewidth=0.5) +
  90. annotate("segment", x=-71.9, y=41.25, xend=-59, yend=37.5, linewidth=0.5) +
  91. theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
  92. axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())
  93. lakes_map <- plot_grid(NE_map, Connecticut_zoom, nrow=2, rel_heights=c(1,0.5))
  94. lakes_map
  95. ggsave(paste0(out_dir, "Sampling_map_scalebar.pdf"), lakes_map, width=10, height=8)
  96. detach("package:ggspatial", unload=TRUE)
  97. #### PCA ####
  98. library(pcadapt)
  99. for ( test in c("all", "LDthin") ) {
  100. if ( test == "all") {
  101. pcadapt_args <- NULL
  102. allsites_newfilters <- read.pcadapt("./pcadapt/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.recode.bed", type="bed")
  103. } else {
  104. pcadapt_args <- list(size=50, thr=0.2)
  105. allsites_newfilters <- read.pcadapt("./pcadapt/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.recode.bed", type="bed")
  106. }
  107. allsites_pcadapt <- pcadapt(allsites_newfilters, K=20, min.maf=0.01, LD.clumping=pcadapt_args)
  108. allsites_screeplot <- plot(allsites_pcadapt, option = "screeplot") #Viewing what the best K is (should be 4) - K=4 is the last before it flattens out
  109. # To see the exact values for variance explained
  110. ggplotly(allsites_screeplot)
  111. ggsave(paste0(out_dir, "allsites_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_screeplot_K20_MAF001_", test,".pdf"), plot = allsites_screeplot)
  112. ### START HERE IF K=20 SCREEPLOT ISN'T NEEDED: ###
  113. allsites_pcadapt <- pcadapt(allsites_newfilters, K=4, min.maf=0.01, LD.clumping=pcadapt_args) ## Bringing the K down to the minimum needed
  114. ## Get the variance explained values for PC 1-4
  115. for (pc in c(1,2,3,4)) {
  116. assign(paste0("pc", pc, "_value"), round(allsites_pcadapt$singular.values[[pc]]^2*100, digits=2)) ## Equation from observing points with ggplotly
  117. }
  118. # With integers - Necessary if you want to be able to label your individuals/populations
  119. poplist.names <- c(rep("Amos", 20), rep("Bride", 30), rep("Long", 20), rep("Pat", 19), rep("Quon", 18))
  120. indiv_list <- c("Amos_10", "Amos_11", "Amos_12", "Amos_13", "Amos_16", "Amos_17", "Amos_18", "Amos_19", "Amos_20", "Amos_22", "Amos_23", "Amos_25", "Amos_26", "Amos_3", "Amos_4", "Amos_5", "Amos_6", "Amos_7", "Amos_8", "Amos_9",
  121. "Bride_1", "Bride_10", "Bride_11", "Bride_12", "Bride_13", "Bride_14", "Bride_15", "Bride_16", "Bride_17", "Bride_18", "Bride_19", "Bride_2", "Bride_20", "Bride_21", "Bride_22", "Bride_23", "Bride_24", "Bride_25", "Bride_26", "Bride_27", "Bride_28", "Bride_29", "Bride_3", "Bride_30", "Bride_4", "Bride_5", "Bride_6", "Bride_7", "Bride_8", "Bride_9",
  122. "Long_11", "Long_12", "Long_14", "Long_15", "Long_16", "Long_18", "Long_19", "Long_2", "Long_20", "Long_22", "Long_23", "Long_25", "Long_26", "Long_27", "Long_28", "Long_3", "Long_30", "Long_7", "Long_8", "Long_9",
  123. "Pat_1", "Pat_10", "Pat_11", "Pat_12", "Pat_13", "Pat_14", "Pat_15", "Pat_16", "Pat_17", "Pat_18", "Pat_19", "Pat_2", "Pat_3", "Pat_4", "Pat_5", "Pat_6", "Pat_7", "Pat_8", "Pat_9",
  124. "Quon_10", "Quon_12", "Quon_13", "Quon_18", "Quon_19", "Quon_2", "Quon_20", "Quon_21", "Quon_25", "Quon_26", "Quon_27", "Quon_28", "Quon_3", "Quon_4", "Quon_5", "Quon_6", "Quon_8", "Quon_9")
  125. allsites_pcadapt_scores <- as.data.frame.matrix(allsites_pcadapt$scores) %>%
  126. cbind(., poplist.names, indiv_list) %>%
  127. rename_with(~c("pc1", "pc2", "pc3", "pc4", "lake", "indiv")) %>%
  128. mutate(lake = factor(lake, levels=c("Bride", "Amos", "Long", "Quon", "Pat")))
  129. ## Actually plotting the PCA
  130. xlim12 <- c(min(allsites_pcadapt_scores$pc1)-0.05, max(allsites_pcadapt_scores$pc1)+0.05)
  131. ylim12 <- c(min(allsites_pcadapt_scores$pc2)-0.05, max(allsites_pcadapt_scores$pc2)+0.05)
  132. xlim34 <- c(min(allsites_pcadapt_scores$pc3)-0.05, max(allsites_pcadapt_scores$pc3)+0.05)
  133. ylim34 <- c(min(allsites_pcadapt_scores$pc4)-0.05, max(allsites_pcadapt_scores$pc4)+0.05)
  134. allsites_ggplot_12 <- ggplot(allsites_pcadapt_scores, aes(x=pc1, y=pc2, color=lake, fill=lake)) +
  135. geom_hline(aes(yintercept=0), col="black") +
  136. geom_vline(aes(xintercept=0), col="black") +
  137. geom_point(size=2.2) +
  138. scale_color_manual(values=colors) +
  139. scale_fill_manual(values=colors) +
  140. scale_x_continuous(name=paste0("PC1 (", pc1_value, "%)"), limits=xlim12) +
  141. scale_y_continuous(name=paste0("PC2 (", pc2_value, "%)"), limits=ylim12) +
  142. theme_cowplot() +
  143. theme(legend.position="none", legend.text=element_text(size=15), legend.title=element_text(size=20), axis.text=element_text(size=10), axis.title=element_text(size=15))
  144. ggplotly(allsites_ggplot_12)
  145. allsites_ggplot_34 <- ggplot(allsites_pcadapt_scores, aes(x=pc3, y=pc4, color=lake, fill=lake)) +
  146. geom_hline(aes(yintercept=0), col="black") +
  147. geom_vline(aes(xintercept=0), col="black") +
  148. geom_point(size=2.2) +
  149. scale_color_manual(values=colors) +
  150. scale_fill_manual(values=colors) +
  151. scale_x_continuous(name=paste0("PC3 (", pc3_value, "%)"), limits=xlim34) +
  152. scale_y_continuous(name=paste0("PC4 (", pc4_value, "%)"), limits=ylim34) +
  153. theme_cowplot() +
  154. theme(legend.text=element_text(size=15), legend.title=element_text(size=20), axis.text=element_text(size=10), axis.title=element_text(size=15))
  155. allsites_grid <- plot_grid(allsites_ggplot_12, allsites_ggplot_34, nrow=1, align="h", rel_widths=c(1, 1.3))
  156. allsites_grid
  157. ggsave(paste0(out_dir, "allsites_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_ggplot_pcas_MAF001_", test, "50_02.pdf"), allsites_grid, width=11, height=5)
  158. if ( test == "all" ) {
  159. allsites_ggplot_12_zoom <- ggplot(allsites_pcadapt_scores, aes(x=pc1, y=pc2, color=lake, fill=lake)) +
  160. geom_hline(aes(yintercept=0), col="black") +
  161. geom_vline(aes(xintercept=0), col="black") +
  162. geom_point(size=2.2) +
  163. scale_color_manual(values=colors) +
  164. scale_fill_manual(values=colors) +
  165. theme_cowplot() +
  166. scale_x_continuous(name="PC1", limits=c(0.05,0.095)) +
  167. scale_y_continuous(name="PC2", limits=c(-0.05,0.025)) +
  168. theme(legend.position="none", legend.text=element_text(size=15), legend.title=element_text(size=20),
  169. axis.text=element_text(size=10), axis.title=element_text(size=15))
  170. allsites_ggplot_12_zoom
  171. #ggsave(paste0(out_dir, "allsites_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_ggplot_pca12_zoom_MAF001_", test, ".pdf"), allsites_ggplot_12_zoom, width=5, height=4)
  172. }
  173. }
  174. detach("package:pcadapt", unload=TRUE)
  175. #### FST Corrplot ####
  176. library(corrplot)
  177. wc_fst <- read.table("./popgen_stats/fst/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_wc.fst.summary", header=FALSE)
  178. wc_reordered <- data.frame(1:5, 1:5, 1:5, 1:5, 1:5, row.names=all_lakes)
  179. colnames(wc_reordered) <- all_lakes
  180. for (rowlake in all_lakes) {
  181. for (collake in all_lakes) {
  182. if ( rowlake != collake ) {
  183. wc_reordered[rowlake, collake] <- subset(wc_fst, (V1 %in% rowlake & V2 %in% collake) | (V2 %in% rowlake & V1 %in% collake))$V3
  184. } else { wc_reordered[rowlake, collake] <- NA }
  185. }
  186. }
  187. wc_reordered <- as.matrix(wc_reordered)
  188. col <- colorRampPalette(c("#BCC7C8", "#6E8587", "#2E3738"))(10)
  189. pdf(paste0(out_dir, "plink_pairwise_fst_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.pdf"), height=6, width=6)
  190. fst_corrplot <- corrplot(wc_reordered, method="circle", is.corr=FALSE, type="lower", col=col, col.lim=c(0.15, 0.45), tl.col="black", tl.srt=0, cl.pos="r", addCoef.col = 'black')
  191. fst_corrplot
  192. dev.off()
  193. write.table(wc_reordered, paste0(out_dir, "plink_fst_table_s1.txt"), quote=FALSE, row.names=TRUE, col.names=TRUE, sep="\t")
  194. detach("package:corrplot", unload=TRUE)
  195. ```
  196. ``` {r Figure 2 & Table S2 - Population Demography}
  197. ## Table S1 set-up
  198. table_s1 <- data.frame("fst"=rep(NA,6), "pi"=rep(NA,6), "td"=rep(NA,6), "ho"=rep(NA,6), "he"=rep(NA,6), "he.ho"=rep(NA,6), "roh_length"=rep(NA,6), "froh"=rep(NA,6), "fis"=rep(NA,6), row.names=c(all_lakes, "anova"))
  199. #### $\pi$ and FST ####
  200. for ( test in c("pi", "fst")) local({
  201. if ( test %in% "pi" ) {
  202. col_name <- "avg_pi"
  203. names_from <- "pop"
  204. lakes_loop <- all_lakes
  205. } else if ( test %in% "fst") {
  206. col_name <- "avg_wc_fst"
  207. names_from <- c("pop1", "pop2")
  208. lakes_loop <- l_lakes
  209. }
  210. file_list <- list.files(path=paste0("/Users/corcorri/Documents/Denver/Manuscript/popgen_stats/pixy_files/", test), pattern="\\.txt$", full.names=TRUE)
  211. data_list <- lapply(file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  212. combined_data <- do.call(rbind, data_list)
  213. assign(paste("all", test, "long", sep="_"), combined_data, pos=1)
  214. data_pivot <- pivot_wider(combined_data, id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=all_of(names_from), values_from=col_name)
  215. colnames(data_pivot) <- tolower(colnames(data_pivot))
  216. if ( test %in% "fst" ) {
  217. data_pivot <- cbind(data_pivot[,1:3], data_pivot[,grepl("bride", colnames(data_pivot))])
  218. }
  219. for ( lake in lakes_loop ) {
  220. test_df <- data_pivot[,c("chromosome", "window_pos_1", "window_pos_2", colnames(data_pivot[which(grepl(lake, colnames(data_pivot)))]))] %>%
  221. mutate(window_pos_1 = as.numeric(window_pos_1),
  222. window_pos_2 = as.numeric(window_pos_2)) %>%
  223. rename_with(~"avg_value", colnames(.)[4]) %>%
  224. merge(., chroms, by.x="chromosome", by.y="chr") %>%
  225. mutate(chr_num = as.factor(chr_num)) %>%
  226. filter(!is.na(avg_value))
  227. ## Running calculations to fill Table S2
  228. table_s1[lake,test] <- signif(mean(test_df$avg_value), 3)
  229. assign("table_s1", table_s1, pos=1)
  230. }
  231. ## ANOVA
  232. data_pivot_simplified <- data_pivot %>%
  233. select(-c("chromosome", "window_pos_1", "window_pos_2")) %>%
  234. rename_with(~lakes_loop)
  235. data_stacked <- na.omit(stack(data_pivot_simplified))
  236. data_anova <- anova(lm(values ~ ind, data_stacked))
  237. print(TukeyHSD(aov(values ~ ind, data_stacked)), pos=1)
  238. ## Pi diff lwr upr p adj
  239. # amos-bride 1.696170e-05 1.074367e-05 2.317973e-05 0.0000000 ***
  240. # long-bride -9.588048e-06 -1.580608e-05 -3.370017e-06 0.0002515 ***
  241. # quon-bride 7.044534e-05 6.422730e-05 7.666337e-05 0.0000000 ***
  242. # pat-bride -5.048918e-05 -5.670721e-05 -4.427115e-05 0.0000000 ***
  243. # long-amos -2.654975e-05 -3.276778e-05 -2.033172e-05 0.0000000 ***
  244. # quon-amos 5.348364e-05 4.726561e-05 5.970167e-05 0.0000000 ***
  245. # pat-amos -6.745088e-05 -7.366891e-05 -6.123285e-05 0.0000000 ***
  246. # quon-long 8.003338e-05 7.381535e-05 8.625141e-05 0.0000000 ***
  247. # pat-long -4.090113e-05 -4.711917e-05 -3.468310e-05 0.0000000 ***
  248. # pat-quon -1.209345e-04 -1.271525e-04 -1.147165e-04 0.0000000 ***
  249. ## FST diff lwr upr p adj
  250. # long-amos -0.004533741 -0.008195943 -0.0008715394 0.0080168 ***
  251. # quon-amos -0.067216405 -0.070878661 -0.0635541501 0.0000000 ***
  252. # pat-amos -0.051682031 -0.055343912 -0.0480201495 0.0000000 ***
  253. # quon-long -0.062682664 -0.066344652 -0.0590206761 0.0000000 ***
  254. # pat-long -0.047148290 -0.050809904 -0.0434866755 0.0000000 ***
  255. # pat-quon 0.015534375 0.011872707 0.0191960421 0.0000000 ***
  256. # assign("table_s1", table_s1, pos=1)
  257. })
  258. ## Graphing windowed pi results:
  259. pi_boxplot <- all_pi_long %>%
  260. mutate(pop = factor(tolower(pop), levels=all_lakes)) %>%
  261. filter(!is.na(avg_pi)) %>%
  262. ggplot(aes(x=pop, y=avg_pi, color=pop, fill=pop)) +
  263. geom_boxplot(alpha=0.5) +
  264. geom_violin() +
  265. scale_y_continuous(limits=c(0,0.00075)) +
  266. scale_color_manual(values=colors) +
  267. scale_fill_manual(values=colors) +
  268. theme_cowplot()
  269. pi_boxplot
  270. #### Tajima's D ####
  271. for ( lake in all_lakes ) local({
  272. lake <- lake
  273. example <- read.table(paste("./popgen_stats/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1", lake, "maxMissing75_HWE1e-10_50kb.Tajima.D", sep="_"), header=TRUE, sep="\t") %>%
  274. mutate(pop = lake) %>%
  275. merge(., chroms, by.x="CHROM", by.y="chr")
  276. assign(value=example, x=paste(lake, "td", sep="_"), pos=1)
  277. table_s1[lake, "td"] <- signif(mean(example$TajimaD, na.rm=TRUE), 3)
  278. assign("table_s1", table_s1, pos=1)
  279. })
  280. ## Graphing windowed td results:
  281. td_boxplot <- rbind(bride_td, amos_td, long_td, quon_td, pat_td) %>%
  282. mutate(pop = factor(tolower(pop), levels=all_lakes)) %>%
  283. filter(!is.na(TajimaD)) %>%
  284. ggplot(aes(x=pop, y=TajimaD, color=pop, fill=pop)) +
  285. geom_boxplot(alpha=0.5) +
  286. geom_violin(alpha=0.7) +
  287. scale_y_continuous(limits=c(-3, 4.5)) +
  288. scale_color_manual(values=colors) +
  289. scale_fill_manual(values=colors) +
  290. theme_cowplot()
  291. td_boxplot
  292. ## ANOVA
  293. anova(lm(TajimaD ~ pop, rbind(amos_td[,4:5], bride_td[,4:5], long_td[,4:5], pat_td[,4:5], quon_td[,4:5])))
  294. TukeyHSD(aov(TajimaD ~ pop, rbind(amos_td[,4:5], bride_td[,4:5], long_td[,4:5], pat_td[,4:5], quon_td[,4:5])))
  295. # diff lwr upr p adj
  296. # bride-amos -0.926297912 -0.96105561 -0.89154021 0.0000000 ***
  297. # long-amos -0.363778676 -0.39854810 -0.32900925 0.0000000 ***
  298. # pat-amos -0.044907620 -0.07968683 -0.01012841 0.0039173 **
  299. # quon-amos -0.367647344 -0.40242901 -0.33286568 0.0000000 ***
  300. # long-bride 0.562519235 0.52811800 0.59692047 0.0000000 ***
  301. # pat-bride 0.881390292 0.84697916 0.91580142 0.0000000 ***
  302. # quon-bride 0.558650568 0.52423696 0.59306417 0.0000000 ***
  303. # pat-long 0.318871056 0.28444809 0.35329403 0.0000000 ***
  304. # quon-long -0.003868668 -0.03829411 0.03055678 0.9980850
  305. # quon-pat -0.322739724 -0.35717506 -0.28830439 0.0000000 ***
  306. #### Heterozygosity and FIS ####
  307. het <- read.table("./popgen_stats/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.het", header=TRUE, sep="\t") %>%
  308. separate(INDV, c("lake", NA), sep="_", remove=FALSE) %>%
  309. mutate(lake = factor(tolower(lake), levels=all_lakes),
  310. o_het = N_SITES - O.HOM.,
  311. e_het = N_SITES - E.HOM.,
  312. o_het_frac = o_het / N_SITES,
  313. e_het_frac = e_het / N_SITES)
  314. for (lake_file in all_lakes ) local({
  315. het_temp <- subset(het, lake %in% lake_file)
  316. table_s1[lake_file,"ho"] <- paste0(signif(mean(het_temp$o_het_frac), 3), " (",
  317. signif(mean_se(het_temp$o_het_frac)$ymax - mean_se(het_temp$o_het_frac)$y, 2), ")")
  318. table_s1[lake_file,"he"] <- paste0(signif(mean(het_temp$e_het_frac), 3), " (",
  319. signif(mean_se(het_temp$e_het_frac)$ymax - mean_se(het_temp$e_het_frac)$y, 1), ")")
  320. # Does each lake significantly differ between expected and observed heterozygosity?
  321. ttest <- t.test(x=het_temp$e_het_frac, y=het_temp$o_het_frac, alternative="greater", paired=TRUE)
  322. table_s1[lake_file,"he.ho"] <- paste0("t_", ttest$parameter, "=", signif(ttest$statistic, 3), ", p",
  323. ifelse(ttest$p.value==0, "<2.2e-16", paste0("=", signif(ttest$p.value, 3))))
  324. ## FIS
  325. table_s1[lake_file,"fis"] <- paste0(signif(mean(het_temp$F, na.rm=TRUE), 3), " (",
  326. signif(mean_se(het_temp$F)$ymax - mean_se(het_temp$F)$y, 3), ")")
  327. assign("table_s1", table_s1, pos=1)
  328. })
  329. ## Within observed or expected heterozygosity, are the lakes significantly different from each other?
  330. for (type in c("o", "e")) local({
  331. h_anova <- anova(lm(get(paste(type, "het_frac", sep="_")) ~ lake, het))
  332. table_s1["anova",paste0("h", type)] <- paste0("F_(", paste(h_anova$Df[1], h_anova$Df[2], sep=", "), ")=", signif(h_anova$`F value`[1], 3), ", p",
  333. ifelse(h_anova$`Pr(>F)`[1]==0, "<2.2e-16", paste0("=", signif(p.adjust(h_anova$`Pr(>F)`, method="fdr"), 3))))
  334. print(TukeyHSD(aov(get(paste(type, "het_frac", sep="_")) ~ lake, het)), pos=1)
  335. assign("table_s1", table_s1, pos=1)
  336. })
  337. # OBS diff lwr upr p adj
  338. # amos-bride -0.120311184 -0.131291469 -0.10933090 0.0000000 ***
  339. # long-bride -0.114049142 -0.125029427 -0.10306886 0.0000000 ***
  340. # quon-bride -0.088157390 -0.099497779 -0.07681700 0.0000000 ***
  341. # pat-bride -0.080782385 -0.091934695 -0.06963008 0.0000000 ***
  342. # long-amos 0.006262042 -0.005766257 0.01829034 0.5997379
  343. # quon-amos 0.032153795 0.019795892 0.04451170 0.0000000 ***
  344. # pat-amos 0.039528800 0.027343261 0.05171434 0.0000000 ***
  345. # quon-long 0.025891752 0.013533850 0.03824965 0.0000007 ***
  346. # pat-long 0.033266757 0.021081219 0.04545230 0.0000000 ***
  347. # pat-quon 0.007375005 -0.005135995 0.01988600 0.4774741
  348. # EXP diff lwr upr p adj
  349. # amos-bride -9.465430e-04 -0.003035193 0.0011421067 0.7169636
  350. # long-bride -2.387605e-03 -0.004476255 -0.0002989556 0.0166139 *
  351. # quon-bride -1.811118e-03 -0.003968267 0.0003460298 0.1433878
  352. # pat-bride -2.393695e-03 -0.004515067 -0.0002723227 0.0187349 *
  353. # long-amos -1.441062e-03 -0.003729063 0.0008469388 0.4089565
  354. # quon-amos -8.645753e-04 -0.003215273 0.0014861224 0.8449425
  355. # pat-amos -1.447152e-03 -0.003765063 0.0008707591 0.4180666
  356. # quon-long 5.764870e-04 -0.001774211 0.0029271847 0.9601290
  357. # pat-long -6.089444e-06 -0.002324000 0.0023118215 1.0000000
  358. # pat-quon -5.825765e-04 -0.002962396 0.0017972432 0.9603850
  359. ## Windowed Heterozygosity - Histogram
  360. for (lake in c("amos", "bride", "long", "pat", "quon")) local({
  361. lake <- lake
  362. ## First, make the windowed heterozygosity files if they do not already exist using the windows in the pixy output
  363. if ( !file.exists(paste0("./popgen_stats/", lake, "_het_50kb_windows.txt")) ) {
  364. het_example <- read.table(paste0("./popgen_stats/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_", lake, "_subset.hwe", lake, "_subset.hwe"), header=TRUE, sep="\t") %>%
  365. rename(~c("chr", "pos", "obs", "exp")) %>%
  366. separate(OBS.HOM1.HET.HOM2., into=c("OHOM1", "OHET", "OHOM2"), sep=c("/"),
  367. E.HOM1.HET.HOM2., into=c("EHOM1", "EHET", "EHOM2"), sep=c("/")) %>%
  368. mutate(OHOM1 = as.numeric(OHOM1),
  369. OHOM2 = as.numeric(OHOM2),
  370. OHET = as.numeric(OHET),
  371. EHOM1 = as.numeric(EHOM1),
  372. EHOM2 = as.numeric(EHOM2),
  373. EHET = as.numeric(EHET))
  374. ## There are some rows with NA's in the expected columns (any site where no individual in that pop had a retained genotype) so I'm going to remove any rows with them
  375. het_example <- subset(het_example, !is.na(EHOM1) & !is.na(EHOM2) & !is.na(EHET))
  376. ## Calculate fraction heterozygous
  377. print(paste(lake, "Fraction Expected Heterozygous Loci:", sum(het_example$EHET) / sum(sum(het_example$EHOM1), sum(het_example$EHOM2), sum(het_example$EHET))))
  378. print(paste(lake, "Fraction Observed Heterozygous Loci:", sum(het_example$OHET) / sum(sum(het_example$OHOM1), sum(het_example$OHOM2), sum(het_example$OHET))))
  379. ### Calculate fraction heterozygous in 50kb windows ###
  380. het_windows <- c()
  381. for (i in 25:2) {
  382. chr <- chroms$chr[i]
  383. het_frame <- read.table(paste0("./pixy_files/pixy_dxy_50000_", chr, ".txt"), header=TRUE, sep="\t") %>%
  384. pivot_wider(id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=c("pop1", "pop2"), values_from="avg_dxy")[,c(1:3)] %>%
  385. mutate(EHET = 0, OHET = 0, total = 0)
  386. het_windows <- rbind(het_windows, het_frame)
  387. }
  388. het_windows$row_num <- row_number(het_windows)
  389. ## Adding the observed and expected heterozygosity up for each 50kb window to make a histogram
  390. het_example <- subset(het_example, CHR != "NC_014690.1")
  391. for (i in 1:nrow(het_example)) {
  392. if ( i%%100 == 0 ) { print(paste(lake, "row", i, "out of", nrow(het_example))) }
  393. het_window <- subset(het_windows, chromosome %in% het_example$CHR[i] &
  394. window_pos_1 <= het_example$POS[i] &
  395. window_pos_2 >= het_example$POS[i]) %>% ## Pull the window that the row of het_example would fall into
  396. mutate(EHET = sum(EHET, het_example$EHET[i]), ## Add the expected # of het loci in row [i] to the running window sum
  397. OHET = sum(OHET, het_example$OHET[i]), ## Add the observed # of het loci in row [i] to the running window sum
  398. total = sum(total, het_example$n_ind[i])) ## Add the total # of reads at loci [i] to the running window sum
  399. het_windows[het_windows$row_num == het_window$row_num, ] <- het_window ## Replace the window row with the edited window pulled before
  400. }
  401. het_windows <- het_windows %>%
  402. rowwise() %>%
  403. mutate(OHET_frac = (OHET / total),
  404. EHET_frac = (EHET / total))
  405. write.table(x=het_windows, file=paste("./popgen_stats/", lake, "het_50kb_windows.txt", sep="_"), row.names=FALSE, col.names=TRUE, sep="\t", quote=FALSE)
  406. }
  407. ## Reading in the het files (takes ~9 hours to create all 5 lakes' files from scratch as done above)
  408. het_example <- read.table(paste0("./popgen_stats/", lake, "_het_50kb_windows.txt"), header=TRUE, sep="\t") %>%
  409. merge(., chroms, by.x="chromosome", by.y="chr") %>%
  410. mutate(chr_num = as.factor(chr_num),
  411. lake = lake)
  412. assign(x=paste(lake, "het_windows", sep="_"), value=het_example, pos=1)
  413. print(paste0("Number of windows with 0 heterozygous SNPs in ", lake, ": ", nrow(subset(het_example, OHET==0))), pos=1)
  414. })
  415. ## Graphing windowed H_o results:
  416. ho_boxplot <- rbind(bride_het_windows, amos_het_windows, long_het_windows, pat_het_windows, quon_het_windows) %>%
  417. mutate(lake = factor(tolower(lake), levels=all_lakes)) %>%
  418. filter(!is.na(OHET_frac)) %>%
  419. ggplot(aes(x=lake, y=OHET_frac, color=lake, fill=lake)) +
  420. geom_boxplot(alpha=0.5) +
  421. geom_violin() +
  422. scale_y_continuous(limits=c(0, 0.55)) +
  423. scale_color_manual(values=colors) +
  424. scale_fill_manual(values=colors) +
  425. theme_cowplot()
  426. ho_boxplot
  427. ### FIS Boxplots
  428. fis_plot <- ggplot(het, aes(x=lake, y=F, color=lake)) +
  429. geom_hline(yintercept=0, color="grey50") +
  430. geom_violin(aes(fill=lake), alpha=0.2, width=0.8) +
  431. geom_boxplot(aes(group=lake), width=0.2) +
  432. geom_jitter(width=0.2, size=1) +
  433. scale_color_manual(values=colors, aesthetics=c("fill","color")) +
  434. scale_y_continuous(name="FIS", limits=c(-0.3, 0.5)) +
  435. theme_cowplot() +
  436. theme(legend.position="none", axis.title.x=element_blank())
  437. fis_plot
  438. fis_anova <- anova(lm(F ~ lake, data=het))
  439. table_s1["anova", "fis"] <- paste0("F_(", paste(fis_anova$Df[1], fis_anova$Df[2], sep=", "), ")=", signif(fis_anova$`F value`[1], 3), ", p",
  440. ifelse(fis_anova$`Pr(>F)`[1]==0, "<2.2e-16", paste0("=", signif(fis_anova$`Pr(>F)`[1], 3))))
  441. TukeyHSD(aov(F ~ lake, data=het))
  442. # diff lwr upr p adj
  443. # amos-bride 0.46908483 0.43001035 0.50815932 0.0000000 ***
  444. # long-bride 0.43986733 0.40079285 0.47894182 0.0000000 ***
  445. # quon-bride 0.33954800 0.29919204 0.37990396 0.0000000 ***
  446. # pat-bride 0.30832554 0.26863889 0.34801220 0.0000000 ***
  447. # long-amos -0.02921750 -0.07202146 0.01358646 0.3262752
  448. # quon-amos -0.12953683 -0.17351372 -0.08555995 0.0000000 ***
  449. # pat-amos -0.16075929 -0.20412280 -0.11739578 0.0000000 ***
  450. # quon-long -0.10031933 -0.14429622 -0.05634245 0.0000001 ***
  451. # pat-long -0.13154179 -0.17490530 -0.08817828 0.0000000 ***
  452. # pat-quon -0.03122246 -0.07574415 0.01329924 0.2992979
  453. #### ROH and FROH ####
  454. total_genome <- sum(read.csv("GCF_018492685.1_fAloSap1.pri_genomic.fna.fai", sep="\t", header=FALSE)$V2)
  455. ## Plotting total # of bins for ROH bins per lake
  456. roh_bin <- data.frame("lake"=c(rep("bride",3), rep("amos",3), rep("long",3), rep("quon",3), rep("pat",3)),
  457. "bin"=rep(c("0.5-0.75", "0.75-1.0", ">1.0"), 5),
  458. "count"=rep(1, 15),
  459. "sum_length"=rep(1, 15))
  460. all_roh <- c()
  461. froh_cat <- c()
  462. bin_row <- 1
  463. for (lake in all_lakes) {
  464. ## plink ( FID IID PHE CHR SNP1 SNP2 POS1 POS2 KB NSNP DENSITY PHOM PHET)
  465. example_roh <- read.table(paste("./popgen_stats/ashad_replaced", lake, "plink_roh.hom", sep="_"), header=TRUE, sep="\t")[,-c(1:2,4,6:7)] %>%
  466. mutate(roh_lake = lake,
  467. roh_color = get(paste0(lake, "_col")),
  468. IID = tolower(IID))
  469. ## Combine lake-specific files for later use
  470. all_roh <- rbind(all_roh, example_roh)
  471. # Calculating lake stats for bins
  472. example_roh_five <- subset(example_roh, KB >= 500 & KB <= 750)
  473. example_roh_seven <- subset(example_roh, KB >= 750 & KB <= 1000)
  474. example_roh_ten <- subset(example_roh, KB >= 1000)
  475. if (lake == "bride") { lake_indiv <- 30
  476. } else if (lake == "amos" || lake == "long") { lake_indiv <- 20
  477. } else if (lake == "pat") { lake_indiv <- 19
  478. } else if (lake == "quon") { lake_indiv <- 18 }
  479. for (bin in c("five", "seven", "ten")) {
  480. bin_example <- get(paste("example_roh", bin, sep="_"))
  481. roh_bin$count[bin_row] <- nrow(bin_example) / lake_indiv
  482. roh_bin$sum_length[bin_row] <- sum(bin_example$KB) / lake_indiv
  483. bin_row <- bin_row + 1
  484. }
  485. ## FROH ##
  486. lake_list <- data.frame("indiv"=tolower(read.csv(paste0(lake, "_n107_vcftools.txt"), header=FALSE)[[1]]))
  487. lake_froh <- data.frame("indiv"=rep("1", nrow(lake_list)),
  488. "froh"=rep(1, nrow(lake_list)),
  489. "count"=rep(1, nrow(lake_list)))
  490. row_num <- 1
  491. for (indiv in c(1:30)) {
  492. if (paste(lake, indiv, sep="_") %in% lake_list[[1]]) {
  493. lake_roh_indiv <- subset(example_roh, IID == paste(lake, indiv, sep="_"))
  494. lake_froh$indiv[row_num] <- paste(lake, indiv, sep="_")
  495. lake_froh$froh[row_num] <- (sum(lake_roh_indiv$KB * 1000) / total_genome)
  496. lake_froh$count[row_num] <- nrow(lake_roh_indiv)
  497. row_num <- row_num + 1
  498. }
  499. }
  500. #write.table(file=paste0(out_dir, lake, "_froh.tsv"), x=lake_froh, sep="\t", row.names=FALSE)
  501. assign(value=lake_froh, x=paste(lake, "froh", sep="_"))
  502. froh_cat <- rbind(froh_cat, lake_froh)
  503. }
  504. all_roh <- merge(all_roh, chroms, by.x="CHR", by.y="chr")
  505. froh_cat <- froh_cat %>%
  506. separate(indiv, c("froh_lake", "indiv"), "_") %>%
  507. mutate(froh_lake = factor(froh_lake, levels=all_lakes))
  508. ## Are ROH significantly different between lakes?
  509. roh_len_anova <- anova(lm(KB ~ roh_lake, all_roh))
  510. # Filter_redone Df Sum Sq Mean Sq F value Pr(>F)
  511. # roh_lake 4 223954 55989 2.292 0.0576 .
  512. # Residuals 1414 34546284 24432
  513. ## Normalize ROH by population size and check again for significance
  514. all_roh$KB_norm <- if_else(all_roh$roh_lake %in% "bride", all_roh$KB / 30,
  515. if_else(all_roh$roh_lake %in% "amos" | all_roh$roh_lake %in% "long", all_roh$KB / 20,
  516. if_else(all_roh$roh_lake %in% "pat", all_roh$KB / 19, all_roh$KB / 18)))
  517. ## Add in mean/se normalized ROH length
  518. for ( lake in all_lakes ) local({
  519. roh_subset <- subset(all_roh, roh_lake %in% lake)
  520. table_s1[lake, "roh_length"] <- paste0(signif(mean(roh_subset$KB_norm), 3), " (",
  521. signif(mean_se(roh_subset$KB_norm)$ymax - mean_se(roh_subset$KB_norm)$y, 3), ")")
  522. ## FROH > 0
  523. froh_subset <- subset(froh_cat, froh_lake %in% lake & froh != 0)
  524. table_s1[lake, "froh"] <- paste0(signif(mean(froh_subset$froh), 3), " (",
  525. signif(mean_se(froh_subset$froh)$ymax - mean_se(froh_subset$froh)$y, 3), ")")
  526. assign("table_s1", table_s1, pos=1)
  527. })
  528. ## ROH ANOVA
  529. roh_len_anova <- anova(lm(KB_norm ~ roh_lake, all_roh))
  530. table_s1["anova", "roh_length"] <- paste0("F_(", paste(roh_len_anova$Df[1], roh_len_anova$Df[2], sep=", "), ")=", signif(roh_len_anova$`F value`[1], 3), ", p",
  531. ifelse(roh_len_anova$`Pr(>F)`[1]==0, "<2.2e-16", paste0("=", signif(roh_len_anova$`Pr(>F)`[1], 3))))
  532. TukeyHSD(aov(KB_norm ~ roh_lake, all_roh))
  533. # diff lwr upr p adj
  534. # bride-amos -11.1473284 -13.9735745 -8.321082 0.0000000 ***
  535. # long-amos -0.6448182 -2.4014344 1.111798 0.8542420
  536. # pat-amos 2.5628361 0.8545010 4.271171 0.0004240 ***
  537. # quon-amos 3.9938848 1.9259041 6.061865 0.0000015 ***
  538. # long-bride 10.5025102 7.8101192 13.194901 0.0000000 ***
  539. # pat-bride 13.7101644 11.0490223 16.371307 0.0000000 ***
  540. # quon-bride 15.1412131 12.2360775 18.046349 0.0000000 ***
  541. # pat-long 3.2076543 1.7312699 4.684039 0.0000000 ***
  542. # quon-long 4.6387030 2.7577867 6.519619 0.0000000 ***
  543. # quon-pat 1.4310487 -0.4048583 3.266956 0.2082813
  544. ## F_ROH ANOVA
  545. anova(lm(froh ~ froh_lake, data=froh_cat))
  546. # Df Sum Sq Mean Sq F value Pr(>F)
  547. # froh_lake 4 0.0037003 0.00092507 5.5048 0.000471 ***
  548. # Residuals 102 0.0171407 0.00016805
  549. TukeyHSD(aov(froh ~ froh_lake, data=froh_cat))
  550. # diff lwr upr p adj
  551. # amos-bride 0.0070619775 -0.003330708 0.017454663 0.3308357
  552. # long-bride 0.0123619899 0.001969304 0.022754676 0.0112879 *
  553. # quon-bride 0.0062604492 -0.004473071 0.016993969 0.4883303
  554. # pat-bride 0.0164134155 0.005857910 0.026968921 0.0003467 ***
  555. # long-amos 0.0053000124 -0.006084604 0.016684629 0.6961678
  556. # quon-amos -0.0008015284 -0.012498110 0.010895054 0.9997022
  557. # pat-amos 0.0093514379 -0.002182004 0.020884880 0.1694309
  558. # quon-long -0.0061015408 -0.017798123 0.005595041 0.5978775
  559. # pat-long 0.0040514256 -0.007482016 0.015584867 0.8656213
  560. # pat-quon 0.0101529663 -0.001688520 0.021994453 0.1288607
  561. ## FROH > 0 ANOVA
  562. froh_anova <- anova(lm(froh ~ froh_lake, data=subset(froh_cat, froh!=0)))
  563. table_s1["anova", "froh"] <- paste0("F_(", paste(froh_anova$Df[1], froh_anova$Df[2], sep=", "), ")=", signif(froh_anova$`F value`[1], 3), ", p",
  564. ifelse(froh_anova$`Pr(>F)`[1]==0, "<2.2e-16", paste0("=", signif(froh_anova$`Pr(>F)`[1], 3))))
  565. TukeyHSD(aov(froh ~ froh_lake, data=subset(froh_cat, froh!=0)))
  566. # Filtering redone diff lwr upr p adj
  567. # amos-bride 0.022778983 0.007056918 0.038501048 0.0012017 **
  568. # long-bride 0.015127249 0.003173051 0.027081448 0.0062475 **
  569. # quon-bride 0.009520769 -0.003515279 0.022556817 0.2550180
  570. # pat-bride 0.020495444 0.008317184 0.032673703 0.0001212 ***
  571. # long-amos -0.007651734 -0.023976394 0.008672926 0.6831929
  572. # quon-amos -0.013258214 -0.030390938 0.003874509 0.2038181
  573. # pat-amos -0.002283540 -0.018772981 0.014205901 0.9950676
  574. # quon-long -0.005606480 -0.019363287 0.008150327 0.7831134
  575. # pat-long 0.005368194 -0.007578666 0.018315055 0.7722205
  576. # pat-quon 0.010974675 -0.002977274 0.024926623 0.1902868
  577. ## Plotting binned ROH
  578. roh_count_bins <- roh_bin %>%
  579. mutate(lake = factor(lake, levels=all_lakes),
  580. bin = factor(bin, levels=c("0.5-0.75", "0.75-1.0", ">1.0"))) %>%
  581. ggplot(aes(x=lake, y=count, fill=bin)) +
  582. geom_bar(position="dodge", stat="identity") +
  583. ylab("Normalized ROH Count") +
  584. theme_cowplot() +
  585. scale_fill_manual("Bins (Mb)", values=c("0.5-0.75"="#D3CED3", "0.75-1.0"="#8F8C8F", ">1.0"="#4A4A4A")) +
  586. theme(axis.title.x=element_blank())
  587. roh_count_bins
  588. ## Plotting FROH
  589. maxes <- c(by(froh_cat$froh, froh_cat$froh_lake, max))
  590. col_labs <- c(paste("n =", nrow(subset(bride_froh, froh!=0))),
  591. paste("n =", nrow(subset(amos_froh, froh!=0))),
  592. paste("n =", nrow(subset(long_froh, froh!=0))),
  593. paste("n =", nrow(subset(quon_froh, froh!=0))),
  594. paste("n =", nrow(subset(pat_froh, froh!=0))))
  595. froh_comp <- ggplot(froh_cat, aes(x=froh_lake, y=froh, color=froh_lake)) +
  596. geom_boxplot(data=subset(froh_cat, froh!=0), aes(x=froh_lake, y=froh, color=froh_lake, group=froh_lake), width=0.2) +
  597. geom_jitter(width=0.2) +
  598. geom_text(data=data.frame(), aes(x=names(maxes), y=maxes + 0.01, label=col_labs, color="black")) +
  599. scale_color_manual(values=c(colors, "black"), aesthetics=c("color", "fill")) +
  600. xlab("Lake") +
  601. scale_y_continuous(name="F_ROH", limits=c(0,0.075)) +
  602. theme_cowplot() +
  603. theme(legend.position="none", axis.title.x=element_blank())
  604. froh_comp
  605. #### Finalizing Figure 2 and Table S2 ####
  606. ## Table S1 column/row names
  607. colnames(table_s1) <- c("FST (with Bride)", "pi", "Tajima's D", "H_o", "H_e", "H_e < H_o (one-tailed paired t-test)", "Normalized ROH Length (Kb)", "F_ROH (for F_ROH > 0)", "F_IS")
  608. rownames(table_s1) <- c(all_lakes, "One-way ANOVA")
  609. write.table(table_s1, paste0(out_dir, "popgen_demography_stats_table_s1_test.txt"), quote=FALSE, row.names=TRUE, col.names=TRUE, sep="\t")
  610. ## Figure 2
  611. fig2_graphs <- plot_grid(pi_boxplot, roh_count_bins, ho_boxplot, froh_comp, td_boxplot, fis_plot, ncol=2, axis="b", align="v", rel_widths=c(1,0.8))
  612. fig2_graphs
  613. ggsave(paste0(out_dir, "figure_2_sans_ld_graphs_boxplots_test.pdf"), fig2_graphs, width=10, height=8)
  614. ```
  615. Rocha 2022 paper: https://github.com/joanocha/ngsSelection/blob/main/Ohana/llratios_windows.py
  616. ``` {r Figure 3 & Table S3 - Ohana Manhattan Plot & Shared Outliers}
  617. ohana_quantile <- 0.99
  618. ohana_quantile_name <- as.character((1-ohana_quantile)*100)
  619. metric <- "mean_lle_ratio" #cum_lle_ratio max_lle_ratio top_mean_lle_ratio
  620. #### Load in pairwise selection scan ####
  621. for ( lake in l_lakes ) local({
  622. ## Site-level log-likelihood ratios
  623. ohana_selscan <- read.table(paste("./ohana/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_bride", lake, "maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_MAF001_geno01_scan_lle-ratios.txt", sep="_"), header=TRUE, sep="\t")[,-1]
  624. ohana_map <- read.table(paste("./ohana/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_bride", lake, "maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_MAF001_geno01.map", sep="_"), header=FALSE, sep="\t")[,-c(2:3)]
  625. ## Merge the LLR result file & mapping files together
  626. ohana_snp_level <- cbind(ohana_selscan, ohana_map) %>%
  627. rename_with(~c("lle_ratio", "global_lle", "local_lle", "f_1", "f_2", "chr", "pos")) %>%
  628. merge(., chroms, by="chr") %>%
  629. mutate(chr_num = as.factor(chr_num),
  630. outlier = if_else(lle_ratio >= quantile(lle_ratio, ohana_quantile), 1, 0),
  631. zscore = (lle_ratio - mean(lle_ratio, na.rm=TRUE)) / sd(lle_ratio, na.rm=TRUE),
  632. pop = lake)
  633. assign(x=paste(lake, "ohana_snp", sep="_"), value=ohana_snp_level, pos=1)
  634. ## Windowed files - Load if already made, create if not
  635. windowed_file <- paste("./ohana/pairwise_selscan", lake, "redone_filtering_50kb_10kb_snpcount.txt", sep="_")
  636. if ( file.exists(windowed_file) ) {
  637. ohana_windows <- read.table(paste("./ohana/pairwise_selscan", lake, "redone_filtering_50kb_10kb_snpcount.txt", sep="_"), header=TRUE, sep="\t") %>%
  638. filter(!is.na(get(metric))) %>%
  639. mutate(chr_num = as.factor(chr_num),
  640. outlier = if_else(.[[metric]] >= quantile(.[[metric]], ohana_quantile, na.rm=TRUE), 1, 0))
  641. assign(x=paste(lake, "ohana_windows", sep="_"), value=ohana_windows, pos=1)
  642. } else { ### Recreating the windowed files if desired
  643. chrom_length <- read.table("./chrom_length.tsv", header=TRUE, sep="\t")
  644. ohana_pos <- subset(ohana_snp_level, chr != "NC_014690.1")
  645. num_windows <- ceiling(sum(chrom_length[,2]) / 10000) + 5
  646. ohana_windows <- data.frame("chr"=rep(0, num_windows), "window_pos_1"=rep(0, num_windows), "window_pos_2"=rep(0, num_windows),
  647. "mean_lle_ratio"=rep(0, num_windows), "cum_lle_ratio"=rep(0, num_windows), "num_snps"=rep(0, num_windows))
  648. ohana_windows_row <- 1
  649. for (chrom in 1:24) {
  650. print(paste("Running", lake, "chr", chrom))
  651. chr_subset <- subset(ohana_pos, chr %in% chrom_length[chrom,1]) %>%
  652. arrange(pos) %>%
  653. mutate(snp_count = 1:nrow(.))
  654. window_start <- 1
  655. window_end <- 50000
  656. slide <- 10000
  657. ## Create the windowed files with a variety of summary metrics if desired
  658. while ( window_start + 40000 <= chrom_length[chrom,2] ) {
  659. subset_tmp <- subset(chr_subset, pos >= window_start & pos <= window_end)
  660. ohana_windows$chr[ohana_windows_row] <- chrom_length[chrom,1]
  661. ohana_windows$window_pos_1[ohana_windows_row] <- window_start
  662. ohana_windows$window_pos_2[ohana_windows_row] <- window_end
  663. ohana_windows$mean_lle_ratio[ohana_windows_row] <- mean(subset_tmp$lle_ratio, na.rm=TRUE)
  664. ohana_windows$cum_lle_ratio[ohana_windows_row] <- sum(subset_tmp$lle_ratio)
  665. ohana_windows$num_snps[ohana_windows_row] <- nrow(subset_tmp)
  666. ohana_windows$top_mean_lle_ratio[ohana_windows_row] <- mean(subset(subset_tmp, lle_ratio >= quantile(subset_tmp$lle_ratio, 0.8))$lle_ratio)
  667. ohana_windows$max_lle_ratio[ohana_windows_row] <- ifelse(ohana_windows$num_snps[ohana_windows_row] != 0, max(subset_tmp$lle_ratio, na.rm=TRUE), NA)
  668. window_start <- window_start + slide
  669. if ( window_end + slide <= chrom_length[chrom,2] ) { window_end <- window_end + slide }
  670. else if ( window_end + slide > chrom_length[chrom,2] ) { window_end <- chrom_length[chrom,2] }
  671. ohana_windows_row <- ohana_windows_row + 1
  672. }
  673. }
  674. print(summary(ohana_windows$num_snps))
  675. ohana_windows <- subset(ohana_windows, !is.na(cum_lle_ratio)) %>%
  676. merge(., chroms, by="chr") %>%
  677. mutate(chr_num = as.factor(chr_num),
  678. outlier = ifelse(.[[metric]] >= quantile(.[[metric]], ohana_quantile, na.rm=TRUE), 1, 0))
  679. assign(x=paste(lake, "ohana_windows", sep="_"), value=ohana_windows, pos=1)
  680. }
  681. })
  682. #### Figure 3A - Shared Outlier Manhattan Plot ####
  683. all_ohana_windows <- merge(amos_ohana_windows, long_ohana_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE, suffixes=c("_amos", "_long")) %>%
  684. merge(., quon_ohana_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE) %>%
  685. merge(., pat_ohana_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE, suffixes=c( "_quon", "_pat")) %>%
  686. mutate(outlier_amos = ifelse(is.na(outlier_amos), 0, outlier_amos),
  687. outlier_long = ifelse(is.na(outlier_long), 0, outlier_long),
  688. outlier_quon = ifelse(is.na(outlier_quon), 0, outlier_quon),
  689. outlier_pat = ifelse(is.na(outlier_pat), 0, outlier_pat)) %>%
  690. mutate(total_outliers = rowSums(.[,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")]),
  691. outlier_lakes = NA)
  692. ## Add list of outlier pakes per window
  693. # In a subset of just "1 outlier" rows looking only at the 0/1 outlier columns, apply a function (gsub) to get the "name" of "which" column (2 instead of row (1)) has "metric" "%in%" 1
  694. for ( i in 1:nrow(all_ohana_windows) ) {
  695. if ( all_ohana_windows$total_outliers[i] == 1 ) {
  696. lake <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  697. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) ))) )
  698. all_ohana_windows$outlier_lakes[i] <- lake
  699. }
  700. else if ( all_ohana_windows$total_outliers[i] == 2 ) {
  701. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  702. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) )))[1] )
  703. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  704. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) )))[2] )
  705. all_ohana_windows$outlier_lakes[i] <- paste(lake1, lake2, sep="_")
  706. }
  707. else if ( all_ohana_windows$total_outliers[i] == 3 ) {
  708. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  709. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) )))[1] )
  710. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  711. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) )))[2] )
  712. lake3 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  713. apply(all_ohana_windows[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1)) )))[3] )
  714. all_ohana_windows$outlier_lakes[i] <- paste(lake1, lake2, lake3, sep="_")
  715. }
  716. else if ( all_ohana_windows$total_outliers[i] == 4 ) {
  717. all_ohana_windows$outlier_lakes[i] <- paste("amos", "long", "pat", "quon", sep="_")
  718. }
  719. }
  720. rm(lake, lake1, lake2, lake3, i)
  721. ## 4-way manhattan plotgrid
  722. for (lake in l_lakes ) local({
  723. ohana_windows_na_rm <- get(paste(lake, "ohana_windows", sep="_"), pos=1) %>%
  724. mutate(outlier = if_else(is.na(outlier), 0, outlier))
  725. ## Remove a majority of non-significant windows to decrease the file size of the resulting plots for easier editing
  726. subset_plot <- FALSE
  727. if ( subset_plot == TRUE ) {
  728. nonsig_count <- 1
  729. ohana_windows_subset <- ohana_windows_na_rm[,]
  730. for ( row_num in 1:nrow(ohana_windows_na_rm) ) {
  731. if ( ohana_windows_na_rm$outlier[row_num] == 0 & nonsig_count%%10 == 0 ) {
  732. ohana_windows_subset <- rbind(ohana_windows_subset, ohana_windows_na_rm[row_num,])
  733. nonsig_count <- nonsig_count + 1
  734. } else if ( nonsig_count%%10 != 0 ) { nonsig_count <- nonsig_count + 1 }
  735. }
  736. ohana_windows_na_rm <- ohana_windows_subset[-1,] %>%
  737. mutate(chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  738. }
  739. ## Plotting
  740. if ( metric %like% "cum") {
  741. ylim_max <- 850
  742. } else {
  743. ylim_max <- 20
  744. }
  745. lake <- lake
  746. ohana_selscan_plot <<- ggplot(subset(ohana_windows_na_rm, outlier %in% 0), aes(x=window_pos_1 + 25000, y=get(metric), col=chr_num)) +
  747. geom_point() +
  748. geom_hline(aes(yintercept=quantile(ohana_windows_na_rm[, metric], ohana_quantile, na.rm=TRUE), color=get(paste0(lake, "_col"))), linewidth=1) +
  749. geom_point(data=subset(all_ohana_windows, total_outliers %in% 1 & outlier_lakes %like% lake),
  750. aes(x=window_pos_1 + 25000, y=get(paste(metric, lake, sep="_")), col=get(paste0(lake, "_col")))) +
  751. xlab("Chromosome") +
  752. scale_y_continuous(name=lake, limits=c(0,ylim_max)) +
  753. scale_color_manual(values=c(rep(c(chrom1, chrom2), 12), get(paste0(lake, "_col")), two_lakes_col, three_lakes_col, four_lakes_col)) +
  754. facet_grid( ~ chr_num, scales = "free_x", switch = "x", space = "free_x") +
  755. theme_minimal() +
  756. theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
  757. panel.spacing = unit(0.05, "cm"),
  758. panel.grid = element_blank(),
  759. strip.background = element_blank(),
  760. strip.placement = "outside",
  761. legend.position = "none",
  762. axis.line.x = element_line(linewidth = 0.5, linetype = "solid", color = "black"))
  763. ## Remove the x-axis labels to condense the plots together
  764. if (lake != "pat") {
  765. ohana_selscan_plot <<- ohana_selscan_plot + theme(axis.title.x=element_blank(), axis.text.x=element_blank(),
  766. axis.ticks.x=element_blank(), strip.text.x = element_blank())
  767. }
  768. ## Adding on 2, 3, and 4-way points to the above plot
  769. ohana_selscan_plot2 <<- ohana_selscan_plot +
  770. geom_point(data=subset(all_ohana_windows, total_outliers == 2 & outlier_lakes %like% lake),
  771. aes(x=window_pos_1 + 25000, y=get(paste(metric, lake, sep="_")), col=two_lakes_col)) +
  772. geom_point(data=subset(all_ohana_windows, total_outliers == 3 & outlier_lakes %like% lake),
  773. aes(x=window_pos_1 + 25000, y=get(paste(metric, lake, sep="_")), col=three_lakes_col), shape=17, size=2) +
  774. geom_point(data=subset(all_ohana_windows, total_outliers == 4),
  775. aes(x=window_pos_1 + 25000, y=get(paste(metric, lake, sep="_")), col=four_lakes_col), shape=18, size=3)
  776. assign(x=paste(lake, "ohana_selscan_plot", sep="_"), value=ohana_selscan_plot2, pos=1)
  777. })
  778. ohana_selscan_plotgrid <- plot_grid(amos_ohana_selscan_plot, NULL, long_ohana_selscan_plot, NULL, quon_ohana_selscan_plot, NULL, pat_ohana_selscan_plot, nrow=7, rel_heights=c(1, -0.25, 1, -0.25, 1, -0.25, 1), align="hv", axis="b")
  779. ohana_selscan_plotgrid
  780. #### Figure 3B - Shared Outlier Windows ####
  781. all_ohana_outliers <- subset(all_ohana_windows, outlier_amos == 1 | outlier_long == 1 | outlier_quon == 1 | outlier_pat == 1) %>%
  782. rename_with(~l_lakes, c(outlier_amos, outlier_long, outlier_quon, outlier_pat))
  783. library(ComplexUpset)
  784. size = get_size_mode('exclusive_intersection')
  785. window_upset <- upset(all_ohana_outliers, c("pat","quon","long","amos"),
  786. base_annotations=list(
  787. 'Intersection size'=intersection_size(
  788. text_mapping=aes(label=!!size,
  789. color="black", y=(!!size + 25))
  790. ) +
  791. ylab("Mean LLR Outlier Windows") +
  792. ylim(0,800)
  793. ),
  794. intersections=list("amos", "long", "quon", "pat",
  795. c("amos", "long"), c("amos", "quon"), c("amos", "pat"),
  796. c("long", "quon"), c("long", "pat"), c("quon", "pat"),
  797. c("amos", "long", "quon"), c("amos", "long", "pat"), c("amos", "quon", "pat"), c("long", "quon", "pat"),
  798. c("amos", "long", "quon", "pat")),
  799. matrix=(
  800. intersection_matrix(
  801. outline_color=list(active="#909190", inactive="#EBEBEB"),
  802. geom=geom_point(size=3))
  803. ),
  804. queries=list(
  805. upset_query(set='amos', fill=amos_col),
  806. upset_query(set='long', fill=long_col),
  807. upset_query(set='quon', fill=quon_col),
  808. upset_query(set='pat', fill=pat_col),
  809. upset_query(intersect=c('amos'), fill=amos_col, color=amos_col, only_components=c('intersections_matrix', 'Intersection size')),
  810. upset_query(intersect=c('long'), fill=long_col, color=long_col, only_components=c('intersections_matrix', 'Intersection size')),
  811. upset_query(intersect=c('quon'), fill=quon_col, color=quon_col, only_components=c('intersections_matrix', 'Intersection size')),
  812. upset_query(intersect=c('pat'), fill=pat_col, color=pat_col, only_components=c('intersections_matrix', 'Intersection size')),
  813. upset_query(intersect=c('amos', 'long'), fill=two_lakes_col, color=two_lakes_col,
  814. only_components=c('intersections_matrix', 'Intersection size')),
  815. upset_query(intersect=c('amos', 'quon'), fill=two_lakes_col, color=two_lakes_col,
  816. only_components=c('intersections_matrix', 'Intersection size')),
  817. upset_query(intersect=c('amos', 'pat'), fill=two_lakes_col, color=two_lakes_col,
  818. only_components=c('intersections_matrix', 'Intersection size')),
  819. upset_query(intersect=c('long', 'quon'), fill=two_lakes_col, color=two_lakes_col,
  820. only_components=c('intersections_matrix', 'Intersection size')),
  821. upset_query(intersect=c('long', 'pat'), fill=two_lakes_col, color=two_lakes_col,
  822. only_components=c('intersections_matrix', 'Intersection size')),
  823. upset_query(intersect=c('quon', 'pat'), fill=two_lakes_col, color=two_lakes_col,
  824. only_components=c('intersections_matrix', 'Intersection size')),
  825. upset_query(intersect=c('amos', 'long', 'quon'), fill=three_lakes_col, color=three_lakes_col,
  826. only_components=c('intersections_matrix')), #, 'Intersection size')),
  827. upset_query(intersect=c('amos', 'long', 'pat'), fill=three_lakes_col, color=three_lakes_col,
  828. only_components=c('intersections_matrix')), #, 'Intersection size')),
  829. upset_query(intersect=c('amos', 'quon', 'pat'), fill=three_lakes_col, color=three_lakes_col,
  830. only_components=c('intersections_matrix')), #, 'Intersection size')),
  831. upset_query(intersect=c('long', 'quon', 'pat'), fill=three_lakes_col, color=three_lakes_col,
  832. only_components=c('intersections_matrix', 'Intersection size')),
  833. upset_query(intersect=c('amos', 'long', 'quon', 'pat'), fill=four_lakes_col, color=four_lakes_col,
  834. only_components=c('intersections_matrix'))#, 'Intersection size'))
  835. ),
  836. set_sizes=(
  837. upset_set_size() + theme_cowplot() + theme(axis.title.y=element_blank(), axis.text.y=element_blank(), strip.text.y=element_blank(), axis.ticks.y=element_blank(), axis.line.y=element_blank())
  838. ),
  839. themes=list('Intersection size'=list(theme_cowplot(), theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(), strip.text.x = element_blank())),
  840. 'intersections_matrix'=list(theme_cowplot(), theme(axis.text.x=element_blank(), strip.text.x = element_blank(), axis.title=element_blank(), axis.ticks=element_blank(), axis.line=element_blank()))
  841. ),
  842. name=NULL, keep_empty_groups=TRUE, width_ratio = 0.15, stripes=c('#F5F5F5', 'white'), sort_sets=FALSE, sort_intersections=FALSE
  843. )
  844. window_upset
  845. #### Combine Figre 3A and 3B ####
  846. ohana_manhattan_upset <- plot_grid(ohana_selscan_plotgrid, window_upset, nrow=2, rel_heights = c(1, 0.6))
  847. ohana_manhattan_upset
  848. ggsave(paste0(out_dir, "filtering_redone_pairwise_50kb_10kb_00", ohana_quantile_name, "_meanllr_windows_fig3.pdf"), ohana_manhattan_upset, width=12, height=10)
  849. #### Table S2 - Table of shared LLR outlier windows ####
  850. ## First load in the annotation file
  851. annotation_df <- read.delim("./GCF_018492685.1_fAloSap1.pri_genomic.gtf.gz", header = FALSE, sep = "\t", skip = 4,
  852. col.names=c("chr", "type", "type2", "start", "stop", "blank1", "strand", "blank2", "info")) %>%
  853. filter(type %in% "Gnomon" & type2 %in% "gene") %>%
  854. select(-c(blank1, blank2)) %>%
  855. separate_wider_delim(info, delim="; ", too_few="align_start",
  856. names=c("gene_name1", NA, "GeneID", "gbkey", "gene_name2", "gene_biotype", NA, NA)) %>%
  857. mutate(gene_name1 = unlist(strsplit(gene_name1, split="gene_id\ "))[c(seq(2,2*nrow(.), 2))],
  858. GeneID = unlist(strsplit(GeneID, split="db_xref GeneID:"))[c(seq(2,2*nrow(.), 2))],
  859. gbkey = unlist(strsplit(gbkey, split="gbkey\ "))[c(seq(2,2*nrow(.), 2))],
  860. gene_name2 = unlist(strsplit(gene_name2, split="gene\ "))[c(seq(2,2*nrow(.), 2))],
  861. gene_biotype = unlist(strsplit(gene_biotype, split="gene_biotype\ "))[c(seq(2,2*nrow(.), 2))]) %>%
  862. filter(gene_name1 %in% gene_name2) %>%
  863. select(-c(gene_name1, gbkey, gene_biotype)) %>%
  864. rename_with(~c("ID", "name"), c(GeneID, gene_name2))
  865. ## Now pull the genes in each peak
  866. n_peaks <- 100
  867. genes_all <- c()
  868. for (lake in l_lakes ) {
  869. highest_ohana_peaks_all <- c()
  870. for (outlier_count in c(2,3,4)) {
  871. ## Create an empty df for the new lake/outlier_count combo
  872. highest_ohana_peaks <- data.frame("peak_num"=rep(0, n_peaks), "chr"=rep(0, n_peaks), "window_pos_1"=rep(0, n_peaks), "window_pos_2"=rep(0, n_peaks), "chr_num"=rep(0, n_peaks), "n_windows"=rep(0, n_peaks), "lake"=rep(lake, n_peaks))
  873. ## Subset all outliers to only those matching the lake & outlier count
  874. ohana_outlier_subset <- subset(all_ohana_windows, total_outliers == outlier_count & get(paste("outlier", lake, sep="_")) == 1)
  875. if ( dim(ohana_outlier_subset)[1] == 0 ) {
  876. break
  877. }
  878. row_num <- 1
  879. peak_num <- 1
  880. for ( i in 1:nrow(ohana_outlier_subset) ) {
  881. current_peak <- ohana_outlier_subset[i,]
  882. ## Subset to any peaks on the same chromosome & within +/- 10Kb of the current peak
  883. ohana_peaks_subset <- subset(highest_ohana_peaks, chr == current_peak$chr &
  884. ( (window_pos_1 >= (current_peak$window_pos_1 - 10000) &
  885. window_pos_1 <= (current_peak$window_pos_1 + 10000)) |
  886. (window_pos_2 >= (current_peak$window_pos_2 - 10000) &
  887. window_pos_2 <= (current_peak$window_pos_2 + 10000)) ) )
  888. if ( dim(ohana_peaks_subset)[1] == 0 ) { #Are there peaks on this chromosome yet AND within 10Kb? NO -
  889. highest_ohana_peaks$peak_num[row_num] <- peak_num
  890. highest_ohana_peaks$chr[row_num] <- current_peak$chr
  891. highest_ohana_peaks$window_pos_1[row_num] <- current_peak$window_pos_1
  892. highest_ohana_peaks$window_pos_2[row_num] <- current_peak$window_pos_2
  893. highest_ohana_peaks$chr_num[row_num] <- current_peak$chr_num
  894. highest_ohana_peaks$n_windows[row_num] <- 1
  895. row_num <- row_num + 1
  896. peak_num <- peak_num + 1
  897. } else { #YES - Update highest_ohana_peaks with new peak minimum & maximum, count 1 more individual with that peak
  898. ohana_peaks_subset$window_pos_1 <- if_else(current_peak$window_pos_1 < min(ohana_peaks_subset$window_pos_1),
  899. current_peak$window_pos_1, min(ohana_peaks_subset$window_pos_1))
  900. ohana_peaks_subset$window_pos_2 <- if_else(current_peak$window_pos_2 > max(ohana_peaks_subset$window_pos_2),
  901. current_peak$window_pos_2, max(ohana_peaks_subset$window_pos_2))
  902. ohana_peaks_subset$n_windows <- sum(ohana_peaks_subset$n_windows) + 1
  903. highest_ohana_peaks[highest_ohana_peaks$peak_num == ohana_peaks_subset$peak_num, ] <- ohana_peaks_subset[1,]
  904. }
  905. }
  906. highest_ohana_peaks <- highest_ohana_peaks %>%
  907. filter(n_windows != 0) %>%
  908. mutate(reason = if_else(outlier_count == 2, "2_outlier", if_else(outlier_count == 3, "3_outlier", "4_outlier")))
  909. highest_ohana_peaks_all <- rbind(highest_ohana_peaks_all, highest_ohana_peaks)
  910. }
  911. ## Adding the names of overlapping genes
  912. genes <- c()
  913. for (i in 1:nrow(highest_ohana_peaks_all)) {
  914. # Either the gene 1) starts or 2) stops within the range, or 3) reaches across the whole range
  915. gene_subset <- subset(annotation_df, chr %in% highest_ohana_peaks_all$chr[i] & (
  916. ( start >= highest_ohana_peaks_all$window_pos_1[i] & start <= highest_ohana_peaks_all$window_pos_2[i] ) |
  917. ( stop >= highest_ohana_peaks_all$window_pos_1[i] & stop <= highest_ohana_peaks_all$window_pos_2[i] ) |
  918. ( start <= highest_ohana_peaks_all$window_pos_1[i] & stop >= highest_ohana_peaks_all$window_pos_2[i] )))
  919. if ( dim(gene_subset)[1] == 0 ) {
  920. highest_ohana_peaks_all$gene_overlap_names[i] <- NA
  921. } else {
  922. gene_names <- gene_subset %>%
  923. group_by(chr) %>%
  924. summarise(name = paste(name, collapse = ", "))
  925. highest_ohana_peaks_all$gene_overlap_names[i] <- gene_names[1]
  926. }
  927. if ( i == 1 ) { genes <- gene_subset }
  928. else { genes <- rbind(genes, gene_subset) }
  929. }
  930. assign(x=paste(lake, "ohana_peaks_concat", sep="_"), value=as.data.frame(highest_ohana_peaks_all))
  931. assign(paste(lake, "outlier_genes", sep="_"), genes)
  932. rm(current_peak, ohana_outlier_subset, ohana_peaks_subset, highest_ohana_peaks, genes, gene_subset, gene_names)
  933. }
  934. ## Combine each lake-specific peak-gene overlap into one file and overlap the peaks if shared between lakes
  935. all_ohana_peaks_raw <- rbind(amos_ohana_peaks_concat, long_ohana_peaks_concat, quon_ohana_peaks_concat, pat_ohana_peaks_concat)
  936. all_ohana_peaks_reduced <- c()
  937. for (i in 1:nrow(all_ohana_peaks_raw)) {
  938. all_ohana_peaks_tmp <- subset(all_ohana_peaks_raw, lake %in% all_ohana_peaks_raw$lake[i] & chr %in% all_ohana_peaks_raw$chr[i] &
  939. window_pos_1 %in% all_ohana_peaks_raw$window_pos_1[i] &
  940. window_pos_2 %in% all_ohana_peaks_raw$window_pos_2[i])
  941. if (dim(all_ohana_peaks_tmp)[1] == 1) {
  942. all_ohana_peaks_reduced <- rbind(all_ohana_peaks_reduced, all_ohana_peaks_tmp)
  943. } else {
  944. all_ohana_peaks_tmp$reason[1] <- all_ohana_peaks_tmp %>%
  945. summarise(reason = paste(reason, collapse = ","))
  946. all_ohana_peaks_reduced <- rbind(all_ohana_peaks_reduced, all_ohana_peaks_tmp[1,])
  947. }
  948. }
  949. all_ohana_peaks_reduced <- unique(all_ohana_peaks_reduced[,-1])
  950. all_ohana_peaks_reduced <- all_ohana_peaks_reduced[with(all_ohana_peaks_reduced, order(chr_num,window_pos_1)),]
  951. all_ohana_peaks <- all_ohana_peaks_reduced
  952. for ( i in 1:nrow(all_ohana_peaks_reduced) ) {
  953. all_ohana_peaks_tmp <- subset(all_ohana_peaks_reduced,
  954. (chr %in% all_ohana_peaks_reduced$chr[i]) &
  955. (window_pos_1 %in% all_ohana_peaks_reduced$window_pos_1[i]) &
  956. (window_pos_1 %in% all_ohana_peaks_reduced$window_pos_1[i]) &
  957. (chr_num %in% all_ohana_peaks_reduced$chr_num[i]) &
  958. (reason %in% all_ohana_peaks_reduced$reason[i]))
  959. if ( nrow(all_ohana_peaks_tmp) != 1 ) {
  960. all_ohana_peaks$lake[i] <- paste0(all_ohana_peaks_tmp$lake, collapse=", ")
  961. }
  962. }
  963. all_ohana_peaks <- unique(all_ohana_peaks)
  964. rm(all_ohana_peaks_raw, all_ohana_peaks_tmp, all_ohana_peaks_reduced)
  965. write.table(file=paste0(out_dir, "all_shared_meanLLR_peaks_genes_labeled_table_s3.txt"), x=apply(all_ohana_peaks,2,as.character), row.names=FALSE, col.names=TRUE, quote=FALSE, sep="\t")
  966. ## GO enrichment on all genes within outlier windows
  967. genes_all <- rbind(amos_outlier_genes, long_outlier_genes, pat_outlier_genes, quon_outlier_genes)
  968. all_ohana_peaks_genes_go <- gost(unique(genes_all$name),
  969. organism = "charengus",
  970. significant = TRUE,
  971. correction_method = "g_SCS",
  972. domain_scope = "custom", custom_bg = unique(annotation_df$name))
  973. View(all_ohana_peaks_genes_go$result)
  974. ## GO enrichment for each lake's outliers:
  975. for ( lake in l_lakes ) {
  976. print(lake)
  977. genes_list <- get(paste(lake, "outlier_genes", sep="_"))
  978. ohana_peaks_genes_go <- gost(unique(genes_list$name),
  979. organism = "charengus",
  980. significant = TRUE,
  981. correction_method = "g_SCS",
  982. domain_scope = "custom", custom_bg = unique(annotation_df$name))
  983. print(ohana_peaks_genes_go$result)
  984. }
  985. ```
  986. ``` {r Figures 4, S2 and S3 & Tables S2 and S3 - Protocadherins and Balancing Selection}
  987. #### FST, $\pi$, and dXY ####
  988. for ( value in c("fst", "pi", "dxy") ) {
  989. ## Load in various value-specific parameters
  990. nuc_div_threshold <- 0.99
  991. if ( value %in% "pi" ) {
  992. names_from <- "pop"
  993. col_name <- "avg_pi"
  994. lake_list <- all_lakes
  995. chrom_y_max <- 0.017
  996. } else {
  997. names_from <- c("pop1", "pop2")
  998. lake_list <- l_lakes
  999. if ( value %in% "dxy" ) {
  1000. col_name <- "avg_dxy"
  1001. chrom_y_max <- 0.018
  1002. } else if ( value %in% "fst" ) {
  1003. col_name <- "avg_wc_fst"
  1004. chrom_y_max <- 1
  1005. }
  1006. }
  1007. ## Windowed files - Load if already made, create if not
  1008. windowed_file <- paste0("./popgen_stats/pixy_files/all_", value, "_wide_50kb_10kb_windows.txt")
  1009. if ( file.exists(windowed_file) ) {
  1010. data_wide <- read.table(file=paste0("./popgen_stats/pixy_files/all_", value, "_wide_50kb_10kb_windows.txt"), header=TRUE, sep="\t") %>%
  1011. mutate(chr_num = as.factor(chr_num))
  1012. assign(paste("all", value, "wide", sep="_"), data_wide, pos=1)
  1013. } else if ( file.exists(paste0("./popgen_stats/pixy_files/sitelevel/", value, "/all_", value, "_chr1_wide_50kb_10kb_windows.txt")) ) { ## If the per-chromosome windowed files exist already, but just need to be combined
  1014. file_list <- list.files(path=paste0("./popgen_stats/pixy_files/sitelevel/", value), pattern=paste0("all_", value, "_chr\\d+_wide_50kb_10kb_windows\\.txt$"), full.names=TRUE)
  1015. data_list <- lapply(file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  1016. data_wide <- do.call(rbind, data_list)
  1017. for ( lake in lake_list ) {
  1018. value_colname <- paste("mean_value", lake, sep="_")
  1019. data_wide <- data_wide %>%
  1020. mutate(outlier = ifelse(get(value_colname) >= quantile(get(value_colname), probs = nuc_div_threshold, na.rm=TRUE), 1, 0)) %>%
  1021. rename_with(~paste("outlier", lake, sep="_"), "outlier")
  1022. }
  1023. if ( value %in% "dxy" ) {
  1024. data_wide <- data_wide %>%
  1025. mutate(total_outliers = rowSums(.[,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")]),
  1026. outlier_lakes = NA)
  1027. assign(paste("all", value, "wide", sep="_"), data_wide)
  1028. } else {
  1029. data_wide <- data_wide %>%
  1030. mutate(total_outliers = rowSums(.[,c("outlier_amos", "outlier_bride", "outlier_long","outlier_quon","outlier_pat")]),
  1031. outlier_lakes = NA)
  1032. assign(paste("all", value, "wide", sep="_"), data_wide)
  1033. }
  1034. write.table(file=paste0("./popgen_stats/pixy_files/all_", value, "_wide_50kb_10kb_windows.txt"), data_wide, col.names=TRUE, row.names=FALSE, quote=FALSE, sep="\t")
  1035. } else { ### Recreating the windowed files if desired or needed
  1036. file_list <- list.files(path=paste0("./popgen_stats/pixy_files/sitelevel/", value), pattern="\\.txt$", full.names=TRUE)
  1037. data_list <- lapply(file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  1038. data_long <- do.call(rbind, data_list) %>%
  1039. #rename_with(~c("avg"), get(colname)) %>%
  1040. filter(!is.na(get(col_name)))
  1041. data_wide <- pivot_wider(data_long, id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=all_of(names_from), values_from=all_of(col_name)) %>%
  1042. select(-window_pos_2) %>%
  1043. merge(., chroms, by.x="chromosome", by.y="chr") %>%
  1044. mutate(chr_num = as.factor(chr_num),
  1045. window_pos_1 = as.numeric(window_pos_1))
  1046. colnames(data_wide) <- tolower(colnames(data_wide))
  1047. ## Subset to just the comparisons of interest if necessary (i.e. for fst and dxy)
  1048. if ( value %in% "fst" | value %in% "dxy" ) {
  1049. data_wide <- data_wide %>% select(contains(c("chromosome", "window", "bride"))) %>%
  1050. rename_with(~c("amos", "long", "quon", "pat"), c("amos_bride", "bride_long", "bride_pat", "bride_quon"))
  1051. }
  1052. for ( lake in lake_list ) {
  1053. lake_value <- cbind(data_wide[,1:2], data_wide[,grepl(lake, colnames(data_wide))]) %>%
  1054. rename_with(~c("chr", "pos", "value")) %>%
  1055. filter(!is.na(value), chr != "NC_014690.1") %>%
  1056. mutate(value = as.numeric(value))
  1057. assign(x=paste(lake, value, sep="_"), value=lake_value, pos=1)
  1058. num_windows <- ceiling(sum(chrom_length[,2]) / 10000) + 5
  1059. windows <- data.frame("chr"=rep(0, num_windows), "window_pos_1"=rep(0, num_windows), "window_pos_2"=rep(0, num_windows),
  1060. "mean_value"=rep(0, num_windows), "num_snps"=rep(0, num_windows))
  1061. windows_row <- 1
  1062. for (chrom in 1:24) {
  1063. print(paste("Running", lake, "chr", chrom))
  1064. chr_subset <- subset(lake_value, chr %in% chrom_length[chrom,1]) %>%
  1065. arrange(pos) %>%
  1066. mutate(snp_count = 1:nrow(.))
  1067. window_start <- 1
  1068. window_end <- 50000
  1069. slide <- 10000
  1070. while ( window_start + 40000 <= chrom_length[chrom,2] ) {
  1071. subset_tmp <- subset(chr_subset, pos >= window_start & pos <= window_end)
  1072. windows$chr[windows_row] <- chrom_length[chrom,1]
  1073. windows$window_pos_1[windows_row] <- window_start
  1074. windows$window_pos_2[windows_row] <- window_end
  1075. windows$mean_value[windows_row] <- mean(subset_tmp$value, na.rm=TRUE)
  1076. windows$num_snps[windows_row] <- nrow(subset_tmp)
  1077. window_start <- window_start + slide
  1078. if ( window_end + slide <= chrom_length[chrom,2] ) { window_end <- window_end + slide }
  1079. else if ( window_end + slide > chrom_length[chrom,2] ) { window_end <- chrom_length[chrom,2] }
  1080. windows_row <- windows_row + 1
  1081. }
  1082. }
  1083. print(summary(windows$num_snps))
  1084. windows <- subset(windows, !is.na(mean_value)) %>%
  1085. merge(., chroms, by="chr") %>%
  1086. mutate(chr_num = as.factor(chr_num))
  1087. if ( value %in% "fst" | value %in% "dxy" ) {
  1088. windows <- windows %>%
  1089. mutate(outlier = ifelse(mean_value >= quantile(mean_value, probs = nuc_div_threshold, na.rm=TRUE), 1, 0)) %>%
  1090. rename_with(~paste("outlier", lake, sep="_"), "outlier")
  1091. } else {
  1092. windows <- windows %>%
  1093. mutate(outlier = ifelse(mean_value <= quantile(mean_value, probs = nuc_div_threshold, na.rm=TRUE), 1, 0)) %>%
  1094. rename_with(~paste("outlier", lake, sep="_"), "outlier")
  1095. }
  1096. assign(x=paste(lake, "windows", sep="_"), value=windows, pos=1)
  1097. }
  1098. if ( value %in% "fst" | value %in% "dxy" ) {
  1099. data_wide <- merge(amos_windows, long_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), suffixes=c("_amos", "_long"), all=TRUE) %>%
  1100. merge(., quon_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE) %>%
  1101. merge(., pat_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), suffixes=c("_quon", "_pat"), all=TRUE) %>%
  1102. mutate(total_outliers = rowSums(.[,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")]),
  1103. outlier_lakes = NA,
  1104. window_pos_1 = as.numeric(window_pos_1),
  1105. window_pos_2 = as.numeric(window_pos_2)) %>%
  1106. filter(!is.na(total_outliers))
  1107. } else {
  1108. data_wide <- merge(amos_windows, bride_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), suffixes=c("_amos", "_bride"), all=TRUE) %>%
  1109. merge(., long_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE) %>%
  1110. merge(., quon_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), suffixes=c("_long", "_quon"), all=TRUE) %>%
  1111. merge(., pat_windows, by=c("chr", "window_pos_1", "window_pos_2", "chr_num"), all=TRUE) %>%
  1112. rename_with(~c("mean_value_pat", "num_snps_pat"), c("mean_value", "num_snps")) %>%
  1113. mutate(total_outliers = rowSums(.[,c("amos_outlier", "bride_outlier", "long_outlier", "pat_outlier", "quon_outlier")]),
  1114. outlier_lakes = NA,
  1115. window_pos_1 = as.numeric(window_pos_1),
  1116. window_pos_2 = as.numeric(window_pos_2)) %>%
  1117. filter(!is.na(total_outliers))
  1118. }
  1119. }
  1120. ### Chromosome-wide graphing & other metrics
  1121. # Calculate the total number of outliers per window
  1122. if ( value %in% "fst" | value %in% "dxy" ) {
  1123. for ( i in 1:nrow(data_wide) ) {
  1124. if ( data_wide$total_outliers[i] == 1 ) {
  1125. lake <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1126. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1127. )))
  1128. )
  1129. data_wide$outlier_lakes[i] <- lake
  1130. }
  1131. else if ( data_wide$total_outliers[i] == 2 ) {
  1132. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1133. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1134. )))[1])
  1135. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1136. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1137. )))[2])
  1138. data_wide$outlier_lakes[i] <- paste(lake1, lake2, sep="_")
  1139. }
  1140. else if ( data_wide$total_outliers[i] == 3 ) {
  1141. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1142. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1143. )))[1])
  1144. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1145. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1146. )))[2])
  1147. lake3 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1148. apply(data_wide[i,c("outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1149. )))[3])
  1150. data_wide$outlier_lakes[i] <- paste(lake1, lake2, lake3, sep="_")
  1151. }
  1152. else if ( data_wide$total_outliers[i] == 4 ) {
  1153. data_wide$outlier_lakes[i] <- paste("amos", "long", "pat", "quon", sep="_")
  1154. }
  1155. }
  1156. ## Pi has 5 lakes, so it needs a separate loop
  1157. } else {
  1158. for ( i in 1:nrow(data_wide) ) {
  1159. if ( data_wide$total_outliers[i] == 1 ) {
  1160. lake <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1161. apply(data_wide[i,c("outlier_bride", "outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1162. )))
  1163. )
  1164. data_wide$outlier_lakes[i] <- lake
  1165. }
  1166. else if ( data_wide$total_outliers[i] == 2 ) {
  1167. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1168. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1169. )))[1])
  1170. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1171. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1172. )))[2])
  1173. data_wide$outlier_lakes[i] <- paste(lake1, lake2, sep="_")
  1174. }
  1175. else if ( data_wide$total_outliers[i] == 3 ) {
  1176. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1177. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1178. )))[1])
  1179. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1180. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1181. )))[2])
  1182. lake3 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1183. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1184. )))[3])
  1185. data_wide$outlier_lakes[i] <- paste(lake1, lake2, lake3, sep="_")
  1186. }
  1187. else if ( data_wide$total_outliers[i] == 4 ) {
  1188. lake1 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1189. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1190. )))[1])
  1191. lake2 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1192. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1193. )))[2])
  1194. lake3 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1195. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1196. )))[3])
  1197. lake4 <- gsub(pattern=".+_(.+)", replacement="\\1", x=as.character(names(which(
  1198. apply(data_wide[i,c("outlier_bride","outlier_amos","outlier_long","outlier_quon","outlier_pat")], 2, function(r) any(r %in% 1))
  1199. )))[4])
  1200. data_wide$outlier_lakes[i] <- paste(lake1, lake2, lake3, lake4, sep="_")
  1201. }
  1202. else if ( data_wide$total_outliers[i] == 5 ) {
  1203. data_wide$outlier_lakes[i] <- paste("bride", "amos", "long", "pat", "quon", sep="_")
  1204. }
  1205. }
  1206. }
  1207. ## Graph each lake separately
  1208. for ( lake in lake_list ) local({
  1209. lake <- lake
  1210. value_df <- data_wide %>%
  1211. select(contains(c("chr", "window_pos_1", "window_pos_2", colnames(.[which(grepl(lake, colnames(data_wide)))]), "total_outliers", "outlier_lakes"))) %>%
  1212. mutate(avg_window = (window_pos_1 + window_pos_2) / 2,
  1213. chr_num = factor(chr_num)) %>%
  1214. rename_with(~c("avg_value", "num_snps", "outlier"), 5:7) %>%
  1215. filter(!is.na(avg_value))
  1216. print(paste0(lake, " mean ", value, ": ", signif(mean(value_df$avg_value), 4)), pos=1)
  1217. print(paste("se: ", signif(mean_se(value_df$avg_value)$ymax - mean_se(value_df$avg_value)$y, 4)), pos=1)
  1218. ### Chromosomal graphs ###
  1219. print(quantile(value_df$avg_value, probs = nuc_div_threshold), pos=1)
  1220. ## All chromosomes - Fig. S2
  1221. # Remove some non-significant points from the graph for a smaller file size and easier manipulation
  1222. if ( value %in% "dxy" | value %in% "pi" ) {
  1223. nonsig_count <- 1
  1224. value_df_subset <- value_df[0,]
  1225. for ( row_num in 1:nrow(value_df) ) {
  1226. if ( nonsig_count%%10 == 0 ) {
  1227. value_df_subset <- rbind(value_df_subset, value_df[row_num,])
  1228. nonsig_count <- nonsig_count + 1
  1229. } else if ( nonsig_count%%10 != 0 ) { nonsig_count <- nonsig_count + 1 }
  1230. }
  1231. value_df_subset <- value_df_subset[-1,] %>%
  1232. mutate(chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  1233. } else {
  1234. nonsig_count <- 1
  1235. value_df_subset <- value_df[0,]
  1236. for ( row_num in 1:nrow(value_df) ) {
  1237. if ( nonsig_count%%5 == 0 ) {
  1238. value_df_subset <- rbind(value_df_subset, value_df[row_num,])
  1239. nonsig_count <- nonsig_count + 1
  1240. } else if ( nonsig_count%%5 != 0 ) { nonsig_count <- nonsig_count + 1 }
  1241. }
  1242. value_df_subset <- value_df_subset[-1,] %>%
  1243. mutate(chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  1244. }
  1245. value_plot <<- subset(value_df_subset, outlier == 0) %>%
  1246. ggplot(., aes(x=avg_window, y=avg_value, col=chr_num)) +
  1247. geom_point() +
  1248. geom_hline(aes(yintercept = quantile(value_df$avg_value, probs = nuc_div_threshold), color = get(paste0(lake, "_col")))) +
  1249. geom_point(data=subset(value_df, outlier %in% 1), aes(x=avg_window, y=avg_value, col=get(paste0(lake, "_col"))))+
  1250. scale_color_manual(values = c(rep(c("lightgrey", "darkgrey"), 12), get(paste0(lake, "_col")))) +
  1251. facet_grid( ~ chr_num, scales = "free_x", switch = "x", space = "free_x") +
  1252. theme_minimal() +
  1253. xlab("Chromosome") +
  1254. scale_y_continuous(name=paste(lake, value), limits=c(0, chrom_y_max)) +
  1255. theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
  1256. panel.spacing = unit(0.05, "cm"),
  1257. panel.grid = element_blank(),
  1258. strip.background = element_blank(),
  1259. strip.placement = "outside",
  1260. legend.position = "none",
  1261. axis.line.x = element_line(linewidth = 0.5, linetype = "solid", color = "black"))
  1262. ## Adding on 2, 3, and 4-way points to the above plot for FST and dXY
  1263. if ( value == "dxy" | value == "fst" ) {
  1264. value_plot2 <<- value_plot +
  1265. geom_point(data=subset(value_df, total_outliers %in% 2 & outlier_lakes %like% lake),
  1266. aes(x=window_pos_1 + 25000, y=avg_value, col=two_lakes_col)) +
  1267. geom_point(data=subset(value_df, total_outliers %in% 3 & outlier_lakes %like% lake),
  1268. aes(x=window_pos_1 + 25000, y=avg_value, col=three_lakes_col), shape=17, size=2) +
  1269. geom_point(data=subset(value_df, total_outliers %in% 4),
  1270. aes(x=window_pos_1 + 25000, y=avg_value, col=four_lakes_col), shape=18, size=3) +
  1271. scale_color_manual(values=c(rep(c(chrom1, chrom2), 12), get(paste0(lake, "_col")), two_lakes_col, three_lakes_col, four_lakes_col))
  1272. } else {
  1273. value_plot2 <<- value_plot
  1274. }
  1275. ### Only the chromosomes with pi/dxy 4-way outliers - Fig. 4 A-C
  1276. value_df_zoom <- subset(value_df, chr_num == 5 | chr_num == 20)
  1277. # Remove some non-significant points from the graph for a smaller file size and easier manipulation
  1278. if ( value %like% "dxy" | value %like% "pi" ) {
  1279. value_df_zoom_subset <- merge(value_df_zoom, value_df_subset[,c("chr", "avg_window")], by=c("chr", "avg_window")) %>%
  1280. mutate(chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  1281. } else {
  1282. value_df_zoom_subset <- subset(value_df_zoom, outlier == 0)
  1283. }
  1284. value_plot_zoom <<- ggplot(value_df_zoom_subset, aes(x=avg_window, y=avg_value, col=chr_num)) +
  1285. geom_point() +
  1286. geom_smooth(data=value_df_zoom, method = "loess", linewidth = 0.5, se = FALSE, color = "black", span = 0.05, na.rm=TRUE) +
  1287. geom_hline(aes(yintercept = quantile(value_df$avg_value, probs = nuc_div_threshold), color="outliers")) +
  1288. geom_point(data=subset(value_df_zoom, outlier == 1), aes(x=avg_window, y=avg_value, col="outliers")) +
  1289. geom_point(data=subset(value_df_zoom, chr_num == 5 & ((window_pos_1 >= 28200001 & window_pos_2 <= 28450000))), aes(x=avg_window, y=avg_value, col="balsel"), shape=18, size=3) +
  1290. geom_point(data=subset(value_df_zoom, chr_num == 20 & ((window_pos_1 >= 8930001 & window_pos_2 <= 9410000))), aes(x=avg_window, y=avg_value, col="balsel"), shape=18, size=3) +
  1291. scale_color_manual(values = c("lightgrey", "darkgrey", "outliers"=get(paste0(lake, "_col")), "balsel"=three_lakes_col)) +
  1292. facet_grid( ~ chr_num, scales = "free_x", switch = "x", space = "free_x") +
  1293. theme_cowplot() +
  1294. xlab("Chromosome") +
  1295. scale_y_continuous(name=paste(lake, value), limits=c(0, chrom_y_max)) +
  1296. theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
  1297. panel.spacing = unit(0.05, "cm"),
  1298. panel.grid = element_blank(),
  1299. strip.background = element_blank(),
  1300. strip.placement = "outside",
  1301. legend.position = "none",
  1302. axis.line.x = element_line(linewidth = 0.5, linetype = "solid", color = "black"))
  1303. ## Remove the x-axis labels from all but Pat
  1304. if ( lake != "pat" ) {
  1305. value_plot2 <- value_plot2 +
  1306. theme(axis.title.x = element_blank(), strip.text.x = element_blank())
  1307. value_plot_zoom <- value_plot_zoom +
  1308. theme(axis.title.x = element_blank(), strip.text.x = element_blank())
  1309. }
  1310. ## Save each lake's files
  1311. assign(paste(lake, "plot", sep="_"), value_plot2, pos=1)
  1312. assign(paste(lake, "plot_zoom", sep="_"), value_plot_zoom, pos=1)
  1313. assign(paste(lake, value, sep="_"), value_df, pos=1)
  1314. })
  1315. ## Grid the per-lake manhattan plots into one graph per nucleotide diversity value
  1316. if ( value %in% "pi" ) {
  1317. value_grid <- plot_grid(bride_plot, NULL, amos_plot, NULL, long_plot, NULL, quon_plot, NULL, pat_plot, nrow=9, align="hv", axis="b", rel_heights=c(0.98,-0.3,0.98,-0.3,0.98,-0.3,0.98,-0.3,1))
  1318. value_grid_zoom <- plot_grid(bride_plot_zoom, NULL, amos_plot_zoom, NULL, long_plot_zoom, NULL, quon_plot_zoom, NULL, pat_plot_zoom, nrow=9, align="hv", axis="b", rel_heights=c(0.93,-0.3,0.93,-0.3,0.93,-0.3,0.93,-0.3,1))
  1319. } else {
  1320. value_grid <- plot_grid(amos_plot, NULL, long_plot, NULL, quon_plot, NULL, pat_plot, nrow=7, align="hv", axis="b", rel_heights=c(0.95,-0.2,0.95,-0.2,0.95,-0.2,1))
  1321. value_grid_zoom <- plot_grid(amos_plot_zoom, NULL, long_plot_zoom, NULL, quon_plot_zoom, NULL, pat_plot_zoom, nrow=7, align="hv", axis="b", rel_heights=c(1,-0.2,1,-0.2,1,-0.,1))
  1322. }
  1323. assign(paste(value, "grid", sep="_"), value_grid)
  1324. assign(paste(value, "grid_zoom", sep="_"), value_grid_zoom)
  1325. }
  1326. ### Figure 4A-C
  1327. fst_grid_zoom
  1328. dxy_grid_zoom
  1329. pi_grid_zoom
  1330. chr5_chr20_plotgrid <- plot_grid(fst_grid_zoom, dxy_grid_zoom, pi_grid_zoom, nrow=3)
  1331. ggsave(paste(out_dir0, "filtering_redone_pairwise_fst_dxy_pi_sliding_50kb_10kb_50kb_001_chr5_chr20.pdf"), chr5_chr20_plotgrid, height=20, width=10)
  1332. ### Figure S2
  1333. fst_grid
  1334. dxy_grid
  1335. pi_grid
  1336. fst_dxy_pi_plotgrid <- plot_grid(fst_grid, dxy_grid, pi_grid, nrow=3)
  1337. fst_dxy_pi_plotgrid
  1338. ggsave(paste0(out_dir, "filtering_redone_pairwise_fst_dxy_pi_sliding_50kb_10kb_001_sharedpoints.pdf"), fst_dxy_pi_plotgrid, height=12, width=18)
  1339. ## Saving Fig. S2 fst, pi, and dxy separately for easier illustrator work
  1340. ggsave(paste0(out_dir, "filtering_redone_pairwise_fst_sliding_50kb_10kb_001_sharedpoints.pdf"), fst_grid, height=4, width=18)
  1341. ggsave(paste0(out_dir, "filtering_redone_pairwise_dxy_sliding_50kb_10kb_001_sharedpoints.pdf"), dxy_grid, height=4, width=18)
  1342. ggsave(paste0(out_dir, "filtering_redone_pairwise_pi_sliding_50kb_10kb_001_sharedpoints.pdf"), pi_grid, height=5, width=18)
  1343. #### Testing averages specifically within balancing selection regions (added within text) ####
  1344. for ( region in c("chr5_28200001-28400000", "chr20_8950001-9400000") ) {
  1345. for ( value in c("fst", "pi", "dxy") ) {
  1346. bal_subset <- subset(get(paste("all", value, "wide", sep="_")), chr_num %in% gsub("chr", "", str_split_i(region, "_", 1)) & window_pos_1 >= str_split_i(str_split_i(region, "_", 2), "-", 1) & window_pos_2 <= str_split_i(str_split_i(region, "_", 2), "-", 2))
  1347. if ( value %like% "pi" ) { lake_list <- all_lakes
  1348. } else { lake_list <- l_lakes }
  1349. sd_list <- c()
  1350. for ( lake in lake_list ) {
  1351. mean_val <- mean(bal_subset[[paste("mean_value", lake, sep="_")]], na.rm=TRUE)
  1352. print(paste0(region, " ", value, " ", lake, " mean: ", signif(mean_val, digits=4),
  1353. " (", round(sd(bal_subset[[paste("mean_value", lake, sep="_")]], na.rm=TRUE)/sqrt(nrow(bal_subset)), digits=4), ")"))
  1354. sd_val <- (mean_val - mean(get(paste("all", value, "wide", sep="_"))[[paste("mean_value", lake, sep="_")]], na.rm=TRUE)) / sd(get(paste("all", value, "wide", sep="_"))[[paste("mean_value", lake, sep="_")]], na.rm=TRUE)
  1355. print(paste(signif(sd_val, digits=4), "sd", ifelse(sd_val > 0, "above", "below"), "the mean"))
  1356. sd_list <- c(sd_list, sd_val)
  1357. }
  1358. print(paste0(region, " ", value, ": average ", signif(mean(sd_list), digits=4), ifelse(mean(sd_list) > 0, " above", " below"), " the mean"))
  1359. }
  1360. rm(sd_list, mean_val, sd_val)
  1361. }
  1362. #### Figure S3 ####
  1363. annotation_df <- read.delim("./GCF_018492685.1_fAloSap1.pri_genomic.gtf.gz", header = FALSE, sep = "\t", skip = 4,
  1364. col.names=c("chr", "type", "type2", "start", "stop", "blank1", "strand", "blank2", "info")) %>%
  1365. filter(type %in% "Gnomon" & type2 %in% "gene") %>%
  1366. select(-c(blank1, blank2)) %>%
  1367. separate_wider_delim(info, delim="; ", too_few="align_start",
  1368. names=c("gene_name1", NA, "GeneID", "gbkey", "gene_name2", "gene_biotype", NA, NA)) %>%
  1369. mutate(gene_name1 = unlist(strsplit(gene_name1, split="gene_id\ "))[c(seq(2,2*nrow(.), 2))],
  1370. GeneID = unlist(strsplit(GeneID, split="db_xref GeneID:"))[c(seq(2,2*nrow(.), 2))],
  1371. gbkey = unlist(strsplit(gbkey, split="gbkey\ "))[c(seq(2,2*nrow(.), 2))],
  1372. gene_name2 = unlist(strsplit(gene_name2, split="gene\ "))[c(seq(2,2*nrow(.), 2))],
  1373. gene_biotype = unlist(strsplit(gene_biotype, split="gene_biotype\ "))[c(seq(2,2*nrow(.), 2))]) %>%
  1374. filter(gene_name1 %in% gene_name2) %>%
  1375. select(-c(gene_name1, gbkey, gene_biotype)) %>%
  1376. rename_with(~c("ID", "name"), c(GeneID, gene_name2))
  1377. ## Plot site-level pi for any region of FST and dXY outliers:
  1378. for ( value in c("dxy", "fst") ) {
  1379. all_nuc_outliers <- get(paste("all", value, "wide", sep="_"))
  1380. n_peaks <- 500
  1381. highest_peaks_all <- c()
  1382. ## Only look at 4-way outliers (but can change to look at others)
  1383. for ( outlier_count in c(4) ) {
  1384. highest_peaks <- data.frame("peak_num"=rep(0, n_peaks), "chr"=rep(0, n_peaks), "window_pos_1"=rep(0, n_peaks), "window_pos_2"=rep(0, n_peaks), "chr_num"=rep(0, n_peaks), "n_windows"=rep(0, n_peaks), "lake"=rep(c("amos, long, quon, pat"), n_peaks), "reason"=rep(0, n_peaks))#, "reason2"=rep(0, n_peaks))
  1385. fst_dxy_outliers <- subset(all_nuc_outliers, total_outliers == outlier_count)
  1386. ## Break the loop if no such outliers exist
  1387. if ( dim(fst_dxy_outliers)[1] == 0 ) {
  1388. break
  1389. }
  1390. row_num <- 1
  1391. peak_num <- 1
  1392. for ( i in 1:nrow(fst_dxy_outliers) ) {
  1393. current_peak <- fst_dxy_outliers[i,]
  1394. peaks_subset <- subset(highest_peaks, chr %in% current_peak$chr)
  1395. if ( dim(peaks_subset)[1] == 0 ) { #Are there peaks on this chromosome yet? NO -
  1396. highest_peaks$peak_num[row_num] <- peak_num
  1397. highest_peaks$chr[row_num] <- current_peak$chr
  1398. highest_peaks$window_pos_1[row_num] <- current_peak$window_pos_1
  1399. highest_peaks$window_pos_2[row_num] <- current_peak$window_pos_2
  1400. highest_peaks$chr_num[row_num] <- current_peak$chr_num
  1401. highest_peaks$n_windows[row_num] <- 1
  1402. highest_peaks$reason[row_num] <- paste(value, outlier_count, "outlier", sep="_")
  1403. row_num <- row_num + 1
  1404. peak_num <- peak_num + 1
  1405. } else { #YES - Now subset windows on that chromosome to find other peaks within +-10kb of the current peak
  1406. peaks_subset <- subset(peaks_subset, (window_pos_1 >= (current_peak$window_pos_1 - 10000) &
  1407. window_pos_1 <= (current_peak$window_pos_1 + 10000)) |
  1408. (window_pos_2 >= (current_peak$window_pos_2 - 10000) &
  1409. window_pos_2 <= (current_peak$window_pos_2 + 10000)) )
  1410. if ( dim(peaks_subset)[1] == 0 ) { #Is there a peak on this chromosome within 10kb of this new peak? - NO
  1411. highest_peaks$peak_num[row_num] <- peak_num
  1412. highest_peaks$chr[row_num] <- current_peak$chr
  1413. highest_peaks$window_pos_1[row_num] <- current_peak$window_pos_1
  1414. highest_peaks$window_pos_2[row_num] <- current_peak$window_pos_2
  1415. highest_peaks$chr_num[row_num] <- current_peak$chr_num
  1416. highest_peaks$n_windows[row_num] <- 1
  1417. highest_peaks$reason[row_num] <- paste(value, outlier_count, "outlier", sep="_")
  1418. row_num <- row_num + 1
  1419. peak_num <- peak_num + 1
  1420. } else { #YES - Update highest_peaks with new peak minimum & maximum, count 1 more individual with that peak
  1421. peaks_subset$window_pos_1 <- if_else(current_peak$window_pos_1 < peaks_subset$window_pos_1,
  1422. current_peak$window_pos_1, peaks_subset$window_pos_1)
  1423. peaks_subset$window_pos_2 <- if_else(current_peak$window_pos_2 > peaks_subset$window_pos_2,
  1424. current_peak$window_pos_2, peaks_subset$window_pos_2)
  1425. peaks_subset$n_windows <- peaks_subset$n_windows + 1
  1426. peaks_subset$reason <- paste(unique(c(peaks_subset$reason, paste(value, outlier_count, "outlier", sep="_"))), collapse=' ')
  1427. highest_peaks[highest_peaks$peak_num == peaks_subset$peak_num, ] <- peaks_subset
  1428. }
  1429. }
  1430. }
  1431. highest_peaks <- highest_peaks %>%
  1432. filter(n_windows != 0)
  1433. highest_peaks_all <- rbind(highest_peaks_all, highest_peaks)
  1434. rm(highest_peaks, peaks_subset, row_num, peak_num)
  1435. }
  1436. # ## Adding the names of overlapping genes
  1437. genes_value <- c()
  1438. value_num <- 1
  1439. for (i in 1:nrow(highest_peaks_all)) {
  1440. # Either the gene 1) starts or 2) stops within the range, or 3) reaches across the whole range
  1441. gene_subset <- subset(annotation_df, chr %in% highest_peaks_all$chr[i] & (
  1442. ( start >= highest_peaks_all$window_pos_1[i] & start <= highest_peaks_all$window_pos_2[i] ) |
  1443. ( stop >= highest_peaks_all$window_pos_1[i] & stop <= highest_peaks_all$window_pos_2[i] ) |
  1444. ( start <= highest_peaks_all$window_pos_1[i] & stop >= highest_peaks_all$window_pos_2[i] )))
  1445. if ( dim(gene_subset)[1] == 0 ) {
  1446. highest_peaks_all$gene_overlap_names[i] <- NA
  1447. } else {
  1448. gene_names <- gene_subset %>%
  1449. group_by(chr) %>%
  1450. summarise(name = paste(name, collapse = ", "))
  1451. highest_peaks_all$gene_overlap_names[i] <- gene_names[1]
  1452. }
  1453. if ( value_num == 1 ) { genes_value <- gene_subset }
  1454. else { genes_value <- rbind(genes_value, gene_subset) }
  1455. value_num <- value_num + 1
  1456. }
  1457. assign(x=paste("genes", value, sep="_"), unique(genes_value))
  1458. highest_peaks_all <- highest_peaks_all[,-1]
  1459. assign(paste0(value, "_highest_peaks"), highest_peaks_all)
  1460. # write.table(file=paste0(out_dir, "all_", value, "_fourway_peaks_genes_labeled_table_s3.txt"), x=apply(highest_peaks_all,2,as.character), row.names=FALSE, col.names=TRUE, quote=FALSE, sep="\t")
  1461. }
  1462. #### Plot dXY and Pi within FST 4-way outliers ####
  1463. file_list <- list.files(path="/Users/corcorri/Documents/Denver/Manuscript/popgen_stats/pixy_files/sitelevel/fst", pattern="\\.txt$", full.names=TRUE)
  1464. data_list <- lapply(file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  1465. fst_sitelevel <- do.call(rbind, data_list) %>%
  1466. filter(!is.na(avg_wc_fst)) %>%
  1467. mutate(avg_wc_fst = as.numeric(avg_wc_fst)) %>%
  1468. pivot_wider(., id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=c("pop1", "pop2"), values_from="avg_wc_fst") %>%
  1469. select(-window_pos_2) %>%
  1470. mutate(window_pos_1 = as.numeric(window_pos_1)) %>%
  1471. select(contains(c("chr", "window", "Bride"))) %>%
  1472. rename_with(~c("amos_fst", "long_fst", "quon_fst", "pat_fst"), c("Amos_Bride", "Bride_Long", "Bride_Pat", "Bride_Quon"))
  1473. for ( i in 1:nrow(subset(highest_peaks_all, reason %like% "fst")) ) {
  1474. chrnum <- highest_peaks_all$chr_num[i]
  1475. start <- floor((highest_peaks_all$window_pos_1[i] - 50000)/1e6)*1e6+1
  1476. end <- ceiling((highest_peaks_all$window_pos_2[i] + 50000)/1e6)*1e6
  1477. if ( i == 1 ) { file_list <- c("chr1_3000001-4000000", "chr1_4000001-5000000")
  1478. } else if ( i == 2 ) { file_list <- c("chr2_42000001-43000000")
  1479. } else if ( i == 3 ) { file_list <- c("chr3_2000001-3000000")
  1480. } else if ( i == 4 ) { file_list <- c("chr3_39000001-40000000")
  1481. } else if ( i == 5 ) { file_list <- c("chr10_37000001-38000000")
  1482. } else if ( i == 6 ) { file_list <- c("chr13_10000000-11000000")
  1483. } else if ( i == 7 ) { file_list <- c("chr16_13000001-14000000")
  1484. } else if ( i == 8 ) { file_list <- c("chr17_3000001-4000000")
  1485. } else if ( i == 9 ) { file_list <- c("chr18_28000001-29000000") }
  1486. pi_file_list <- paste0(rep("/Users/corcorri/Documents/Denver/Manuscript/popgen_stats/pixy_files/sitelevel/pi/", length(file_list)), file_list, rep("_pi_subset.txt", length(file_list)))
  1487. dxy_file_list <- paste0(rep("/Users/corcorri/Documents/Denver/Manuscript/popgen_stats/pixy_files/sitelevel/dxy/", length(file_list)), file_list, rep("_dxy_subset.txt", length(file_list)))
  1488. # chr1_3000001-5000000_pi_subset.txt chr2_42000001-43000000_pi_subset.txt chr3_2000001-3000000_pi_subset.txt chr3_39000001-40000000_pi_subset.txt chr10_37000001-38000000_pi_subset.txt chr13_10000001-11000000_pi_subset.txt chr16_13000001-14000000_pi_subset.txt chr17_3000001-4000000_pi_subset.txt chr18_28000001-29000000_pi_subset.txt
  1489. ## Read in the files - pi
  1490. pi_list <- lapply(pi_file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  1491. pi_long <- do.call(rbind, pi_list) %>%
  1492. filter(!is.na(avg_pi)) %>%
  1493. merge(., chroms, by.x="chromosome", by.y="chr") %>%
  1494. mutate(pop = factor(tolower(pop), levels=all_lakes),
  1495. chr_num = as.factor(chr_num)) %>%
  1496. merge(., fst_sitelevel, by=c("chromosome", "window_pos_1")) %>%
  1497. #filter(pop != "bride") ## If you want to remove/add in Bride's pi in this region
  1498. subset(., window_pos_1 >= highest_peaks_all$window_pos_1[i] - 100000 & window_pos_1 <= highest_peaks_all$window_pos_2[i] + 100000)
  1499. # Plot
  1500. pi_sitelevel <- ggplot(pi_long, aes(x=window_pos_1, y=avg_pi, color=pop)) +
  1501. annotate(geom="rect", xmin=highest_peaks_all$window_pos_1[i], xmax=highest_peaks_all$window_pos_2[i], ymin=0, ymax=1, color="grey50", alpha=0.3) +
  1502. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.72,
  1503. label="mean pi_land within window", size=3, color="black") +
  1504. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.68,
  1505. label=signif(mean(subset(pi_long, window_pos_1 >= highest_peaks_all$window_pos_1[i] & window_pos_1 <= highest_peaks_all$window_pos_2[i])$avg_pi, na.rm=TRUE), digits=4), size=3, color="black") +
  1506. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.72,
  1507. label="mean pi_land outside window", size=3, color="black") +
  1508. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.68,
  1509. label=signif(mean(subset(pi_long, window_pos_1 < highest_peaks_all$window_pos_1[i] | window_pos_1 > highest_peaks_all$window_pos_2[i])$avg_pi, na.rm=TRUE), digits=4), size=3, color="black") +
  1510. geom_point(alpha=0.5) +
  1511. geom_line(alpha=0.3) +
  1512. scale_x_continuous(name=paste("Chr", chrnum)) +
  1513. scale_y_continuous(limits=c(0,1), name="Site-level pi") +
  1514. scale_color_manual(values=colors) +
  1515. theme_cowplot() +
  1516. theme(legend.position="none")
  1517. print(pi_sitelevel)
  1518. if ( chrnum == 18) {
  1519. for ( lake in all_lakes ) {
  1520. mean_pi <- mean(subset(pi_long, window_pos_1 < highest_peaks_all$window_pos_1[i] | window_pos_1 > highest_peaks_all$window_pos_2[i] &
  1521. pop %in% lake)$avg_pi, na.rm=TRUE)
  1522. mean_pi_region <- mean(subset(pi_long, window_pos_1 >= highest_peaks_all$window_pos_1[i] & window_pos_1 <= highest_peaks_all$window_pos_2[i] &
  1523. pop %in% lake)$avg_pi, na.rm=TRUE)
  1524. print(paste0(lake, " pi:", mean_pi_region/mean_pi, "x the mean of the surrounding region"))
  1525. }
  1526. }
  1527. ## Read in the files - dxy
  1528. dxy_list <- lapply(dxy_file_list, function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  1529. dxy_long <- do.call(rbind, dxy_list) %>%
  1530. filter(!is.na(avg_dxy),
  1531. pop1 %like% "Bride" | pop2 %like% "Bride") %>%
  1532. merge(., chroms, by.x="chromosome", by.y="chr") %>%
  1533. mutate(pop1 = ifelse(pop1 == "Bride", NA, pop1),
  1534. pop2 = ifelse(pop2 == "Bride", NA, pop2),
  1535. pop = factor(tolower(ifelse(is.na(pop1), pop2, pop1)), l_lakes),
  1536. chr_num = as.factor(chr_num)) %>%
  1537. select(-c(pop1, pop2, window_pos_2)) %>%
  1538. merge(., fst_sitelevel, by=c("chromosome", "window_pos_1")) %>%
  1539. subset(., window_pos_1 >= highest_peaks_all$window_pos_1[i] - 100000 & window_pos_1 <= highest_peaks_all$window_pos_2[i] + 100000)
  1540. # Plot
  1541. dxy_sitelevel <- ggplot(dxy_long, aes(x=window_pos_1, y=avg_dxy, color=pop)) +
  1542. annotate(geom="rect", xmin=highest_peaks_all$window_pos_1[i], xmax=highest_peaks_all$window_pos_2[i], ymin=0, ymax=1, color="grey50", alpha=0.3) +
  1543. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.72,
  1544. label="mean dxy within window", size=3, color="black") +
  1545. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.68,
  1546. label=signif(mean(subset(dxy_long, window_pos_1 >= highest_peaks_all$window_pos_1[i] & window_pos_1 <= highest_peaks_all$window_pos_2[i])$avg_dxy, na.rm=TRUE), digits=4), size=3, color="black") +
  1547. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.72,
  1548. label="mean dxy outside window", size=3, color="black") +
  1549. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.68,
  1550. label=signif(mean(subset(dxy_long, window_pos_1 < highest_peaks_all$window_pos_1[i] | window_pos_1 > highest_peaks_all$window_pos_2[i])$avg_dxy, na.rm=TRUE), digits=4), size=3, color="black") +
  1551. geom_point(alpha=0.5) +
  1552. geom_line(alpha=0.3) +
  1553. scale_x_continuous(name=paste("Chr", chrnum)) +
  1554. scale_y_continuous(limits=c(0,1), name="Site-level dXY") +
  1555. scale_color_manual(values=l_colors) +
  1556. theme_cowplot() +
  1557. theme(axis.title.x = element_blank(), strip.text.x = element_blank(), axis.text.x = element_blank(), legend.position = "none")
  1558. print(dxy_sitelevel)
  1559. if ( chrnum == 18) {
  1560. for ( lake in l_lakes ) {
  1561. mean_dxy <- mean(subset(dxy_long, window_pos_1 < highest_peaks_all$window_pos_1[i] | window_pos_1 > highest_peaks_all$window_pos_2[i] &
  1562. pop %in% lake)$avg_dxy, na.rm=TRUE)
  1563. mean_dxy_region <- mean(subset(dxy_long, window_pos_1 >= highest_peaks_all$window_pos_1[i] & window_pos_1 <= highest_peaks_all$window_pos_2[i] &
  1564. pop %in% lake)$avg_dxy, na.rm=TRUE)
  1565. print(paste0(lake, " dxy:", mean_dxy_region/mean_dxy, "x the mean of the surrounding region"))
  1566. }
  1567. }
  1568. ## Get site-level LLR and FST for the 3-way LLR and 4-way FST outlier as well
  1569. if ( i == 9 ) {
  1570. ## FST
  1571. region_subset <- merge(fst_sitelevel, chroms, by.x="chromosome", by.y="chr") %>%
  1572. subset(., window_pos_1 >= highest_peaks_all$window_pos_1[i] - 100000 & window_pos_1 <= highest_peaks_all$window_pos_2[i] + 100000 & chr_num %in% chrnum) %>%
  1573. rename_with(~l_lakes, c(paste(l_lakes, rep("fst", 4), sep="_"))) %>%
  1574. pivot_longer(cols=l_lakes, names_to="pop", values_to="avg_fst") %>%
  1575. mutate(avg_fst = as.numeric(if_else(avg_fst < 0, 0, avg_fst)),
  1576. pop = factor(pop, levels=l_lakes)) %>%
  1577. filter(!is.na(avg_fst))
  1578. fst_sitelevel_plot <- ggplot(region_subset, aes(x=window_pos_1, y=avg_fst, color=pop)) +
  1579. annotate(geom="rect", xmin=highest_peaks_all$window_pos_1[i], xmax=highest_peaks_all$window_pos_2[i], ymin=0, ymax=1, color="grey50", alpha=0.3) +
  1580. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.72,
  1581. label="mean fst within window", size=3, color="black") +
  1582. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=0.68,
  1583. label=signif(mean(subset(region_subset, window_pos_1 >= highest_peaks_all$window_pos_1[i] & window_pos_1 <= highest_peaks_all$window_pos_2[i])$avg_fst, na.rm=TRUE), digits=4), size=3, color="black") +
  1584. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.72,
  1585. label="mean fst outside window", size=3, color="black") +
  1586. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=0.68,
  1587. label=signif(mean(subset(region_subset, window_pos_1 < highest_peaks_all$window_pos_1[i] | window_pos_1 > highest_peaks_all$window_pos_2[i])$avg_fst, na.rm=TRUE), digits=4), size=3, color="black") +
  1588. geom_point(alpha=0.5) +
  1589. geom_line(alpha=0.3) +
  1590. scale_x_continuous(name=paste("Chr", chrnum)) +
  1591. scale_y_continuous(limits=c(0,1), name="Site-level FST") +
  1592. scale_color_manual(values=l_colors) +
  1593. theme_cowplot() +
  1594. theme(axis.title.x = element_blank(), strip.text.x = element_blank(), axis.text.x = element_blank(), legend.position = "none")
  1595. ## LLR - MUST RUN PART OF THE FIGURE 3 SCRIPT TO GET THE PER-SNP OHANA VALUES
  1596. region_subset <- rbind(amos_ohana_snp, long_ohana_snp, quon_ohana_snp, pat_ohana_snp) %>%
  1597. subset(., pos >= highest_peaks_all$window_pos_1[i] - 100000 & pos <= highest_peaks_all$window_pos_2[i] + 100000 & chr_num %in% chrnum) %>%
  1598. mutate(lle_ratio = as.numeric(lle_ratio),
  1599. pop = factor(pop, levels=l_lakes)) %>%
  1600. filter(!is.na(lle_ratio))
  1601. llr_sitelevel_plot <- ggplot(region_subset, aes(x=pos, y=lle_ratio, color=pop)) +
  1602. annotate(geom="rect", xmin=highest_peaks_all$window_pos_1[i], xmax=highest_peaks_all$window_pos_2[i], ymin=0, ymax=12, color="grey50", alpha=0.3) +
  1603. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=11,
  1604. label="mean LLR within window", size=3, color="black") +
  1605. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i]), y=10.5,
  1606. label=signif(mean(subset(region_subset, pos >= highest_peaks_all$window_pos_1[i] & pos <= highest_peaks_all$window_pos_2[i])$lle_ratio, na.rm=TRUE), digits=4), size=3, color="black") +
  1607. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=11,
  1608. label="mean LLR outside window", size=3, color="black") +
  1609. annotate("text", x=((highest_peaks_all$window_pos_2[i] - highest_peaks_all$window_pos_1[i])/2 + highest_peaks_all$window_pos_1[i] + 75000), y=10.5,
  1610. label=signif(mean(subset(region_subset, pos < highest_peaks_all$window_pos_1[i] | pos > highest_peaks_all$window_pos_2[i])$lle_ratio, na.rm=TRUE), digits=4), size=3, color="black") +
  1611. geom_point(alpha=0.5) +
  1612. geom_line(alpha=0.3) +
  1613. scale_x_continuous(name=paste("Chr", chrnum)) +
  1614. scale_y_continuous(limits=c(0,12), name="Site-level LLR") +
  1615. scale_color_manual(values=l_colors) +
  1616. theme_cowplot() +
  1617. theme(axis.title.x = element_blank(), strip.text.x = element_blank(), axis.text.x = element_blank(), legend.position = "none")
  1618. ## Plotgrid of all of the relevant metrics
  1619. pi_dxy_grid <- plot_grid(llr_sitelevel_plot, fst_sitelevel_plot, dxy_sitelevel, pi_sitelevel, nrow=4, rel_heights = c(1, 1, 1, 1.31))
  1620. } else {
  1621. pi_dxy_grid <- plot_grid(dxy_sitelevel, pi_sitelevel, nrow=2)
  1622. }
  1623. assign(paste0("chr", chr_num, "_", start, "_", end, "_grid"), pi_dxy_grid)
  1624. }
  1625. pi_grid <- plot_grid(`chr1_3000001_5e+06_grid`, `chr2_42000001_4.3e+07_grid`, `chr3_2000001_3e+06_grid`, `chr3_39000001_4e+07_grid`, `chr10_37000001_3.8e+07_grid`, `chr13_10000001_1.1e+07_grid`, `chr16_13000001_1.4e+07_grid`, `chr17_3000001_4e+06_grid`, `chr18_28000001_2.9e+07_grid`, nrow=5)
  1626. pi_grid
  1627. ggsave(paste0(out_dir, "fst_fourway_pi_dxy_sitelevel_lines.pdf"), pi_grid, width=12, height=20)
  1628. ## Just chr18, as is Fig. S3
  1629. `chr18_28000001_2.9e+07_grid`
  1630. ggsave(paste0(out_dir, "chr18_llr_fst_pi_dxy_sitelevel_lines.pdf"), `chr18_28000001_2.9e+07_grid`, width=10, height=6)
  1631. #### Table S2 - Adding balancing selection dXY outliers ####
  1632. ## Load in the dXY and meanLLR outliers, subsetting the dXY outliers to only those within the regions of balancing selection
  1633. dxy_table <- read.table(paste0(out_dir, "all_dxy_fourway_peaks_genes_labeled_table_s3.txt"), header=TRUE, sep="\t") %>%
  1634. filter((chr == "NC_055961.1" & window_pos_1 == 28190001) | (chr == "NC_055976.1" & window_pos_1 == 8930001) ) %>%
  1635. mutate(reason = ifelse(reason == "dxy_4_outlier", "dxy_4_outlier, pi_5_outlier", reason))
  1636. meanLLR_pairwise_table <- read.table(paste0(out_dir, "all_shared_meanLLR_peaks_genes_labeled_table_s3.txt"), header=TRUE, sep="\t") %>%
  1637. select(-c("peak_label"))
  1638. ## Combine the tables
  1639. all_peaks <- rbind(dxy_table, meanLLR_pairwise_table) %>%
  1640. arrange(chr_num, window_pos_1)
  1641. write.table(file=paste0(out_dir, "all_fst_dxy_meanLLR_2-4_shared_peaks_genes_labeled_table_s3.txt"), all_peaks, row.names=FALSE, col.names=TRUE, quote=FALSE, sep="\t")
  1642. #### Table S3 - Pairwise dXY in outlier regions ####
  1643. chr5_dxy <- read.table("./popgen_stats/pixy_files/sitelevel/dxy/chr5_28000001-29000000_dxy_subset.txt", header=TRUE, sep="\t") %>%
  1644. filter(window_pos_1 >= 28200001 & window_pos_1 <= 28450000)
  1645. chr20_dxy <- rbind(read.table("./popgen_stats/pixy_files/sitelevel/dxy/chr20_8000001-9000000_dxy_subset.txt", header=TRUE, sep="\t"),
  1646. read.table("./popgen_stats/pixy_files/sitelevel/dxy/chr20_9000001-9999999_dxy_subset.txt", header=TRUE, sep="\t")) %>%
  1647. filter(window_pos_1 >= 8930001 & window_pos_1 <= 9410000)
  1648. for ( region in c("chr5_28200001-284500000", "chr20_8930001-9410000") ) local({
  1649. ## Region-specific variables
  1650. region_chr_num <- as.numeric(gsub(pattern="chr(.+)_.+-.+", region, replacement="\\1"))
  1651. range_start <- as.numeric(gsub(pattern=".+_(.+)-.+", region, replacement="\\1"))
  1652. range_end <- as.numeric(gsub(pattern=".+_.+-(.+)", region, replacement="\\1"))
  1653. print(paste0("Running region ", region, ": dXY"), pos=1)
  1654. chrom_wider <- pivot_wider(get(paste0("chr", region_chr_num, "_dxy"), pos=1),
  1655. id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=all_of(c("pop1", "pop2")), values_from="avg_dxy")
  1656. ## Create pairwise average dfs for circle plots
  1657. dxy_pairwise <- subset(chrom_wider, window_pos_1 >= range_start & window_pos_1 <= range_end)[,-3] %>%
  1658. rename_with(tolower)
  1659. dxy_triangle <- data.frame("bride"=rep(NA,5), "amos"=rep(NA,5), "long"=rep(NA,5), "quon"=rep(NA,5), "pat"=rep(NA, 5),
  1660. row.names = c("bride", "amos", "long", "quon", "pat"))
  1661. for ( rowlake in c("amos", "bride", "long", "pat", "quon") ) {
  1662. for ( collake in c("amos", "bride", "long", "pat", "quon") ) {
  1663. if ( rowlake != collake ) {
  1664. colname <- paste(rowlake, collake, sep="_")
  1665. if ( colname %in% colnames(dxy_pairwise) ) {
  1666. if ( (rowlake=="bride" || rowlake=="amos" || rowlake=="long") && (collake=="long" || collake=="pat" || collake=="quon") ) {
  1667. dxy_triangle[collake, rowlake] <- mean(dxy_pairwise[[colname]], na.rm=TRUE)
  1668. } else {
  1669. dxy_triangle[rowlake, collake] <- mean(dxy_pairwise[[colname]], na.rm=TRUE)
  1670. }
  1671. }
  1672. }
  1673. }
  1674. }
  1675. assign(paste0("dxy_average_chr", region_chr_num), as.matrix(dxy_triangle), pos=1)
  1676. })
  1677. ```
  1678. ``` {r Figure 4F-G, S4 - BetaScan}
  1679. for ( chr in c("NC_055961.1", "NC_055976.1")) {
  1680. if ( chr %in% "NC_055961.1" ) { chr_num <- 5
  1681. } else if ( chr %in% "NC_055976.1" ) { chr_num <- 20 }
  1682. for ( lake in c("bride", "amos", "long", "pat", "quon")) {
  1683. b1 <- read.table(paste("./betascan/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1", lake, "maxMissing75_HWE1e-10", chr, "w2400_m015_p20.betascores.txt", sep="_"), sep="\t", header=TRUE) %>%
  1684. rename_with(~c("pos", "beta1")) %>%
  1685. mutate(lake = lake,
  1686. zscore = (beta1 - mean(beta1, na.rm=TRUE)) / sd(beta1))
  1687. assign(paste(lake, chr_num, "b1", sep="_"), b1)
  1688. }
  1689. value <- "beta1"
  1690. ## First combine all pop-specific files into one dataframe for graphing
  1691. b1_rbind <- rbind(get(paste("bride", chr_num, "b1", sep="_")), get(paste("amos", chr_num, "b1", sep="_")),
  1692. get(paste("long", chr_num, "b1", sep="_")), get(paste("quon", chr_num, "b1", sep="_")),
  1693. get(paste("pat", chr_num, "b1", sep="_"))) %>%
  1694. mutate(lake = factor(lake, levels=c("bride", "amos", "long", "quon", "pat")))
  1695. assign(paste("all_pops", chr_num, "b1", sep="_"), b1_rbind)
  1696. ## Subset the dataframe to only the top 1% and bottom 99% to differentiate colors on the graph (1% outliers are colored)
  1697. top_value <- 0.95
  1698. b1_top <- subset(b1_rbind, (lake %in% "bride" & get(value) >= quantile(get(paste("bride", chr_num, "b1", sep="_"))[[value]], top_value)) |
  1699. (lake %in% "amos" & get(value) >= quantile(get(paste("amos", chr_num, "b1", sep="_"))[[value]], top_value)) |
  1700. (lake %in% "long" & get(value) >= quantile(get(paste("long", chr_num, "b1", sep="_"))[[value]], top_value)) |
  1701. (lake %in% "pat" & get(value) >= quantile(get(paste("pat", chr_num, "b1", sep="_"))[[value]], top_value)) |
  1702. (lake %in% "quon" & get(value) >= quantile(get(paste("quon", chr_num, "b1", sep="_"))[[value]], top_value)) )%>%
  1703. mutate(lake = factor(lake, levels=c("bride", "amos", "long", "quon", "pat")))
  1704. assign(paste("all_pops", chr_num, "b1_top", sep="_"), b1_top)
  1705. b1_bottom <- anti_join(b1_rbind, b1_top, by=c("pos", "beta1", "lake"))
  1706. ## Plotting
  1707. if ( chr_num == 5 ) {
  1708. region_start <- 28200001
  1709. region_end <- 28450000
  1710. } else if ( chr_num == 20 ) {
  1711. region_start <- 8930001
  1712. region_end <- 9410000
  1713. }
  1714. rect_min <- min(b1_rbind[[value]])
  1715. rect_max <- max(b1_rbind[[value]]) + (range(b1_rbind[[value]])[2] - range(b1_rbind[[value]])[1])*0.05 ## Window rect +5% taller than the range
  1716. gene_top <- max(b1_rbind[[value]])*0.8
  1717. gene_mid <- max(b1_rbind[[value]])*0.75
  1718. gene_bot <- max(b1_rbind[[value]])*0.7
  1719. ## Zoom on just the -800Kb to +1000Kb around the 4-way region
  1720. b1_zoom <- subset(b1_rbind, pos >= (region_start - 800000) & pos <= (region_end + 1000000))
  1721. chrom_zoom <- ggplot(b1_zoom, aes(x=pos, y=get(value), group=lake, color=lake)) +
  1722. annotate(geom="rect", xmin=region_start, xmax=region_end, ymin=rect_min, ymax=rect_max, color="grey50", alpha=0.3) +
  1723. geom_line(alpha=0.5) +
  1724. scale_color_manual(values=c(colors, "grey20")) +
  1725. scale_x_continuous(name=paste("Chr", chr_num)) +
  1726. scale_y_continuous(name=value) +
  1727. theme_cowplot()
  1728. if ( chr_num == 5 ) {
  1729. chrom_zoom <- chrom_zoom +
  1730. annotate(geom="segment", x=28228417, xend=28391246, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1731. annotate(geom="segment", x=28307847, xend=28314166, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1732. annotate(geom="segment", x=28381269, xend=28383405, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1733. annotate(geom="segment", x=28385064, xend=28387610, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1734. annotate(geom="segment", x=28396987, xend=28399447, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1735. annotate(geom="segment", x=28412971, xend=28416134, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1736. annotate(geom="segment", x=28438276, xend=28504691, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1737. annotate(geom="segment", x=28493676, xend=28498165, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1738. annotate(geom="segment", x=28521527, xend=28530635, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1739. annotate(geom="segment", x=28505617, xend=28507890, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1740. annotate(geom="segment", x=28508816, xend=28511211, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1741. annotate(geom="segment", x=28511812, xend=28514550, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1742. annotate(geom="segment", x=28531249, xend=28535957, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5)
  1743. ## LOC121709120 - 28228417..28391246 (protocadherin gamma-A4-like)
  1744. ## LOC121709123 - 28307847..28314166 (protocadherin gamma-A4-like)
  1745. ## LOC121710140 - 28381269..28383405 (protocadherin gamma-A4-like)
  1746. ## LOC121708907 - 28385064..28387610 (protocadherin beta-15-like)
  1747. ## LOC121708908 - 28396987..28399447 (protocadherin beta-16-like)
  1748. ## LOC121709757 - 28412971..28416134 (protocadherin beta-16-like)
  1749. ## LOC121709523 - 28438276..28504691 (protocadherin alpha-C2-like)
  1750. ## LOC121709525 - 28521527..28530635 (protocadherin alpha-C2-like)
  1751. ## LOC121709759 - 28505617..28507890 (protocadherin alpha-12-like)
  1752. ## LOC121709528 - 28508816..28511211 (protocadherin alpha-2-like)
  1753. ## LOC121709524 - 28493676..28498165 (protocadherin alpha-2-like)
  1754. ## LOC121709526 - 28511812..28514550 (protocadherin alpha-1-like)
  1755. ## LOC121709521 - 28531249..28535957 (protocadherin-10)
  1756. } else {
  1757. chrom_zoom <- chrom_zoom +
  1758. annotate(geom="segment", x=8975000, xend=9010276, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1759. annotate(geom="segment", x=9015269, xend=9017929, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1760. annotate(geom="segment", x=9018098, xend=9020528, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1761. annotate(geom="segment", x=9021142, xend=9192693, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1762. annotate(geom="segment", x=9053052, xend=9058530, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1763. annotate(geom="segment", x=9121609, xend=9124645, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1764. annotate(geom="segment", x=9111286, xend=9114221, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1765. annotate(geom="segment", x=9108509, xend=9110664, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1766. annotate(geom="segment", x=9119038, xend=9121323, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1767. annotate(geom="segment", x=9204710, xend=9401767, y=gene_top, yend=gene_top, color="grey20", linewidth=0.5) +
  1768. annotate(geom="segment", x=9207621, xend=9240325, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1769. annotate(geom="segment", x=9227979, xend=9230859, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1770. annotate(geom="segment", x=9247246, xend=9254167, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1771. annotate(geom="segment", x=9258751, xend=9261663, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1772. annotate(geom="segment", x=9261792, xend=9265960, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5) +
  1773. annotate(geom="segment", x=9320653, xend=9323736, y=gene_bot, yend=gene_bot, color="grey20", linewidth=0.5) +
  1774. annotate(geom="segment", x=9329782, xend=9335320, y=gene_mid, yend=gene_mid, color="grey20", linewidth=0.5)
  1775. ## LOC121693912 8975000..9010276 (protocadherin alpha-C2-like)
  1776. ## LOC121693920 9015269..9017929 (protocadherin alpha-10-like)
  1777. ## LOC121693922 9018098..9020528 (protocadherin alpha-8-like)
  1778. ## LOC121693910 9021142..9192693 (protocadherin alpha-C2-like)
  1779. ## LOC121693917 9053052..9058530 (protocadherin alpha-7-like)
  1780. ## LOC121693926 9121609..9124645 (protocadherin alpha-8-like)
  1781. ## LOC121693925 9111286..9114221 (protocadherin alpha-7-like)
  1782. ## LOC121694333 9108509..9110664 (protocadherin alpha-3-like)
  1783. ## LOC121694863 9119038..9121323 (protocadherin alpha-3-like)
  1784. ## pcdh2g28 9204710..9401767 (protocadherin 2 gamma 28)
  1785. ## LOC121693914 9207621..9240325 (protocadherin gamma-A2-like)
  1786. ## LOC121693918 9227979..9230859 (protocadherin beta-16-like)
  1787. ## LOC121693921 9247246..9254167 (protocadherin beta-15-like)
  1788. ## LOC121693923 9258751..9261663 (protocadherin gamma-A10-like)
  1789. ## LOC121693916 9261792..9265960 (protocadherin beta-15)
  1790. ## LOC121693915 9320653..9323736 (protocadherin gamma-A11-like)
  1791. ## LOC121693924 9329782..9335320 (protocadherin gamma-A11-like)
  1792. }
  1793. assign(paste0("chr", chr_num, "_zoom_b1"), chrom_zoom)
  1794. ## Upset plot of balanced SNPs
  1795. b1_bottom_zoom <- subset(b1_bottom, pos >= (region_start - 800000) & pos <= (region_end + 1000000))
  1796. b1_top_zoom <- subset(b1_top, pos >= (region_start - 800000) & pos <= (region_end + 1000000))
  1797. if ( chr_num == 5 ) {
  1798. b1_top_zoom <- subset(b1_top_zoom, pos >= 28200001 & pos <= 28450000)
  1799. } else {
  1800. b1_top_zoom <- subset(b1_top_zoom, pos >= 8930001 & pos <= 9410000)
  1801. }
  1802. b1_top_zoom_wider <- pivot_wider(b1_top_zoom, id_cols="pos", names_from="lake", values_from=c("beta1", "zscore"))
  1803. for ( lake in c("bride", "amos", "long", "quon", "pat")) {
  1804. b1_top_zoom_wider[[lake]] <- if_else(is.na(b1_top_zoom_wider[[paste("beta1", lake, sep="_")]]), 0, 1)
  1805. }
  1806. assign(paste0("chr", chr_num, "_zoom_b1_wider"), b1_top_zoom_wider)
  1807. size = get_size_mode('exclusive_intersection')
  1808. upset_order <- colnames(b1_top_zoom_wider)[c(16:12)]
  1809. upset_plot <- upset(b1_top_zoom_wider, upset_order,
  1810. base_annotations=list(
  1811. 'Intersection size'=intersection_size(
  1812. text_mapping=aes(color="black", y=(!!size + 25))
  1813. ) +
  1814. ylab(paste0("SNPs with >=", top_value*100, "% Beta1: chr", chr_num)) +
  1815. ylim(0,650)
  1816. ),
  1817. intersections=list("bride", "amos", "long", "quon", "pat",
  1818. c("bride", "amos"), c("bride", "long"), c("bride", "quon"), c("bride", "pat"),
  1819. c("amos", "long"), c("amos", "quon"), c("amos", "pat"),
  1820. c("long", "quon"), c("long", "pat"), c("quon", "pat"),
  1821. c("bride", "amos", "long"), c("bride", "amos", "quon"), c("bride", "amos", "pat"), c("bride", "long", "quon"),
  1822. c("bride", "long", "pat"), c("bride", "quon", "pat"),
  1823. c("amos", "long", "quon"), c("amos", "long", "pat"), c("amos", "quon", "pat"), c("long", "quon", "pat"),
  1824. c("bride", "amos", "long", "quon"), c("bride", "amos", "long", "pat"), c("bride", "amos", "quon", "pat"),
  1825. c("bride", "long", "quon", "pat"),
  1826. c("amos", "long", "quon", "pat"),
  1827. c("bride", "amos", "long", "quon", "pat")),
  1828. queries=list(
  1829. upset_query(set='bride', fill=bride_col),
  1830. upset_query(set='amos', fill=amos_col),
  1831. upset_query(set='long', fill=long_col),
  1832. upset_query(set='pat', fill=pat_col),
  1833. upset_query(set='quon', fill=quon_col)),
  1834. themes=list('Intersection size'=list(theme_cowplot(), theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(), strip.text.x = element_blank())),
  1835. 'intersections_matrix'=list(theme_cowplot(), theme(axis.text.x=element_blank(), strip.text.x = element_blank(), axis.title=element_blank(), axis.ticks=element_blank(), axis.line=element_blank()))
  1836. ),
  1837. name="population", keep_empty_groups=TRUE, width_ratio = 0.15, sort_sets=FALSE, sort_intersections=FALSE,
  1838. )
  1839. assign(paste("chr", chr_num, "top_balanced_upset", sep="_"), upset_plot)
  1840. assign(paste0("chr", chr_num, "top", top_value*100, "_bal_snps"), b1_top_zoom_wider)
  1841. }
  1842. chr5_b1
  1843. chr5_zoom_b1
  1844. chr20_b1
  1845. chr20_zoom_b1
  1846. zoom_plots <- plot_grid(chr5_zoom_b1, chr20_zoom_b1, nrow=2, align="v", axis="b")
  1847. zoom_plots
  1848. # ggsave(paste0("betascan/betascan_chr5_chr20_zoom_", value, ".pdf"), zoom_plots, width=6, height=8)
  1849. ggsave(paste0(out_dir, "betascan_chr5_chr20_pi_dxy_zoom_", value, "_lineplot.pdf"), zoom_plots, width=6, height=6)
  1850. ## Upset plots
  1851. chr_5_top_balanced_upset
  1852. chr_20_top_balanced_upset
  1853. zoom_upsets <- plot_grid(chr_5_top_balanced_upset, chr_20_top_balanced_upset, nrow=2, align="v", axis="b")
  1854. zoom_upsets
  1855. ggsave(paste0(out_dir, "betascan_chr5_chr20_pi_dxy_zoom_", value, "_upsets.pdf"), zoom_upsets, width=12, height=6)
  1856. ### Manhattan Plot of balancing selection
  1857. for ( lake in all_lakes ) {
  1858. for ( chrom_name in chroms$chr[2:nrow(chroms)] ) {
  1859. tmp <- read.table(paste("./betascan/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1", lake, "maxMissing75_HWE1e-10", chrom_name, "w2400_m015_p20.betascores.txt", sep="_"), header=TRUE, sep="\t") %>%
  1860. rename_with(~c("pos", "b1")) %>%
  1861. mutate(lake = lake,
  1862. chr_num = chroms$chr_num[which(chroms$chr == chrom_name)])
  1863. if ( chrom_name %like% "80" ) { tmp2 <- tmp
  1864. } else { tmp2 <- rbind(tmp2, tmp) }
  1865. }
  1866. assign(paste(lake, "betascores", sep="_"), tmp2, pos=1)
  1867. rm(tmp, tmp2)
  1868. }
  1869. all_betascores <- rbind(bride_betascores, amos_betascores, long_betascores, pat_betascores, quon_betascores) %>%
  1870. mutate(lake = factor(lake, levels=all_lakes),
  1871. chr_num = factor(chr_num, levels=seq(1, 24)))
  1872. value_plot <- ggplot(all_betascores, aes(x=pos, y=b1, group=lake, col=lake)) +
  1873. geom_line(alpha=0.5) +
  1874. #geom_point(alpha=0.2) +
  1875. scale_color_manual(values = colors) +
  1876. facet_grid( ~ chr_num, scales = "free_x", switch = "x", space = "free_x") +
  1877. theme_minimal() +
  1878. xlab("Chromosome") +
  1879. scale_y_continuous(name="BetaScan") +
  1880. theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
  1881. panel.spacing = unit(0.05, "cm"),
  1882. panel.grid = element_blank(),
  1883. strip.background = element_blank(),
  1884. strip.placement = "outside",
  1885. legend.position = "none",
  1886. axis.line.x = element_line(linewidth = 0.5, linetype = "solid", color = "black"))
  1887. value_plot
  1888. ggsave("betascan/betascan_full_genome_lineplot.pdf", value_plot, height=6, width=12)
  1889. ## Per-lake plots
  1890. for (lake in all_lakes ) {
  1891. tmp <- get(paste(lake, "betascores", sep="_"), pos=1) %>%
  1892. mutate(outlier = ifelse(b1 >= quantile(b1, 0.99), 1, 0),
  1893. chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  1894. nonsig_count <- 1
  1895. tmp_subset <- data.frame("pos"=0, "b1"=0, "lake"=0, "chr_num"=0, "outlier"=0)
  1896. for (row_num in 1:nrow(tmp)) {
  1897. if ( tmp$outlier[row_num] == 0 & nonsig_count%%10 == 0 ) {
  1898. tmp_subset <- rbind(tmp_subset, tmp[row_num,])
  1899. nonsig_count <- nonsig_count + 1
  1900. } else if ( nonsig_count%%10 != 0 ) { nonsig_count <- nonsig_count + 1 }
  1901. }
  1902. tmp_subset <- tmp_subset[-1,] %>%
  1903. mutate(chr_num = factor(chr_num, levels=c(as.character(seq(1,24)))))
  1904. ## Plotting
  1905. lake_col <- get(paste0(lake, "_col"))
  1906. tmp_plot <- ggplot(tmp) +
  1907. geom_point(aes(x=pos, y=b1, col=chr_num)) +
  1908. geom_hline(aes(yintercept=quantile(b1, 0.99), col=lake_col), linewidth=1) +
  1909. geom_point(data=subset(tmp, outlier %in% 1), aes(x=pos, y=b1, col=lake_col)) +
  1910. xlab("Chromosome") +
  1911. scale_y_continuous(name=lake, limits=c(0,830)) +
  1912. scale_color_manual(values=c(rep(c(chrom1, chrom2), 12), rep(lake_col, 2))) +
  1913. facet_grid( ~ chr_num, scales = "free_x", switch = "x", space = "free_x") +
  1914. theme_minimal() +
  1915. theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
  1916. panel.spacing = unit(0.05, "cm"),
  1917. panel.grid = element_blank(),
  1918. strip.background = element_blank(),
  1919. strip.placement = "outside",
  1920. legend.position = "none",
  1921. axis.line.x = element_line(linewidth = 0.5, linetype = "solid", color = "black"))
  1922. ## Remove the x-axis labels to condense the plots together
  1923. if (lake != "pat") {
  1924. tmp_plot <<- tmp_plot + theme(axis.title.x=element_blank(), axis.text.x=element_blank(),
  1925. axis.ticks.x=element_blank(), strip.text.x = element_blank())
  1926. }
  1927. assign(x=paste(lake, "betascan_plot", sep="_"), value=tmp_plot, pos=1)
  1928. }
  1929. betascan_plotgrid <- plot_grid(bride_betascan_plot, NULL, amos_betascan_plot, NULL, long_betascan_plot, NULL, quon_betascan_plot, NULL, pat_betascan_plot, nrow=9, rel_heights=c(0.98,-0.3,0.98,-0.3,0.98,-0.3,0.98,-0.3,1), align="hv", axis="b")
  1930. betascan_plotgrid
  1931. ggsave(paste0("./betascan/betascan_full_genome_scatterplot_separate.pdf"), betascan_plotgrid, height=6, width=12)
  1932. #### Figure 4D & E - AF in Balancing Selection Regions ####
  1933. for ( region in c("chr5_28200001-28450000", "chr20_8930001-9410000") ) {
  1934. af_df <- read.table(paste("./betascan/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.recode", region, "alt_dmc_input.txt", sep="_"), header=FALSE) %>%
  1935. t(.) %>%
  1936. as.data.frame(.) %>%
  1937. rename_with(~c("amos", "bride", "long", "pat", "quon")) %>%
  1938. pivot_longer(cols=c("amos", "bride", "long", "pat", "quon"), names_to="lake", values_to="alt_af")
  1939. if ( region %like% "chr20" ) {
  1940. ymax <- 1000
  1941. } else {
  1942. ymax <- 500
  1943. }
  1944. bins <- c()
  1945. col_num <- 2
  1946. increment <- 0.02
  1947. for ( lake_file in all_lakes ) {
  1948. row_num <- 1
  1949. for (i in seq(0, 1, by = increment)) {
  1950. bins$bin[row_num] <- i
  1951. bins$count[row_num] <- nrow(subset(af_df, lake %in% lake_file & alt_af > (i - increment) & alt_af <= i))
  1952. row_num <- row_num + 1
  1953. }
  1954. bins <- data.frame(bins)
  1955. colnames(bins)[col_num] <- lake_file
  1956. col_num <- col_num + 1
  1957. }
  1958. bins_pivot <- pivot_longer(bins, cols=c("bride", "amos", "long", "quon", "pat"), names_to="lake", values_to="count") %>%
  1959. mutate(lake = factor(lake, levels=all_lakes))
  1960. af_hist <- ggplot(bins_pivot, aes(x=bin, y=count, color=lake)) +
  1961. geom_line() +
  1962. scale_x_continuous(name=paste("Alternate AF:", region), limits=c(0,1)) +
  1963. scale_y_continuous(name="Count", limits=c(0, ymax)) +
  1964. scale_color_manual(values=colors) +
  1965. theme_cowplot() +
  1966. theme(legend.position = "none")
  1967. assign(paste(region, "af_hist", sep="_"), af_hist)
  1968. }
  1969. `chr5_28200001-28450000_af_hist`
  1970. `chr20_8930001-9410000_af_hist`
  1971. af_hist_grid <- plot_grid(`chr5_28200001-28450000_af_hist`, `chr20_8930001-9410000_af_hist`, nrow=2)
  1972. ggsave(paste0(out_dir, "fst_pi_dxy_balancing_selection_regions_af_hists.pdf"), af_hist_grid, width=5, height=5)
  1973. #### Figure S4B & D - AF Landlocked vs. Anadromous in Balancing Selection Regions ####
  1974. for ( region in c("chr5_28200001-28450000", "chr20_8930001-9410000") ) {
  1975. b1_top <- get(paste0("chr", str_split_i(str_split_i(region, "_", 1), "chr", 2), "_zoom_b1_wider")) %>%
  1976. select(c("pos", "amos", "bride", "long", "pat", "quon")) %>%
  1977. filter(bride != 1) %>%
  1978. mutate(total_outliers = rowSums(.[,c("amos", "long", "quon", "pat")])) %>%
  1979. filter(total_outliers != 1)
  1980. af_df <- read.table(paste("./betascan/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.recode", region, "alt_dmc_input.txt", sep="_"), header=FALSE) %>%
  1981. t(.) %>%
  1982. as.data.frame(.) %>%
  1983. rename_with(~c("amos", "bride", "long", "pat", "quon")) %>%
  1984. mutate(pos = read_rds(paste("./betascan/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10.recode", region, "snps_pos_absolute.rds", sep="_"))) %>%
  1985. merge(., b1_top, by="pos", suffixes=c("_af", "_b1"), all.y=TRUE) %>%
  1986. filter(pos >= str_split_i(str_split_i(region, "_", 2), "-", 1) & pos <= str_split_i(region, "-", 2)) %>%
  1987. rename_with(~c("pos", "amos", "bride", "long", "pat", "quon"), c("pos", "amos_af", "bride_af", "long_af", "pat_af", "quon_af")) %>%
  1988. pivot_longer(cols=c("amos", "bride", "long", "pat", "quon"), names_to="lake", values_to="alt_af") %>%
  1989. filter((amos_b1 == 1 & lake == "amos") | (long_b1 == 1 & lake == "long") | (quon_b1 == 1 & lake == "quon") | (pat_b1 == 1 & lake == "pat") | lake == "bride") %>%
  1990. mutate(lake = if_else(lake == "bride", "bride", "land"))
  1991. print(nrow(subset(af_df, lake != "bride")))
  1992. if ( region %like% "chr20" ) {
  1993. ymax <- 120
  1994. } else {
  1995. ymax <- 35
  1996. }
  1997. bins <- c()
  1998. col_num <- 2
  1999. increment <- 0.01
  2000. for ( lake_file in c("bride", "land") ) {
  2001. row_num <- 1
  2002. for (i in seq(0, 1, by = increment)) {
  2003. bins$bin[row_num] <- i
  2004. bins$count[row_num] <- nrow(subset(af_df, lake %in% lake_file & alt_af > (i - increment) & alt_af <= i))
  2005. row_num <- row_num + 1
  2006. }
  2007. bins <- data.frame(bins)
  2008. colnames(bins)[col_num] <- lake_file
  2009. col_num <- col_num + 1
  2010. }
  2011. bins_pivot <- pivot_longer(bins, cols=c("bride", "land"), names_to="lake", values_to="count") %>%
  2012. mutate(lake = factor(lake, levels=c("bride", "land")))
  2013. af_hist <- ggplot(bins_pivot, aes(x=bin, y=count, color=lake)) +
  2014. geom_line() +
  2015. scale_x_continuous(name=paste("Alternate AF:", region), limits=c(0,1)) +
  2016. scale_y_continuous(name="Count", limits=c(0, ymax)) +
  2017. scale_color_manual(values=c("#1855F2", "#74AB20")) +
  2018. theme_cowplot() +
  2019. theme(legend.position = "none")
  2020. assign(paste(region, "af_hist", sep="_"), af_hist)
  2021. print(sum(bins_pivot$count))
  2022. }
  2023. `chr5_28200001-28450000_af_hist`
  2024. `chr20_8930001-9410000_af_hist`
  2025. af_hist_grid <- plot_grid(`chr5_28200001-28450000_af_hist`, `chr20_8930001-9410000_af_hist`, nrow=2)
  2026. ggsave(paste0(out_dir, "balancing_selection_regions_af_hists_bride_v_land.pdf"), af_hist_grid, width=5, height=10)
  2027. ```
  2028. ``` {r Figure 6 - Osmoregulatory Genes}
  2029. ## Creating the full american shad annotation file
  2030. annotation_df <- read.delim("./GCF_018492685.1_fAloSap1.pri_genomic.gtf.gz", header = FALSE, sep = "\t", skip = 4,
  2031. col.names=c("chr", "type", "type2", "start", "stop", "blank1", "strand", "blank2", "info")) %>%
  2032. filter(type %in% "Gnomon" & type2 %in% "gene") %>%
  2033. select(-c(blank1, blank2)) %>%
  2034. separate_wider_delim(info, delim="; ", too_few="align_start",
  2035. names=c("gene_name1", NA, "GeneID", "gbkey", "gene_name2", "gene_biotype", NA, NA)) %>%
  2036. mutate(gene_name1 = unlist(strsplit(gene_name1, split="gene_id\ "))[c(seq(2,2*nrow(.), 2))],
  2037. GeneID = unlist(strsplit(GeneID, split="db_xref GeneID:"))[c(seq(2,2*nrow(.), 2))],
  2038. gbkey = unlist(strsplit(gbkey, split="gbkey\ "))[c(seq(2,2*nrow(.), 2))],
  2039. gene_name2 = unlist(strsplit(gene_name2, split="gene\ "))[c(seq(2,2*nrow(.), 2))],
  2040. gene_biotype = unlist(strsplit(gene_biotype, split="gene_biotype\ "))[c(seq(2,2*nrow(.), 2))],
  2041. length = stop - start) %>%
  2042. filter(gene_name1 %in% gene_name2) %>%
  2043. select(-c(gene_name1, gbkey, gene_biotype)) %>%
  2044. rename_with(~c("ID", "name"), c(GeneID, gene_name2)) %>%
  2045. mutate(index = row_number(.))
  2046. ## Make new osmo file with added gene names from the .gtf file
  2047. gtf_osmo_genes <- read.table("./velotta22_osmoregulatory_genes_gtf.txt", header=FALSE) %>%
  2048. rename_with(~c("name", "category"), c(1, 2)) %>%
  2049. mutate(name = if_else(name %like% "LOC", name, tolower(name))) %>%
  2050. merge(., annotation_df, by="name") %>%
  2051. select(c("chr", "start", "stop", "length", "strand", "ID", "name", "category"))
  2052. ## Site-level FST for below
  2053. data_list <- lapply(list.files(path="/Users/corcorri/Documents/Denver/Manuscript/popgen_stats/pixy_files/sitelevel/fst/", pattern="fst", full.names=TRUE),
  2054. function(file) { read.table(file, header=TRUE, sep="\t", stringsAsFactors=FALSE) })
  2055. fst_sitelevel <- do.call(rbind, data_list) %>%
  2056. pivot_wider(., id_cols=c("chromosome", "window_pos_1", "window_pos_2"), names_from=c("pop1", "pop2"), values_from="avg_wc_fst") %>%
  2057. rename_with(tolower) %>%
  2058. select(contains(c("chr", "window_pos_1", "bride")))
  2059. for ( lake in l_lakes ) {
  2060. lake_fst <- cbind(fst_sitelevel[,1:2], fst_sitelevel[,grepl(lake, colnames(fst_sitelevel))]) %>%
  2061. rename_with(~c("chr", "pos", "fst")) %>%
  2062. filter(!is.na(fst)) %>%
  2063. mutate(fst = as.numeric(fst))
  2064. assign(x=paste(lake, "fst", sep="_"), value=lake_fst)
  2065. ## Add columns for average fst within osmo genes
  2066. if (lake == "amos") { colnum <- 9 }
  2067. else if (lake == "long") { colnum <- 10 }
  2068. else if (lake == "quon") { colnum <- 11 }
  2069. else if (lake == "pat") { colnum <- 12 }
  2070. all_gene_snps <- c()
  2071. for (i in 1:nrow(gtf_osmo_genes)) {
  2072. ## Find the average FST per gene
  2073. gtf_osmo_genes[i, colnum] <- mean(subset(lake_fst, chr %in% gtf_osmo_genes$chr[i] &
  2074. pos >= gtf_osmo_genes$start[i] &
  2075. pos <= gtf_osmo_genes$stop[i])$fst, na.rm=TRUE)
  2076. ## Made a dataframe of all SNPs within a gene
  2077. all_gene_snps <- rbind(all_gene_snps, subset(lake_fst, chr %in% gtf_osmo_genes$chr[i] &
  2078. pos >= gtf_osmo_genes$start[i] &
  2079. pos <= gtf_osmo_genes$stop[i]))
  2080. }
  2081. colnames(gtf_osmo_genes)[colnum] <- paste(lake, "fst", sep="_")
  2082. all_gene_snps <- all_gene_snps[!duplicated(all_gene_snps[1:3]),]
  2083. assign(x=paste(lake, "all_osmo_snps", sep="_"), value=all_gene_snps)
  2084. }
  2085. rm(colnum, lake_fst, all_gene_snps)
  2086. #### Calculating pairwise outliers: ####
  2087. for (lake in l_lakes ) {
  2088. ## First must pull the site-level data of interest
  2089. ohana_selscan <- read.table(paste("./ohana/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_bride", lake,
  2090. "maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_MAF001_geno01_scan_lle-ratios.txt", sep="_"), header=TRUE, sep="\t")[,-1]
  2091. ohana_map <- read.table(paste("./ohana/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_bride", lake,
  2092. "maxMeanDP16_softF_rmA28Q17Q1_maxMissing75_HWE1e-10_MAF001_geno01.map", sep="_"), header=FALSE, sep="\t")[,-c(2:3)]
  2093. ohana_pos <- cbind(ohana_selscan, ohana_map)[,c(6,7,1)] %>%
  2094. rename_with(~c("chr", "pos", "lle_ratio")) %>%
  2095. merge(., chroms, by="chr") %>%
  2096. mutate(SNP = paste('SNP',1:nrow(.), sep="_"),
  2097. chr_num <- as.factor(chr_num))
  2098. assign(paste(lake, "ohana_snps", sep="_"), ohana_pos, pos=1)
  2099. ## Running through iterations to match outlier windows to osmo genes
  2100. iter_count <- 10000
  2101. ohana_quantile <- 0.99
  2102. ohana_quantile_name <- as.character((1-ohana_quantile)*100)
  2103. # Pulling only the top 1% of SNPs per lake
  2104. top_snps <- subset(ohana_pos, lle_ratio >= quantile(ohana_pos$lle_ratio, ohana_quantile, na.rm = TRUE))
  2105. # Calculate the expected # of outlier SNPs and observed number of outlier SNPs in the osmo genes
  2106. gtf_osmo_genes$exp <- gtf_osmo_genes$length / sum(chrom_length$len) * nrow(top_snps)
  2107. for ( i in 1:nrow(gtf_osmo_genes) ) {
  2108. gene_start <- gtf_osmo_genes$start[i] # get gene position
  2109. gene_end <- gtf_osmo_genes$stop[i] # get gene position
  2110. gene_chr <- gtf_osmo_genes$chr[i] # get gene chromosome
  2111. gtf_osmo_genes$obs[i] <- nrow(subset(top_snps, chr %in% gene_chr & pos > gene_start & pos < gene_end))
  2112. gtf_osmo_genes$total_snps[i] <- nrow(subset(ohana_pos, chr %in% gene_chr & pos > gene_start & pos < gene_end))
  2113. gtf_osmo_genes$mean_llr[i] <- mean(subset(ohana_pos, chr %in% gene_chr & pos > gene_start & pos < gene_end)$lle_ratio, na.rm=TRUE)
  2114. gtf_osmo_genes$cum_llr[i] <- sum(subset(ohana_pos, chr %in% gene_chr & pos > gene_start & pos < gene_end)$lle_ratio, na.rm=TRUE)
  2115. }
  2116. assign(x=paste(lake, "gtf_overlap", sep = "_"),
  2117. value=gtf_osmo_genes %>%
  2118. mutate(fold_change = obs / exp,
  2119. lake = lake) %>%
  2120. select(c(1:8, "lake", "total_snps", "exp", "obs", "fold_change", "mean_llr", "cum_llr", 9:12)))
  2121. # Are there significantly different SNPs than expected in osmoregulatory genes (chi-square test)?
  2122. if ( sum(gtf_osmo_genes$obs) > 0 ) {
  2123. print(paste("ohana obs > exp:", lake))
  2124. print(chisq.test(x=gtf_osmo_genes$obs, p=(gtf_osmo_genes$exp / sum(gtf_osmo_genes$exp))))
  2125. }
  2126. # Same question, but with a 1-tail t-test (are there *more* observed SNPs than expected)
  2127. if ( sum(gtf_osmo_genes$obs) > 0 ) {
  2128. print(t.test(x=gtf_osmo_genes$obs, y=gtf_osmo_genes$exp, alternative="g", paired = TRUE))
  2129. }
  2130. ### Do the osmo genes contain more outlier SNPs than another random set of genes?
  2131. # 1) Calculate number of outlier SNPs within each gene through the whole annotation file
  2132. annotation_df$exp <- annotation_df$length / sum(chrom_length$len) * nrow(top_snps)
  2133. for ( i in 1:nrow(annotation_df) ) {
  2134. if ( i%%1000 == 0 ) { print(paste(lake, "Row", i, "out of", nrow(annotation_df))) }
  2135. annotation_df$obs[i] <- nrow(subset(top_snps, chr %in% annotation_df$chr[i] &
  2136. pos > annotation_df$start[i] & pos < annotation_df$stop[i]))
  2137. annotation_df$total_snps[i] <- nrow(subset(ohana_pos, chr %in% annotation_df$chr[i] &
  2138. pos > annotation_df$start[i] & pos < annotation_df$stop[i]))
  2139. annotation_df$mean_llr[i] <- mean(subset(ohana_pos, chr %in% annotation_df$chr[i] &
  2140. pos > annotation_df$start[i] &
  2141. pos < annotation_df$stop[i])$lle_ratio, na.rm=TRUE)
  2142. annotation_df$cum_llr[i] <- sum(subset(ohana_pos, chr %in% annotation_df$chr[i] &
  2143. pos > annotation_df$start[i] &
  2144. pos < annotation_df$stop[i])$lle_ratio, na.rm=TRUE)
  2145. }
  2146. assign(x=paste(lake, "annotation_df", sep="_"), value=annotation_df)
  2147. ### Are there more osmo genes with outliers than expected from a random set of 76 genes?
  2148. # 1) Calculate number of SNPs within each gene through the whole annotation file
  2149. # [DONE ABOVE]
  2150. # 2) Sum up total number of SNPs in obs column for a random sample of 82 genes
  2151. # 3) Repeat X times to get a normal distribution of SNP counts within 82 genes
  2152. random_dist_gene <- lapply(1:iter_count, function(iter){
  2153. if ( iter %% 500 == 0 ) { print(paste("Iteration", iter, "of", iter_count, "for", lake)) }
  2154. rand_genes <- sample(1:nrow(annotation_df), nrow(gtf_osmo_genes), replace = FALSE)
  2155. rand_genes_annotation <- subset(annotation_df, index %in% rand_genes)
  2156. ## Various values of interest to return
  2157. total_genes_with <- nrow(subset(rand_genes_annotation, obs >= 1))
  2158. ## Calculate mean LLR from the SNPs (to avoid taking a mean of gene's mean LLR)
  2159. rand_genes_snps_df <- c()
  2160. for ( i in rand_genes ) {
  2161. rand_genes_snps_df <- rbind(rand_genes_snps_df, subset(ohana_pos, chr %in% annotation_df$chr[i] &
  2162. pos >= annotation_df$start[i] &
  2163. pos <= annotation_df$stop[i]))
  2164. }
  2165. rand_genes_snps_df <- unique(rand_genes_snps_df)
  2166. mean_llr <- mean(rand_genes_snps_df$lle_ratio, na.rm = TRUE)
  2167. return(c(total_genes_with, mean_llr))
  2168. })
  2169. random_dist_gene_df <- as.data.frame(do.call(rbind,random_dist_gene)) %>%
  2170. rename_with(~c("total_genes_with", "mean_llr"))
  2171. assign(x=paste(lake, "rand_gene_count", sep = "_"), value=random_dist_gene_df)
  2172. ## Take a subsample of the genes
  2173. assign(x=paste(lake, "rand_gene_subsample", sep = "_"), value=merge(random_dist_gene_df %>% mutate(index = row.names(.)),
  2174. data.frame("index"=sample(1:nrow(random_dist_gene_df), 1000, replace = FALSE)), by="index"))
  2175. ## Are genes in annotation_df with obs >= 1 enriched for GO terms?
  2176. assign(x=paste(lake, "go_with", sep="_"), value=gost(unique(subset(annotation_df, obs >= 1)$name),
  2177. organism = "charengus",
  2178. significant = FALSE,
  2179. correction_method = "g_SCS",
  2180. domain_scope = "custom", custom_bg = unique(annotation_df$name)))
  2181. }
  2182. View(subset(amos_gtf_overlap, obs >= 1))
  2183. View(subset(long_gtf_overlap, obs >= 1))
  2184. View(subset(pat_gtf_overlap, obs >= 1))
  2185. View(subset(quon_gtf_overlap, obs >= 1))
  2186. View(amos_go_with$result)
  2187. View(long_go_with$result)
  2188. View(pat_go_with$result)
  2189. # significant p_value term_size query_size intersection_size precision recall term_id source term_name effective_domain_size source_order parents
  2190. # TRUE 0.04971564 4 590 3 0.005084746 0.75000000 GO:0072176 GO:BP nephric duct development 16599 16973 c("GO:0035295", "GO:0072073")
  2191. # TRUE 0.04971564 4 590 3 0.005084746 0.75000000 GO:0039022 GO:BP pronephric duct development 16599 9700 c("GO:0048793", "GO:0072176")
  2192. View(quon_go_with$result)
  2193. # significant p_value term_size query_size intersection_size precision recall term_id source term_name effective_domain_size source_order parents
  2194. # TRUE 0.004024059 60 509 10 0.019646365 0.166666667 GO:0001525 GO:BP angiogenesis 16599 328 c("GO:0048514", "GO:0048646")
  2195. # TRUE 0.008733188 79 509 11 0.021611002 0.139240506 GO:0048514 GO:BP blood vessel morphogenesis 16599 12632 c("GO:0001568", "GO:0035239")
  2196. # TRUE 0.034639468 26 509 6 0.011787819 0.230769231 GO:0002040 GO:BP sprouting angiogenesis 16599 638 GO:0001525
  2197. ## Merge all osmo outlier info into the same dataframe to compare across lakes - osmo_rbind
  2198. osmo_rbind <- rbind(amos_gtf_overlap, long_gtf_overlap, pat_gtf_overlap, quon_gtf_overlap) %>%
  2199. pivot_longer(., cols=c("amos_fst", "long_fst", "pat_fst", "quon_fst"), names_to=c("lake2", NA), names_sep="_", values_to="mean_fst") %>%
  2200. filter(lake == lake2) %>%
  2201. select(-c("lake2"))
  2202. ## Chi-square test for all osmoregulatory genes
  2203. chisq.test(x=osmo_rbind$obs, p=(osmo_rbind$exp / sum(osmo_rbind$exp)))
  2204. # X-squared = 855.98, df = 327, p-value < 2.2e-16
  2205. t.test(x=osmo_rbind$obs, y=osmo_rbind$exp, alternative="g", paired = TRUE)
  2206. # t = 2.2535, df = 327, p-value = 0.01244
  2207. ### PLOTTING HISTOGRAMS ###
  2208. for ( lake_file in l_lakes ) local({
  2209. hist_df <- get(paste0(lake_file, "_rand_gene_count")) %>%
  2210. mutate(mean_llr = as.numeric(mean_llr))
  2211. hist_color <- get(paste0(lake_file, "_col"))
  2212. print(paste(lake_file, "genes w/ obs >= 1:", range(hist_df$total_genes_with)[1], "-", range(hist_df$total_genes_with)[2]))
  2213. print(paste(lake_file, "mean LLR:", range(hist_df$mean_llr)[1], "-", range(hist_df$mean_llr)[2]))
  2214. print("")
  2215. ## Genes with obs >= 1
  2216. lake_file <- lake_file
  2217. top_5_with <- quantile(hist_df$total_genes_with, 0.95)
  2218. histogram_with <<- ggplot(hist_df, aes(x=total_genes_with, color="hist_color", fill="hist_color")) +
  2219. geom_histogram(binwidth=1) +
  2220. annotate("segment", y=0, yend=3500, x=top_5_with, xend=top_5_with, color="red", linewidth=1) +
  2221. geom_point(data=data.frame(), aes(y=0, x=nrow(subset(osmo_rbind, lake == lake_file & obs >= 1)), color="arrow"), size=5, shape=6) +
  2222. scale_color_manual(values=c("hist_color"=hist_color, "red", "arrow"="black")) +
  2223. scale_fill_manual(values=c("hist_color"=hist_color)) +
  2224. scale_x_continuous(name="# Genes with Outlier SNPs (out of 82 genes)", limits=c(-1, 14)) +
  2225. scale_y_continuous(name=lake_file, limits=c(0,3500)) +
  2226. theme_cowplot() +
  2227. theme(legend.position = "none")
  2228. ## Mean LLR
  2229. osmo_genes_snps_df <- c()
  2230. for ( i in 1:nrow(as.data.frame(gtf_osmo_genes)) ) {
  2231. osmo_genes_snps_df <- rbind(osmo_genes_snps_df, subset(get(paste(lake_file, "ohana_snps", sep="_")), chr %in% gtf_osmo_genes$chr[i] &
  2232. pos >= gtf_osmo_genes$start[i] &
  2233. pos <= gtf_osmo_genes$stop[i]))
  2234. }
  2235. osmo_genes_snps_df <- osmo_genes_snps_df[-1,]
  2236. top_5_mean_llr <- quantile(hist_df$mean_llr, 0.95)
  2237. histogram_meanllr <<- ggplot(hist_df, aes(x=mean_llr, color="hist_color", fill="hist_color")) +
  2238. geom_histogram(binwidth=0.017) +
  2239. annotate("segment", y=0, yend=400, x=top_5_mean_llr, xend=top_5_mean_llr, color="red", linewidth=1) +
  2240. geom_point(data=data.frame(), aes(y=0, x=mean(osmo_genes_snps_df$lle_ratio, na.rm=TRUE), color="arrow"), size=5, shape=6) +
  2241. scale_color_manual(values=c("hist_color"=hist_color, "red", "arrow"="black")) +
  2242. scale_fill_manual(values=c("hist_color"=hist_color)) +
  2243. scale_x_continuous(name="Mean LLR across 82 Random Genes", limits=c(-0.017, 3.4)) +
  2244. scale_y_continuous(name=lake_file, limits=c(0,400)) +
  2245. theme_cowplot() +
  2246. theme(legend.position = "none")
  2247. ## Remove the bottom axes for all graphs but Pat for vertical gridding
  2248. if (lake_file != "pat") {
  2249. histogram_with <<- histogram_with +
  2250. theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank())
  2251. histogram_meanllr <<- histogram_meanllr +
  2252. theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank())
  2253. }
  2254. ## Save the histogram files
  2255. assign(x=paste(lake_file, "hist_meanllr", sep="_"), value=histogram_meanllr, pos=1)
  2256. assign(x=paste(lake_file, "hist_with", sep="_"), value=histogram_with, pos=1)
  2257. })
  2258. ## Plot the histograms as grids
  2259. hist_overlay_with <- plot_grid(amos_hist_with, NULL, long_hist_with, NULL, quon_hist_with, NULL, pat_hist_with,
  2260. rel_heights=c(0.9, -0.3, 0.9, -0.3, 0.9, -0.3, 0.9), nrow=7, align="hv")
  2261. hist_overlay_with
  2262. hist_overlay_mean_llr <- plot_grid(amos_hist_meanllr, NULL, long_hist_meanllr, NULL, quon_hist_meanllr, NULL, pat_hist_meanllr,
  2263. rel_heights=c(0.9, -0.3, 0.9, -0.3, 0.9, -0.3, 0.9), nrow=7, align="hv")
  2264. hist_overlay_mean_llr
  2265. histogram_grid <- plot_grid(hist_overlay_with, hist_overlay_mean_llr, nrow=1)
  2266. histogram_grid
  2267. ### Writing tables of the osmoregulatory gene overlaps
  2268. osmo_rbind <- osmo_rbind[with(osmo_rbind, order(name,lake)),] %>%
  2269. merge(., chroms, by="chr") %>%
  2270. relocate(all_of(colnames(.)[1:length(.)-1]), .after=chr_num)
  2271. write.table(file=paste0("./ohana/pairwise_all_gtf_overlap_00", ohana_quantile_name, "_all_genes.txt"), x=osmo_rbind, row.names = FALSE, col.names = TRUE, sep="\t", quote=FALSE)
  2272. #### Figure 6C - Osmoregulatory gene outlier UpSet plot ####
  2273. library(ComplexUpset)
  2274. osmo_outliers <- subset(osmo_rbind, obs >= 1) %>%
  2275. mutate(keep = 1) %>%
  2276. pivot_wider(., id_cols=c("chr_num", "chr", "start", "stop", "length", "strand", "ID", "name", "category"), names_from="lake", values_from=c("exp", "obs", "total_snps", "mean_llr", "cum_llr", "fold_change", "keep")) %>%
  2277. rename_with(~c("amos", "long", "pat", "quon"), c("keep_amos", "keep_long", "keep_pat", "keep_quon")) %>%
  2278. mutate(category = factor(category, levels=c("cotransporters", "pumps", "osmosensor", "junction", "regulatory", "hormones", "cortisol", "secondary")))
  2279. osmo_outliers[,c(34:37)][is.na(osmo_outliers[,c(34:37)])] <- 0
  2280. shared_genes <- subset(osmo_outliers, (long + quon + pat + amos) >= 2)$name
  2281. size = get_size_mode('exclusive_intersection')
  2282. upset_order <- colnames(osmo_outliers)[c(37,35,34,36)]
  2283. window_upset <- upset(osmo_outliers, upset_order,
  2284. base_annotations=list(
  2285. 'Intersection size'=intersection_size(
  2286. text_mapping=aes(label=!!size,
  2287. color="black", y=(!!size + 0.75)),
  2288. mapping=aes(fill=category)
  2289. ) +
  2290. ylab("Candidate Osmoregulatory Gene Outliers") +
  2291. ylim(0,10) +
  2292. annotate("text", label=shared_genes[3], x=5, y=0.5, size=4, color="black") +
  2293. annotate("text", label=shared_genes[4], x=6, y=0.5, size=4, color="black") +
  2294. annotate("text", label=shared_genes[2], x=8, y=0.5, size=4, color="black") +
  2295. annotate("text", label=shared_genes[1], x=8, y=1.5, size=4, color="black") +
  2296. scale_fill_discrete(name="Functional Group")
  2297. ),
  2298. intersections=list("amos", "long", "quon", "pat",
  2299. c("amos", "long"), c("amos", "quon"), c("amos", "pat"),
  2300. c("long", "quon"), c("long", "pat"), c("quon", "pat"),
  2301. c("amos", "long", "quon"), c("amos", "long", "pat"), c("amos", "quon", "pat"), c("long", "quon", "pat"),
  2302. c("amos", "long", "quon", "pat")
  2303. ),
  2304. matrix=(
  2305. intersection_matrix(
  2306. outline_color=list(active="#909190", inactive="#EBEBEB"),
  2307. geom=geom_point(size=3))
  2308. ),
  2309. queries=list(
  2310. upset_query(set='amos', fill=amos_col),
  2311. upset_query(set='long', fill=long_col),
  2312. upset_query(set='pat', fill=pat_col),
  2313. upset_query(set='quon', fill=quon_col),
  2314. upset_query(intersect=c('amos'), fill='#C0BEBE', color='#C0BEBE', only_components=c('intersections_matrix')),
  2315. upset_query(intersect=c('long'), fill='#C0BEBE', color='#C0BEBE', only_components=c('intersections_matrix')),
  2316. upset_query(intersect=c('quon'), fill='#C0BEBE', color='#C0BEBE', only_components=c('intersections_matrix')),
  2317. upset_query(intersect=c('pat'), fill='#C0BEBE', color='#C0BEBE', only_components=c('intersections_matrix')),
  2318. upset_query(intersect=c('amos', 'long'), fill='#909190', color='#909190',
  2319. only_components=c('intersections_matrix')),
  2320. upset_query(intersect=c('amos', 'quon'), fill='#909190', color='#909190',
  2321. only_components=c('intersections_matrix')),
  2322. upset_query(intersect=c('amos', 'pat'), fill='#909190', color='#909190',
  2323. only_components=c('intersections_matrix')),
  2324. upset_query(intersect=c('long', 'quon'), fill='#909190', color='#909190',
  2325. only_components=c('intersections_matrix')),
  2326. upset_query(intersect=c('long', 'pat'), fill='#909190', color='#909190',
  2327. only_components=c('intersections_matrix')),
  2328. upset_query(intersect=c('quon', 'pat'), fill='#909190', color='#909190',
  2329. only_components=c('intersections_matrix')),
  2330. upset_query(intersect=c('amos', 'long', 'quon'), fill='#5C5D5E', color='#5C5D5E',
  2331. only_components=c('intersections_matrix')),
  2332. upset_query(intersect=c('amos', 'long', 'pat'), fill='#5C5D5E', color='#5C5D5E',
  2333. only_components=c('intersections_matrix')),
  2334. upset_query(intersect=c('amos', 'quon', 'pat'), fill='#5C5D5E', color='#5C5D5E',
  2335. only_components=c('intersections_matrix')),
  2336. upset_query(intersect=c('long', 'quon', 'pat'), fill='#5C5D5E', color='#5C5D5E',
  2337. only_components=c('intersections_matrix')),
  2338. upset_query(intersect=c('amos', 'long', 'quon', 'pat'), fill='#272928', color='#272928',
  2339. only_components=c('intersections_matrix'))
  2340. ),
  2341. set_sizes=(
  2342. upset_set_size() + theme_cowplot() + theme(axis.title.y=element_blank(), axis.text.y=element_blank(), strip.text.y=element_blank(), axis.ticks.y=element_blank(), axis.line.y=element_blank())
  2343. ),
  2344. themes=list('Intersection size'=list(theme_cowplot(), theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(), strip.text.x = element_blank())),
  2345. 'intersections_matrix'=list(theme_cowplot(), theme(axis.text.x=element_blank(), strip.text.x = element_blank(), axis.title=element_blank(), axis.ticks=element_blank(), axis.line=element_blank()))
  2346. ),
  2347. name=NULL, keep_empty_groups=TRUE, width_ratio = 0.15, stripes=c('#F5F5F5', 'white'), sort_sets=FALSE, sort_intersections=FALSE
  2348. )
  2349. window_upset
  2350. ## Combine all three plots and save it
  2351. fig5_grid <- plot_grid(histogram_grid, window_upset, nrow=2, rel_heights=c(1, 0.75))
  2352. fig5_grid
  2353. ggsave(paste0(out_dir, "filtering_redone_pairwise_osmo_outliers_00", ohana_quantile_name, "_histograms_complexUpset_at_least_one.pdf"), fig5_grid, width=12, height=10)
  2354. ```
  2355. ``` {r Figure S5 - Protocadherin RNAseq}
  2356. #### featureCounts
  2357. protocadherin_genes_chr5 <- c("LOC121709124", "LOC121709120", "LOC121709123", "LOC121710140", "LOC121708907", "LOC121708908", "LOC121709757", "LOC121709523") #, ## within dxy/pi outlier windows
  2358. protocadherin_genes_chr20 <- c("si:ch73-233f7.1", "LOC121693912", "LOC121693929", "LOC121693920", "LOC121693922", "LOC121693910", "LOC121693917", "LOC121693930", "LOC121694333", "LOC121693925", "LOC121694863", "LOC121693926", "pcdh2g28", "LOC121693927", "LOC121693914", "LOC121693918", "LOC121693921", "LOC121693923", "LOC121693916", "LOC121693928", "LOC121693915", "LOC121693924", "LOC121693932") ## within dxy/pi outlier windows
  2359. # tissue_list <- c("SRR14511793", "SRR14511794", "SRR14511795", "SRR14511796", "SRR14511797") ## IDs
  2360. tissue_list <- c("ovary", "testis", "muscle", "liver", "brain") ## Names
  2361. ## Load in the featureCounts results
  2362. fc <- read.table("./rnaseq/all_tissues_featureCounts_fastp.txt", skip=1, header=TRUE, sep="\t") %>%
  2363. rename_with(~tissue_list, 7:11)
  2364. for ( tissue in tissue_list ) {
  2365. tissue_col <- paste(tissue, "CPM", sep="_")
  2366. library_size <- sum(fc[[tissue]])
  2367. fc[[tissue_col]] <- as.numeric(fc[[tissue]] / (library_size / 1e6))
  2368. ## Histogram of counts per tissue
  2369. bin <- c()
  2370. row_num <- 1
  2371. increment <- 40
  2372. for (i in seq(0, 1000, by = increment)) {
  2373. bin$bin[row_num] <- i
  2374. bin$count[row_num] <- nrow(fc[ fc[[tissue_col]] > (i - increment) & fc[[tissue_col]] <= i, ])
  2375. row_num <- row_num + 1
  2376. }
  2377. bin <- data.frame(bin)
  2378. colnames(bin)[2] <- tissue
  2379. assign(paste(tissue, "bin", sep="_"), bin)
  2380. }
  2381. colnames(fc)[7:11] <- paste(tissue_list, "counts", sep="_")
  2382. ## Plotting the bins histogram
  2383. for ( chr in c(5, 20)) {
  2384. fc_protocadherins <- fc[0,0]
  2385. protocadherins_gene_list <- get(paste0("protocadherin_genes_chr", chr))
  2386. for ( gene in 1:length(protocadherins_gene_list)) {
  2387. fc_tmp <- subset(fc, Geneid %like% protocadherins_gene_list[gene])
  2388. fc_protocadherins <- rbind(fc_protocadherins, fc_tmp)
  2389. }
  2390. rm(fc_tmp)
  2391. fc_protocadherins_pivot <- pivot_longer(fc_protocadherins, cols=c(7:16), names_to=c("tissue", ".value"), names_sep="_")
  2392. ## Any difference in tissues across all protocadherins in the region?
  2393. print(summary(aov(lm(CPM ~ tissue, fc_protocadherins_pivot))))
  2394. ## No significant difference
  2395. # 5a Df Sum Sq Mean Sq F value Pr(>F)
  2396. # tissue 4 1284 320.9 0.51 0.729
  2397. # Residuals 35 22037 629.6
  2398. # 20a Df Sum Sq Mean Sq F value Pr(>F)
  2399. # tissue 4 2033 508.4 1.138 0.342
  2400. # Residuals 110 49120 446.5
  2401. ## Any difference when considering only "highly" expressed genes?
  2402. fc_protocadherin_aov <- aov(lm(CPM ~ tissue, subset(fc_protocadherins_pivot, CPM >= 1)))
  2403. print(summary(fc_protocadherin_aov))
  2404. ## No significant difference in chr5, minorly significanty in chr20
  2405. # 5a Df Sum Sq Mean Sq F value Pr(>F)
  2406. # tissue 4 3046 761.4 0.478 0.752
  2407. # Residuals 8 12757 1594.6
  2408. # 20a Df Sum Sq Mean Sq F value Pr(>F)
  2409. # tissue 4 14839 3710 3.535 0.0256 *
  2410. # Residuals 19 19939 1049
  2411. print(TukeyHSD(fc_protocadherin_aov))
  2412. # 5a diff lwr upr p adj
  2413. # liver-brain 5.632094 -120.3032 131.56734 0.9998413
  2414. # muscle-brain -28.978342 -154.9136 96.95691 0.9250293
  2415. # ovary-brain -37.075699 -163.0109 88.85955 0.8409883
  2416. # testis-brain -21.402885 -126.7679 83.96210 0.9503936
  2417. # muscle-liver -34.610436 -172.5656 103.34472 0.9013523
  2418. # ovary-liver -42.707793 -180.6629 95.24736 0.8169564
  2419. # testis-liver -27.034979 -146.5076 92.43769 0.9289930
  2420. # ovary-muscle -8.097357 -146.0525 129.85780 0.9995347
  2421. # testis-muscle 7.575457 -111.8972 127.04812 0.9993693
  2422. # testis-ovary 15.672814 -103.7999 135.14548 0.9896162
  2423. # 20a diff lwr upr p adj
  2424. # liver-brain 14.879158 -59.52457 89.282883 0.9731598
  2425. # muscle-brain -48.285400 -122.68913 26.118326 0.3257146
  2426. # ovary-brain -52.207553 -136.57345 32.158342 0.3703071
  2427. # testis-brain -46.543674 -102.78760 9.700256 0.1351403
  2428. # muscle-liver -63.164557 -142.70549 16.376371 0.1615139
  2429. # ovary-liver -67.086710 -156.01617 21.842751 0.1981560
  2430. # testis-liver -61.422832 -124.30546 1.459794 0.0575086
  2431. # ovary-muscle -3.922153 -92.85161 85.007308 0.9999233
  2432. # testis-muscle 1.741726 -61.14090 64.624351 0.9999880
  2433. # testis-ovary 5.663879 -68.73985 80.067604 0.9993323
  2434. ## Sum expression from each region of genes per tissue
  2435. tissue_fc_bar_stack <- ggplot(fc_protocadherins_pivot, aes(x=tissue, y=CPM, color=Geneid, fill=Geneid)) +
  2436. geom_col(position="stack") +
  2437. scale_y_continuous(name="Counts per Million (CPM)", limits=c(0,275)) +
  2438. scale_x_discrete(name="Tissue") +
  2439. theme_cowplot()
  2440. tissue_fc_bar_stack
  2441. ## Alternatively - bar chart not stacked
  2442. tissue_fc_bar_dodge <- ggplot(fc_protocadherins_pivot, aes(x=Geneid, y=CPM, color=tissue, fill=tissue)) +
  2443. geom_col(position="dodge") +
  2444. scale_y_continuous(name="Counts per Million (CPM)", limits=c(0,130)) +
  2445. scale_x_discrete(name=paste(chr, "Protocadherins")) +
  2446. theme_cowplot() +
  2447. theme(axis.text.x = element_text(angle = 45, vjust=1, hjust=1))
  2448. tissue_fc_bar_dodge
  2449. assign(paste0("fc_protocadherins_pivot_chr", chr), fc_protocadherins_pivot)
  2450. assign(paste0("fc_protocadherins_plots_chr", chr), plot_grid(tissue_fc_bar_stack, tissue_fc_bar_dodge, nrow=1, rel_widths=c(0.75,1)))
  2451. }
  2452. fc_protocadherins_plots_chr5
  2453. fc_protocadherins_plots_chr20
  2454. fc_protocadherins_plotgrid <- plot_grid(fc_protocadherins_plots_chr5, fc_protocadherins_plots_chr20, nrow=2, align="h", axis="b")
  2455. fc_protocadherins_plotgrid
  2456. ggsave(paste0(out_dir, "fc_protocadherins_by_region_tissue.pdf"), fc_protocadherins_plotgrid, width=20, height=10)
  2457. ## All protocadherin genes:
  2458. fc_protocadherins_pivot <- rbind(fc_protocadherins_pivot_chr5, fc_protocadherins_pivot_chr20)
  2459. summary(aov(lm(CPM ~ tissue, fc_protocadherins_pivot)))
  2460. ## No significant difference in the average count between tissues when considering all protocadherins
  2461. # Df Sum Sq Mean Sq F value Pr(>F)
  2462. # tissue 4 3229 807.1 1.691 0.155
  2463. # Residuals 150 71578 477.2
  2464. fc_protocadherin_aov <- aov(lm(CPM ~ tissue, subset(fc_protocadherins_pivot, CPM >= 1)))
  2465. summary(fc_protocadherin_aov)
  2466. ## Yes significant difference in the average count between tissues when only considering "highly" expressed genes
  2467. # Df Sum Sq Mean Sq F value Pr(>F)
  2468. # tissue 4 16414 4103 3.843 0.0116 *
  2469. # Residuals 32 34166 1068
  2470. TukeyHSD(fc_protocadherin_aov)
  2471. # diff lwr upr p adj
  2472. # liver-brain 11.638750 -43.64357 66.921070 0.9727458
  2473. # muscle-brain -40.104159 -95.38648 15.178162 0.2465009
  2474. # ovary-brain -45.787670 -104.96386 13.388519 0.1927987
  2475. # testis-brain -37.393367 -80.17768 5.390947 0.1100467
  2476. # muscle-liver -51.742909 -111.45464 7.968822 0.1149827
  2477. # ovary-liver -57.426420 -120.76027 5.907435 0.0904077
  2478. # testis-liver -49.032117 -97.40415 -0.660086 0.0456821 *
  2479. # ovary-muscle -5.683511 -69.01737 57.650344 0.9989495
  2480. # testis-muscle 2.710792 -45.66124 51.082823 0.9998368
  2481. # testis-ovary 8.394303 -44.38391 61.172516 0.9903774
  2482. ```
  2483. ``` {r Figure S6 - imiss vs. idepth}
  2484. ## Missingness vs. Depth scatterplot
  2485. miss_depth_ggplot <- merge(read.table("./filtercheck_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_maxMissing75_HWE1e-10.imiss", header=TRUE, sep="\t"),
  2486. read.table("./filtercheck_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_maxMissing75_HWE1e-10.idepth", header=TRUE, sep="\t"),
  2487. by="INDV") %>%
  2488. mutate(lake = factor(tolower(gsub("([a-zA-Z]+)_[0-9]+", "\\1", INDV)), levels=all_lakes)) %>%
  2489. ggplot(aes(x=F_MISS, y=MEAN_DEPTH, color=lake, fill=lake, label=INDV)) +
  2490. geom_point() +
  2491. geom_text(hjust=-0.1, vjust=-0.5, size=3) +
  2492. scale_x_continuous(limits=c(0,1), name="Fraction Missing") +
  2493. scale_y_continuous(limits=c(0,20), name="Mean Depth") +
  2494. scale_color_manual(values=colors) +
  2495. scale_fill_manual(values=colors) +
  2496. theme_cowplot() +
  2497. theme(legend.position = "none")
  2498. ## Ancestry PCA
  2499. library(pcadapt)
  2500. allsites_newfilters <- read.pcadapt("./pcadapt/all_chrom_scaffolds_ashad_replaced_allsites_bylake-vc_gatkrecHardF_snps_maxMeanDP16_softF_maxMissing75_HWE1e-10.recode.bed", type="bed")
  2501. allsites_pcadapt <- pcadapt(allsites_newfilters, K=4, min.maf=0.01)
  2502. # With integers - Necessary if you want to be able to label your individuals/populations
  2503. poplist.names <- c(rep("Amos", 21), rep("Bride", 30), rep("Long", 20), rep("Pat", 19), rep("Quon", 20))
  2504. indiv_list <- c("Amos_10", "Amos_11", "Amos_12", "Amos_13", "Amos_16", "Amos_17", "Amos_18", "Amos_19", "Amos_20", "Amos_22", "Amos_23", "Amos_25", "Amos_26", "Amos_28", "Amos_3", "Amos_4", "Amos_5", "Amos_6", "Amos_7", "Amos_8", "Amos_9",
  2505. "Bride_1", "Bride_10", "Bride_11", "Bride_12", "Bride_13", "Bride_14", "Bride_15", "Bride_16", "Bride_17", "Bride_18", "Bride_19", "Bride_2", "Bride_20", "Bride_21", "Bride_22", "Bride_23", "Bride_24", "Bride_25", "Bride_26", "Bride_27", "Bride_28", "Bride_29", "Bride_3", "Bride_30", "Bride_4", "Bride_5", "Bride_6", "Bride_7", "Bride_8", "Bride_9",
  2506. "Long_11", "Long_12", "Long_14", "Long_15", "Long_16", "Long_18", "Long_19", "Long_2", "Long_20", "Long_22", "Long_23", "Long_25", "Long_26", "Long_27", "Long_28", "Long_3", "Long_30", "Long_7", "Long_8", "Long_9",
  2507. "Pat_1", "Pat_10", "Pat_11", "Pat_12", "Pat_13", "Pat_14", "Pat_15", "Pat_16", "Pat_17", "Pat_18", "Pat_19", "Pat_2", "Pat_3", "Pat_4", "Pat_5", "Pat_6", "Pat_7", "Pat_8", "Pat_9",
  2508. "Quon_1", "Quon_10", "Quon_12", "Quon_13", "Quon_17", "Quon_18", "Quon_19", "Quon_2", "Quon_20", "Quon_21", "Quon_25", "Quon_26", "Quon_27", "Quon_28", "Quon_3", "Quon_4", "Quon_5", "Quon_6", "Quon_8", "Quon_9")
  2509. allsites_pcadapt_scores <- cbind(as.data.frame.matrix(allsites_pcadapt$scores), poplist.names, indiv_list) %>%
  2510. rename_with(~c("pc1", "pc2", "pc3", "pc4", "lake", "indiv")) %>%
  2511. mutate(lake = factor(lake, levels=c("Bride", "Amos", "Long", "Quon", "Pat")))
  2512. ## Plotting the PCA
  2513. allsites_ggplot_12 <- ggplot(allsites_pcadapt_scores, aes(x=pc1, y=pc2, color=lake, fill=lake, label=indiv)) +
  2514. geom_hline(aes(yintercept=0), col="black") +
  2515. geom_vline(aes(xintercept=0), col="black") +
  2516. geom_point(size=2.2) +
  2517. geom_text(hjust=-0.1, vjust=-0.5, size=2) +
  2518. scale_color_manual(values=colors) +
  2519. scale_fill_manual(values=colors) +
  2520. scale_x_continuous(name=paste0("PC1 (", round(allsites_pcadapt$singular.values[1]^2*100, digits=2), "%)"),
  2521. limits=c(min(allsites_pcadapt_scores$pc1)-0.05, max(allsites_pcadapt_scores$pc1)+0.05)) +
  2522. scale_y_continuous(name=paste0("PC2 (", round(allsites_pcadapt$singular.values[2]^2*100, digits=2), "%)"),
  2523. limits=c(min(allsites_pcadapt_scores$pc2)-0.05, max(allsites_pcadapt_scores$pc2)+0.05)) +
  2524. theme_cowplot() +
  2525. theme(legend.text=element_text(size=15), legend.title=element_text(size=20), axis.text=element_text(size=10), axis.title=element_text(size=15))
  2526. ggplotly(allsites_ggplot_12)
  2527. imissidepth_pca <- plot_grid(miss_depth_ggplot, allsites_ggplot_12, nrow=1, rel_widths=c(1, 1.5))
  2528. ggsave(paste0(out_dir, "allsites_gatkrecHardF_snps_maxMeanDP16_softF_maxMissing75_HWE1e-10_ggplot_imiss_idepth_pcas.pdf"), imissidepth_pca, width=10, height=6)
  2529. ```
  2530. ``` {r Table S5 - SLiM and Ohana}
  2531. parameters_list <- c("bot_10x_s0.005", "bot_10x_s0.05", "bot_10x_s0.5",
  2532. "bot_100x_s0.005", "bot_100x_s0.05", "bot_100x_s0.5",
  2533. "bot_1000x_s0.005", "bot_1000x_s0.05", "bot_1000x_s0.5")
  2534. ## Calculate # of shared outliers per parameter combination
  2535. shared_outliers <- data.frame(cbind("V1" = c(4,3,2), matrix(ncol=9, nrow=3))) %>%
  2536. rename_with(~c("total_outliers", parameters_list))
  2537. for ( parameters in parameters_list ) {
  2538. for ( pop in c(1, 2, 3, 4) ) {
  2539. ## Load in all of the repetitions, and mark outliers & the repetition ir came from for merging across populations
  2540. for ( rep in seq(1:20) ) {
  2541. if ( rep == 1 ) {
  2542. ohana_selscan <- read.table(paste0("./slim/ohana/neutral_vcf_pop", pop, "_", parameters, "_rep", rep, "_maxMissing75_MAF001_geno01_subsample_selscan_50kb_10kb.txt"),
  2543. header=TRUE, sep="\t")[,-c(1,9)] %>%
  2544. mutate(outlier = ifelse(mean_lle_ratio >= quantile(mean_lle_ratio, 0.99), 1, 0),
  2545. rep = rep)
  2546. } else {
  2547. ohana_selscan <- rbind(ohana_selscan,
  2548. read.table(paste0("./slim/ohana/neutral_vcf_pop", pop, "_", parameters, "_rep", rep, "_maxMissing75_MAF001_geno01_subsample_selscan_50kb_10kb.txt"),
  2549. header=TRUE, sep="\t")[,-c(1,9)] %>%
  2550. mutate(outlier = ifelse(mean_lle_ratio >= quantile(mean_lle_ratio, 0.99), 1, 0),
  2551. rep = rep))
  2552. }
  2553. }
  2554. assign(paste0("pop", pop, "_selscan"), ohana_selscan)
  2555. }
  2556. all_ohana_selscan <- merge(pop1_selscan, pop2_selscan, by=c("window_pos_1", "window_pos_2", "rep"), all=TRUE, suffixes=c("_pop1", "_pop2")) %>%
  2557. merge(., pop3_selscan, by=c("window_pos_1", "window_pos_2", "rep"), all=TRUE) %>%
  2558. merge(., pop4_selscan, by=c("window_pos_1", "window_pos_2", "rep"), all=TRUE, suffixes=c("_pop3", "_pop4")) %>%
  2559. rowwise(.) %>%
  2560. mutate(total_outliers = rowSums(.[c("outlier_pop1", "outlier_pop2", "outlier_pop3", "outlier_pop4")]))
  2561. ## Count shared outliers
  2562. shared_outliers[[parameters]][1] <- nrow(subset(all_ohana_selscan, total_outliers == 4))
  2563. shared_outliers[[parameters]][2] <- nrow(subset(all_ohana_selscan, total_outliers == 3))
  2564. shared_outliers[[parameters]][3] <- nrow(subset(all_ohana_selscan, total_outliers == 2))
  2565. }
  2566. shared_outliers_circle <- pivot_longer(shared_outliers, cols=colnames(shared_outliers)[2:length(shared_outliers)], names_to="parameter", values_to="count") %>%
  2567. mutate(outliers_sel = paste(total_outliers, str_split_i(parameter, "s", 2), sep="_"),
  2568. bot_str = paste0(str_split_i(str_split_i(parameter, "_", 2), "x", 1), "x"),
  2569. count = as.numeric(count)) %>%
  2570. select(-c("total_outliers", "parameter")) %>%
  2571. pivot_wider(., id_cols="outliers_sel", names_from="bot_str", values_from="count") %>%
  2572. as.data.frame(.)
  2573. write.table(shared_outliers_circle, paste0(out_dir, "slim_subsample_10Mb_shared_outliers.txt"), col.names=TRUE, row.names=FALSE, quote=FALSE, sep="\t")
  2574. ```

all_analyses_figure_table_creation.Rmd at commit c380e2a, under MIT · at the source

Overview

  1. Department of Biological Sciences, University of Denver, Denver, CO, USA
  2. Department of Ecology and Evolutionary Biology, University of Connecticut, Storrs, CT, USA
Institutions: University of Denver (United States); University of Connecticut (United States)
Journal: Molecular biology and evolution, volume 43, issue 6, article msag149
Dates: received 25 August 2025; accepted 26 May 2026; published online 15 June 2026; in print June 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1093/molbev/msag149 · PMID 42295111 · PMCID PMC13312963 · OpenAlex W7164843725
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), other (organism), cellular / molecular (subfield)
Methods: Connectivity, Statistics, Smoothing, state filtering, decompositions
Keywords: alewife, balancing selection, selection scan, parallelism, convergence, genomics, evolution, anadromy
MeSH: Adaptation, Biological*, Adaptation, Physiological*, Fishes*, Selection, Genetic*, Animal Migration, Animals, Biological Evolution, Fresh Water, Humans, Osmoregulation (* major topic)
Journal subjects: Discoveries
Topic: Genetic Associations and Epidemiology (Genetics, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Citations: not cited yet (Europe PMC); 160 references in the paper

Abstract

The extent to which we can predict evolution is crucial in our era of rapid anthropogenic change. Alewives (Alosa pseudoharengus) in the Atlantic coastal USA are a unique model to test for evolutionary predictability in an anthropogenic context, as multiple, formerly anadromous (migratory from ocean to freshwater) populations have been independently restricted to freshwater (landlocked) by dams built in the last 350 years. Landlocked alewives show parallel changes in life history, feeding morphology, and osmoregulatory physiology. To test if recent freshwater adaptations are repeatable and predictable at the genomic level, we compared whole genomes of four landlocked and one anadromous population representing the ancestor. We determined that repeated positive selection is rare, limited to a single region on a single chromosome. Despite this, candidate analysis revealed that regions of repeatability do occur—in some populations but not others—in genes with putative function in freshwater adaptation, most notably in those involved in osmoregulation. Surprisingly, the strongest signal of selection in the genome was not one of positive selection, but one of conserved, balancing selection in a single gene family known as protocadherins, which play an important role in neural circuit formation and neuron recognition. Our results suggest that constrictive demographic histories and/or a polygenic nature of the complex trait architecture limits parallel selection at the genotypic level despite parallelism of phenotype. This highlights the need to understand both demography and trait architecture when determining the degree to which evolution is predictable.

Reproduced under the paper's license (CC BY-NC), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 8 matches between paragraphs and lines of code.

rileycorcoran

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: the text, “Data processing”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)

ksiewert/betascan

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 2f9c961539a5c0703b1808b49f485a93ac21519f, 25 April 2023
Languages: Python (2)
Size: 4 files, 2 scripts
Software Heritage: not archived
Found in: the text, “Pairwise selection scans”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
3 files

rileycorcoran/Alewife-Selection

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: c380e2a928f6dc8ce49a7c085ae6d24d948b1dd0, 10 June 2026
Languages: R (12), Shell (3)
Size: 73 files, 15 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, 1 notebook
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: tidyverse (10 files), data.table (9 files), ggplot2 (7 files), cowplot (6 files), Plotly (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
17 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 17 scripts, each with its path and the digest of its content;
  • 8 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data availability

Raw sequence reads in demultiplexed fastq format for all 110 alewives are available on NBCI SRA (BioProject PRJNA1477158). Additional data and methods are provided within the Supplementary material online. All scripts used during processing and analysis can be found at https://github.com/rileycorcoran/Alewife-Selection.

Reproduced under the paper's license (CC BY-NC), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 8 keywords, 10 MeSH terms, 142 references.

Cite

This paper

Corcoran, R. M., Schultz, E. T., & Velotta, J. P. (2026). Multiple modes of selection underlie repeated and human-mediated adaptation in a formerly migratory fish. Molecular biology and evolution, 43(6), msag149. https://doi.org/10.1093/molbev/msag149

BibTeX

@article{corcoran2026multiple,
author = {Corcoran, Riley M and Schultz, Eric T and Velotta, Jonathan P},
title = {{Multiple modes of selection underlie repeated and human-mediated adaptation in a formerly migratory fish}},
journal = {Molecular biology and evolution},
year = {2026},
month = jun,
volume = {43},
number = {6},
pages = {msag149},
publisher = {Oxford University Press},
issn = {0737-4038},
doi = {10.1093/molbev/msag149},
url = {https://doi.org/10.1093/molbev/msag149},
pmid = {42295111},
pmcid = {PMC13312963}
}

RIS

TY - JOUR
AU - Corcoran, Riley M
AU - Schultz, Eric T
AU - Velotta, Jonathan P
TI - Multiple modes of selection underlie repeated and human-mediated adaptation in a formerly migratory fish
T2 - Molecular biology and evolution
J2 - Mol Biol Evol
PY - 2026
DA - 2026/06/01
VL - 43
IS - 6
SP - msag149
SN - 0737-4038
PB - Oxford University Press
DO - 10.1093/molbev/msag149
UR - https://doi.org/10.1093/molbev/msag149
LA - en
ER -

CSL-JSON

{
"id": "10.1093/molbev/msag149",
"type": "article-journal",
"title": "Multiple modes of selection underlie repeated and human-mediated adaptation in a formerly migratory fish",
"container-title": "Molecular biology and evolution",
"author": [
{
"family": "Corcoran",
"given": "Riley M"
},
{
"family": "Schultz",
"given": "Eric T"
},
{
"family": "Velotta",
"given": "Jonathan P"
}
],
"container-title-short": "Mol Biol Evol",
"volume": "43",
"issue": "6",
"page": "msag149",
"DOI": "10.1093/molbev/msag149",
"PMID": "42295111",
"PMCID": "PMC13312963",
"ISSN": "0737-4038",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/molbev/msag149",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
1
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1093/gbe/evag222 [code]
Genomic Signatures of Selection Are Enriched in Differentially Expressed Genes in Sticklebacks Adapting to Contrasting Environments.
Journal: Genome biology and evolution
In common: NumPy, other, cellular / molecular, 6 references
[2] doi:10.1038/s41467-026-72598-z [code]
Functional impact of genetic background on variable expressivity in neurodevelopmental disorders.
Journal: Nature communications
In common: data.table, ggplot2, tidyverse, 1 other tool, 4 references
[3] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: Plotly, cowplot, data.table, 3 other tools, cellular / molecular, 1 reference
[4] doi:10.3390/biom16081187
Genetic Diversity and Runs of Homozygosity in Three Masu Salmon (&lt;i&gt;Oncorhynchus masou&lt;/i&gt;) Populations Based on Whole-Genome Resequencing Data.
Journal: Biomolecules
In common: other, cellular / molecular, 5 references
[5] doi:10.1371/journal.pcbi.1014422 [code]
Deciphering cell type-specific causal genetic effects on brain imaging-derived phenotypes and disorders with single-cell Mendelian randomization.
Journal: PLoS computational biology
In common: data.table, ggplot2, tidyverse, cellular / molecular, 3 references
[6] doi:10.1093/bioinformatics/btag592 [code]
Network-based stratification of allele-specific expression reveals patient subgroups in Huntington's disease.
Journal: Bioinformatics (Oxford, England)
In common: Plotly, cowplot, data.table, 3 other tools, 1 reference
[7] doi:10.1038/s41467-026-74753-y [code]
A human-specific microRNA controls the timing of excitatory synaptogenesis.
Journal: Nature communications
In common: Plotly, cowplot, data.table, 2 other tools, cellular / molecular, 1 reference
[8] doi:10.1016/j.cell.2026.05.026 [code]
The critical role of the endogenous immune compartment after CAR T cell therapy in recurrent GBM.
Journal: Cell
In common: cowplot, data.table, ggplot2, 2 other tools, 2 references
[9] doi:10.1186/s11689-026-09713-0 [code]
DRP1 mutations associated with EMPF1 encephalopathy perturb the transcriptional profile and maturation of cortical neurons.
Journal: Journal of neurodevelopmental disorders
In common: Plotly, ggplot2, tidyverse, 1 other tool, 3 references
[10] doi:10.1038/s41514-026-00397-3 [code]
Nasal administration of Protollin enhances monocyte phagocytosis and decreases CD8&lt;sup&gt;+&lt;/sup&gt; T cell cytotoxicity in subjects with early Alzheimer's disease: a Phase 1 clinical trial.
Journal: npj aging
In common: Plotly, cowplot, data.table, 2 other tools, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.