OSCR

Brain Transcriptomic Changes Associated With the Socially Regulated Behavioural Specialisation of Ant Queens.

Code ↔ Paper

9 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 9 matches
  1. [1] § Results › Effect of the Presence of Larvae on the Expression of the Genes of Interest (Experiment C) ↔ R script_Mol_Ecol_2026.R, lines 577 to 650 · score 1.00 · Pheromone binding protein, Lnig_g15862, gLnigGR81, Juvenile hormone esterase, Lnig_g01559, Lnig_g04508 · Report a mismatch
  2. [2] § Results › Effect of Separated Workers on the Expression of the Genes of Interest (Experiment B) ↔ R script_Mol_Ecol_2026.R, lines 577 to 650 · score 0.98 · Juvenile hormone esterase, Lnig_g01559, Lnig_g04508, Lnig_g11740, Lnig_g11948, Lnig_g13064 · Report a mismatch
  3. [3] § Methods › Gene Expression Analyses › Analysis of the Effect of Worker Presence on Queen Brain Gene Expression ↔ R script_Mol_Ecol_2026.R, lines 378 to 416 · score 0.83 · topGO, GO annotation, biological process, GO terms, Fisher, Ontology · Report a mismatch
  4. [4] § Methods › Gene Expression Analyses › Analysis of the Effect of Worker Presence on Queen Brain Gene Expression ↔ R script_Mol_Ecol_2026.R, lines 337 to 375 · score 0.81 · explanatory variables, full model, reduced model, DESeq2, LRT, batch · Report a mismatch
  5. [5] § Results › Effect of Worker Removal on the Expression of the Genes of Interest (Experiment A) ↔ R script_Mol_Ecol_2026.R, lines 786 to 827 · score 0.75 · Juvenile hormone esterase, Lnig_g17128, Lnig_g01448, Lnig_g08195, Kr h1, Insulin · Report a mismatch
  6. [6] § Methods › Sample Collection › Effect of the Experimental Addition of Workers and Separated Workers ↔ R script_Mol_Ecol_2026.R, lines 1407 to 1477 · score 0.68 · wire mesh, antennal contacts, separated workers, queens passed, touching · Report a mismatch
  7. [7] § Results › Effect of the Presence of Workers on Brain Gene Expression in Queens ↔ R script_Mol_Ecol_2026.R, lines 418 to 456 · score 0.55 · biological processes, molecular functions, GO term, genes · Report a mismatch
  8. [8] § Results › Effect of Separated Workers on the Expression of the Genes of Interest (Experiment B) ↔ R script_Mol_Ecol_2026.R, lines 1407 to 1477 · score 0.54 · wire mesh, antennal contacts, separated workers, behaviour, queen · Report a mismatch
  9. [9] § Results › Effect of the Presence of Workers on Brain Gene Expression in Queens ↔ R script_Mol_Ecol_2026.R, lines 418 to 456 · score 0.52 · biological processes, molecular functions, GO term, DEG, gene · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R · 1,888 lines · 59 KB · no license · 9 matches

  1. #R code for the statistical analyses of :
  2. #Transcriptomic changes associated with the socially regulated behavioral specialization of ant queens
  3. #2026
  4. #Hugo Le Lay, Maximilian F. Bolder, Thomas J. Colgan, Romain Libbrecht
  5. rm(list=ls())
  6. #Load packages-----
  7. library(viridis)
  8. library(readxl) # Import Excel files (.xls, .xlsx)
  9. library(car) # Tools for regression analysis and ANOVA
  10. library(lme4) # Linear and generalized linear mixed-effects models (LMM, GLMM)
  11. library(emmeans) # Estimated marginal means and post-hoc comparisons
  12. library(DHARMa) # Model diagnostics using simulated residuals
  13. library(tximport) # Import and summarize transcript-level RNA-seq quantification data
  14. library(DESeq2) # Differential gene expression analysis for RNA-seq data
  15. library(topGO) # Gene Ontology (GO) enrichment analysis
  16. library(tidyverse) # Collection of packages for data manipulation and visualization
  17. library(ggrepel) # Add non-overlapping text labels to ggplot2 plots
  18. library(ggpubr) # Easy-to-use functions for publication-ready plots
  19. library(smplot2) # Themes and functions for scientific data visualization
  20. library(MASS) # Advanced statistical methods and distributions
  21. library(dplyr) # Data manipulation (filter, mutate, summarise, etc.)
  22. library(stats) # Base R statistical functions (tests, linear models, etc.)
  23. library(ggplot2) # Data visualization using the grammar of graphics
  24. library(grid) # Low-level graphical system for fine plot control
  25. library(GO.db) # Gene Ontology annotation database
  26. library(dplyr) # Data manipulation (loaded again here)
  27. library(stringr) # String manipulation functions
  28. library(cowplot) # Plot arrangement and annotation for ggplot2
  29. library(svglite) # Export plots to SVG format
  30. library(multcomp) # Multiple comparisons and post-hoc tests
  31. library(patchwork) # Combine multiple ggplot2 plots into one layout
  32. library(openxlsx) # Export table in excel files
  33. #Build functions to produce plots------
  34. # Create a gene expression plot showing normalized counts
  35. # across worker conditions, with jittered points, mean ± SE,
  36. # formatted adjusted p-value in the subtitle, and layout control for multi-panel grids.
  37. make_gene_plot <- function(row, i, ncol_grid = 3) {
  38. counts <- plotCounts(
  39. dds50_Q1,
  40. gene = row$gene_id,
  41. intgroup = c("batch","workers"),
  42. returnData = TRUE,
  43. normalized = TRUE
  44. )
  45. adj_pval_txt <- fmt_adj_pval(padj_vals[row$padj_key])
  46. if (nzchar(row$extra_desc)) {
  47. subtitle_txt <- sprintf("(%s, %s, %s)",
  48. row$gene_id,
  49. row$extra_desc,
  50. adj_pval_txt)
  51. } else {
  52. subtitle_txt <- sprintf("(%s, %s)",
  53. row$gene_id,
  54. adj_pval_txt)
  55. }
  56. p <- ggplot(counts,
  57. aes(x = workers, y = count)) +
  58. geom_jitter(
  59. colour = jitter_pt_colour,
  60. fill = jitter_pt_fill,
  61. shape = jitter_pt_shape,
  62. size = jitter_pt_size,
  63. width = 0.1,
  64. height = 0
  65. ) +
  66. stat_summary(
  67. aes(colour = workers),
  68. fun.data = mean_se,
  69. geom = "pointrange",
  70. size = mean_se_pt_size,
  71. show.legend = FALSE
  72. ) +
  73. labs(
  74. title = row$display_name,
  75. subtitle = subtitle_txt,
  76. x = NULL,
  77. y = "Normalized read counts"
  78. ) +
  79. scale_color_manual(values = worker_cols) +
  80. scale_x_discrete(labels = c("Without \nworkers", "With \nworkers")) +
  81. theme_classic(base_size = 18) +
  82. theme(
  83. plot.title = element_text(size = title_txt_size, face = "bold.italic"),
  84. plot.subtitle = element_text(size = subtitle_txt_size),
  85. axis.text.x = element_text(size = x_axis_txt_size),
  86. axis.text.y = element_text(size = y_axis_txt_size),
  87. axis.title.y = element_text(size = axis_title_y_sz),
  88. panel.border = element_blank(),
  89. panel.grid.major = element_blank(),
  90. panel.grid.minor = element_blank(),
  91. axis.line = element_line(colour = "black")
  92. )
  93. col_index <- ((i - 1) %% ncol_grid) + 1
  94. if (col_index != 1) {
  95. p <- p + theme(axis.title.y = element_blank())
  96. }
  97. p
  98. }
  99. # Create a gene expression plot using dataset dds50_3.1,
  100. # displaying normalized counts across three worker conditions,
  101. # adding mean ± SE, dynamic y-axis scaling, optional compact letter display (CLD)
  102. # for statistical group comparisons, and grid-aware y-axis formatting.
  103. make_q3_panel <- function(row, i, ncol_grid = 3, cld_df = NULL) {
  104. counts <- plotCounts(
  105. dds50_3.1,
  106. gene = row$gene_id,
  107. intgroup = c("batch","workers"),
  108. returnData = TRUE,
  109. normalized = TRUE
  110. )
  111. padj_val <- padj_vec_3[row$padj_key]
  112. subtitle_txt <- sprintf("(%s%s, %s)",
  113. row$gene_id,
  114. ifelse(nzchar(row$extra_desc),
  115. paste0(", ", row$extra_desc), ""),
  116. fmt_adj_pval(padj_val))
  117. p <- ggplot(counts, aes(x = workers, y = count)) +
  118. geom_jitter(colour = "grey", fill = "grey",
  119. size = 3, width = 0.1, height = 0) +
  120. labs(title = row$display_name,
  121. subtitle = subtitle_txt,
  122. x = NULL,
  123. y = "Normalized read counts") +
  124. scale_color_manual(values = cb_palette) +
  125. scale_x_discrete(labels = c("Without \nworkers",
  126. "With \nworkers",
  127. "Workers \nremoved")) +
  128. theme_classic(base_size = 18) +
  129. theme(
  130. plot.title = element_text(size = 20, face = "bold.italic",
  131. colour = title_col[i]),
  132. plot.subtitle = element_text(size = 18),
  133. axis.text.x = element_text(size = 18),
  134. axis.text.y = element_text(size = 20),
  135. axis.title.y = element_text(size = 20),
  136. panel.border = element_blank(),
  137. panel.grid = element_blank(),
  138. axis.line = element_line(colour = "black")
  139. ) +
  140. stat_summary(aes(colour = workers),
  141. fun.data = mean_se,
  142. geom = "pointrange",
  143. size = 2,
  144. show.legend = FALSE)
  145. max_y <- max(counts$count, na.rm = TRUE)
  146. p <- p + scale_y_continuous(limits = c(0, max_y * 1.2))
  147. if (!is.null(cld_df) && nrow(cld_df) > 0) {
  148. ypos <- max_y * 1.2
  149. cld_df$ypos <- ypos
  150. p <- p + geom_text(data = cld_df,
  151. aes(x = workers, y = ypos, label = letter),
  152. size = 6, colour = "black")
  153. }
  154. if ((i - 1) %% ncol_grid != 0) {
  155. p <- p + theme(axis.title.y = element_blank())
  156. }
  157. p
  158. }
  159. # Create a gene expression plot using dataset dds50_3.2,
  160. # displaying normalized counts across three worker conditions,
  161. # adding mean ± SE, dynamic y-axis scaling, optional compact letter display (CLD)
  162. # for statistical group comparisons, and grid-aware y-axis formatting.
  163. make_q4_panel <- function(row, i, ncol_grid = 3, cld_df = NULL) {
  164. counts <- plotCounts(
  165. dds50_3.2,
  166. gene = row$gene_id,
  167. intgroup = c("batch","workers"),
  168. returnData = TRUE,
  169. normalized = TRUE
  170. )
  171. padj_val <- padj_vec_4[row$padj_key]
  172. subtitle_txt <- sprintf("(%s%s, %s)",
  173. row$gene_id,
  174. ifelse(nzchar(row$extra_desc),
  175. paste0(", ", row$extra_desc), ""),
  176. fmt_adj_pval(padj_val))
  177. p <- ggplot(counts, aes(x = workers, y = count)) +
  178. geom_jitter(colour = "grey", fill = "grey",
  179. size = 3, width = 0.1, height = 0) +
  180. labs(title = row$display_name,
  181. subtitle = subtitle_txt,
  182. x = NULL,
  183. y = "Normalized read counts") +
  184. scale_color_manual(values = cb_palette_4) +
  185. scale_x_discrete(labels = c("Without\nworkers",
  186. "With\nworkers",
  187. "Workers\nseparated")) +
  188. theme_classic(base_size = 18) +
  189. theme(
  190. plot.title = element_text(size = 20, face = "bold.italic",
  191. colour = title_col[i]),
  192. plot.subtitle = element_text(size = 18),
  193. axis.text.x = element_text(size = 18),
  194. axis.text.y = element_text(size = 20),
  195. axis.title.y = element_text(size = 20),
  196. panel.border = element_blank(),
  197. panel.grid = element_blank(),
  198. axis.line = element_line(colour = "black")
  199. ) +
  200. stat_summary(aes(colour = workers),
  201. fun.data = mean_se,
  202. geom = "pointrange",
  203. size = 2,
  204. show.legend = FALSE)
  205. max_y <- max(counts$count, na.rm = TRUE)
  206. p <- p + scale_y_continuous(limits = c(0, max_y * 1.2))
  207. if (!is.null(cld_df) && nrow(cld_df) > 0 && any(cld_df$letter != "")) {
  208. ypos <- max_y * 1.2
  209. cld_df$ypos <- ypos
  210. p <- p + geom_text(data = cld_df,
  211. aes(x = workers, y = ypos, label = letter),
  212. size = 6, colour = "black")
  213. }
  214. if ((i - 1) %% ncol_grid != 0) {
  215. p <- p + theme(axis.title.y = element_blank())
  216. }
  217. p
  218. }
  219. # Create a gene expression plot using dataset dds50_3.3,
  220. # comparing queens with and without larvae,
  221. # showing jittered normalized counts, mean ± SE,
  222. # formatted adjusted p-values, dynamic y-axis limits,
  223. # and grid-aware y-axis formatting.
  224. make_q5_panel <- function(row, i, ncol_grid = 3) {
  225. counts <- plotCounts(
  226. dds50_3.3,
  227. gene = row$gene_id,
  228. intgroup = c("batch","workers"),
  229. returnData = TRUE,
  230. normalized = TRUE
  231. )
  232. counts$workers <- factor(counts$workers,
  233. levels = c("with_larvae","without_larvae"))
  234. padj_val <- padj_vec_5[row$padj_key]
  235. subtitle_txt <- sprintf("(%s%s, %s)",
  236. row$gene_id,
  237. ifelse(nzchar(row$extra_desc),
  238. paste0(", ", row$extra_desc), ""),
  239. fmt_adj_pval(padj_val))
  240. p <- ggplot(counts, aes(x = workers, y = count)) +
  241. geom_jitter(colour = "grey", fill = "grey",
  242. size = 3, width = 0.1, height = 0) +
  243. stat_summary(aes(colour = workers),
  244. fun.data = mean_se,
  245. geom = "pointrange",
  246. size = 2,
  247. show.legend = FALSE) +
  248. labs(title = row$display_name,
  249. subtitle = subtitle_txt,
  250. x = NULL,
  251. y = "Normalized read counts") +
  252. scale_color_manual(values = cb_palette_5) +
  253. scale_x_discrete(labels = c(
  254. without_larvae = "Without\nlarvae",
  255. with_larvae = "With\nlarvae"
  256. )) +
  257. theme_classic(base_size = 18) +
  258. theme(
  259. plot.title = element_text(size = 20, face = "bold.italic",
  260. colour = title_col[i]),
  261. plot.subtitle = element_text(size = 18),
  262. axis.text.x = element_text( size = 18),
  263. axis.text.y = element_text(size = 20),
  264. axis.title.y = element_text(size = 20),
  265. panel.border = element_blank(),
  266. panel.grid = element_blank(),
  267. axis.line = element_line(colour = "black")
  268. )
  269. max_y <- max(counts$count, na.rm = TRUE)
  270. p <- p + scale_y_continuous(limits = c(0, max_y * 1.2))
  271. if ((i - 1) %% ncol_grid != 0) {
  272. p <- p + theme(axis.title.y = element_blank())
  273. }
  274. p
  275. }
  276. #P-value adjustment function to homogenize naming and threshold
  277. fmt_adj_pval <- function(p) {
  278. if (is.na(p)) {
  279. return("adjusted-pvalue = NA")
  280. }
  281. if (p < 0.0001) {
  282. return("adjusted-pvalue < 0.0001")
  283. }
  284. return(sprintf("adjusted-pvalue = %.2g", p))
  285. }
  286. #Set the working directory-----
  287. #Path to the RDS files
  288. setwd(choose.dir())
  289. #Part 1: Comparison of queens with and without workers-----
  290. ##1.1. Load count table and metadata information------
  291. txi_with_without_workers <- readRDS("txi_with_without_workers.rds")
  292. ##1.2. Differential expression analysis-----
  293. #Create dds with batch (i.e., experiment) and presence of workers in the full model
  294. dds_Q1=DESeqDataSetFromMatrix(countData = txi_with_without_workers$counts,
  295. colData= txi_with_without_workers$meta,
  296. design=~batch+workers)
  297. #To only keep genes expressed in more than half of the samples,
  298. #we only keep rows with at least 25 samples with a non-zero value
  299. dds50_Q1=dds_Q1[rowSums(counts(dds_Q1) >0)>= 25]
  300. #Analysis of the impact of the presence of workers, while controlling for batch,
  301. #by conducting a LRT comparison of the full model with a reduced model only containing batch as explanatory variable
  302. dds_test_Q1=DESeq(dds50_Q1, test="LRT", reduced=~batch)
  303. #Produce the result table
  304. res_Q1=results(dds_test_Q1, alpha=0.05)
  305. summary(res_Q1)
  306. #Extract the genes with adjusted-p<0.05
  307. DEG_Q1=subset(res_Q1, padj<0.05)
  308. rownames(DEG_Q1)
  309. nrow(DEG_Q1)
  310. #Note: positive log-fold = With workers > Without workers
  311. #Produce an Excel table with the output of the analysis for the DEG
  312. #DEG_Q1_Supplementary=DEG_Q1
  313. #DEG_Q1_Supplementary$Gene_names=rownames(DEG_Q1_Supplementary)
  314. #write.xlsx(DEG_Q1_Supplementary, file = "DEG_Q1.xlsx")
  315. ##1.3. Run the GO term analysis-----
  316. GO=read.table("GO_terms.txt")
  317. colnames(GO) = c("Transcripts", "GOterms")
  318. all(GO$Transcripts %in% txi_with_without_workers$tx2gene$transcript)
  319. # Merge GO annotations with transcript-to-gene mapping,
  320. # keeping all rows from GO (left join style)
  321. GO_merged = merge(GO, txi_with_without_workers$tx2gene, by.x = "Transcripts", by.y = "transcript", all.x = TRUE)
  322. colnames(GO_merged) = c("Transcripts", "GOterms", "Genes")
  323. # Create a list mapping each gene to its associated GO terms
  324. gene2GO = split(GO_merged$GOterms, GO_merged$Genes)
  325. length(gene2GO)
  326. head(gene2GO)
  327. #Create a factor where:
  328. #1 means the gene is in the DEG list
  329. #0 means the gene is in the background (all genes with GO annotation)
  330. all_genes = unique(txi_with_without_workers$tx2gene$gene)
  331. all_genes = all_genes[all_genes %in% rownames(res_Q1)]
  332. gene_list = factor(as.integer(all_genes %in% rownames(DEG_Q1)))
  333. names(gene_list) = all_genes
  334. #Create a topGO Object for Biological process
  335. GO_data_Q1_BP = new("topGOdata",
  336. ontology = "BP",
  337. allGenes = gene_list,
  338. annot = annFUN.gene2GO,
  339. gene2GO = gene2GO)
  340. #Run Fisher's Exact Test
  341. resultFisher_GO_Q1_BP = runTest(GO_data_Q1_BP, algorithm = "weight01", statistic = "fisher")
  342. print(resultFisher_GO_Q1_BP)
  343. #Retrieve Significant GO Terms represented by at least three genes in the list of DEG
  344. GO_results_Q1_BP = GenTable(GO_data_Q1_BP, resultFisher_GO_Q1_BP, topNodes = 28)
  345. print(subset(GO_results_Q1_BP, Significant >= 3))
  346. #Create a topGO Object for Molecular function
  347. GO_data_Q1_MF = new("topGOdata",
  348. ontology = "MF",
  349. allGenes = gene_list,
  350. annot = annFUN.gene2GO,
  351. gene2GO = gene2GO)
  352. #Run Fisher's Exact Test
  353. resultFisher_GO_Q1_MF = runTest(GO_data_Q1_MF, algorithm = "weight01", statistic = "fisher")
  354. print(resultFisher_GO_Q1_MF)
  355. #Retrieve Significant GO Terms represented by at least three genes in the list of DEG
  356. GO_results_Q1_MF = GenTable(GO_data_Q1_MF, resultFisher_GO_Q1_MF, topNodes = 9)
  357. print(GO_results_Q1_MF)
  358. print(subset(GO_results_Q1_MF, Significant >= 3))
  359. #Extraction of DEGs with significant GOs
  360. #Biological Process
  361. GO2genes_list_BP = genesInTerm(GO_data_Q1_BP)
  362. GO_signif_Q1_BP = subset(GO_results_Q1_BP, Significant >= 3)
  363. GO_signif_Q1_BP$Genes = sapply(GO_signif_Q1_BP$GO.ID, function(go_id) {
  364. genes = GO2genes_list_BP[[go_id]]
  365. paste(genes, collapse = ", ")
  366. genes = intersect(genes, rownames(DEG_Q1))
  367. })
  368. GO_signif_Q1_BP$Genes
  369. #Molecular function
  370. GO2genes_list_MF = genesInTerm(GO_data_Q1_MF)
  371. GO_signif_Q1_MF = subset(GO_results_Q1_MF, Significant >= 3)
  372. GO_signif_Q1_MF$Genes = sapply(GO_signif_Q1_MF$GO.ID, function(go_id) {
  373. genes = GO2genes_list_MF[[go_id]]
  374. paste(genes, collapse = ", ")
  375. genes = intersect(genes, rownames(DEG_Q1))
  376. })
  377. GO_signif_Q1_MF$Genes
  378. ##1.4. Produce Figure 1-----
  379. #Data preparation
  380. res_Q1_df <- as.data.frame(res_Q1)
  381. res_Q1_df <- res_Q1_df[!is.na(res_Q1_df$padj),]
  382. res_Q1_df$diffexpressed <- "No"
  383. res_Q1_df$diffexpressed[res_Q1_df$log2FoldChange > 0 & res_Q1_df$padj < 0.05] <- "Overexpressed with workers"
  384. res_Q1_df$diffexpressed[res_Q1_df$log2FoldChange < 0 & res_Q1_df$padj < 0.05] <- "Underexpressed with workers"
  385. res_Q1_df$Comparison <- ifelse(res_Q1_df$diffexpressed == "Overexpressed with workers", "Workers superior",
  386. ifelse(res_Q1_df$diffexpressed == "Underexpressed with workers", "Workers inferior", "No"))
  387. #Arrow Preparation
  388. y_arrow <- max(-log10(res_Q1_df$padj), na.rm = TRUE) * 1.05
  389. # Volcano plot
  390. Volcano_plot_final <- ggplot(res_Q1_df, aes(x = log2FoldChange, y = -log10(padj), col = Comparison)) +
  391. geom_point(size = 2, alpha = 0.8) +
  392. geom_hline(yintercept = -log10(0.05), color = "red", linetype = "dashed", size = 0.8) +
  393. geom_vline(xintercept = 0, color = "black", linetype = "dashed", size = 0.8) +
  394. annotate("segment", x = 0, xend = 3.5, y = y_arrow, yend = y_arrow,
  395. color = "#0072B2", size = 1) +
  396. annotate("text", x = 1.75, y = y_arrow + 0.4,
  397. label = "Overexpressed in the brain of queens \nprovided with workers", color = "#0072B2", size = 4) +
  398. annotate("segment", x = 0, xend = -3.5, y = y_arrow, yend = y_arrow,
  399. color = "darkgreen", size = 1) +
  400. annotate("text", x = -1.75, y = y_arrow + 0.4,
  401. label = "Overexpressed in the brain of queens \nkept without workers", color = "darkgreen", size = 4) +
  402. scale_color_manual(values = c("Workers superior" = "#0072B2",
  403. "Workers inferior" = "darkgreen",
  404. "No" = "grey70")) +
  405. scale_x_continuous(limits = c(-3.5, 3.5), breaks = seq(-3, 3, 1)) +
  406. labs(x = expression(log[2]~"fold change"), y = expression(-log[10]~"(adjusted p-value)")) + ##C1:Change p Value name as you wish
  407. theme_minimal(base_size = 12) +
  408. theme(
  409. legend.position = "none",
  410. axis.title = element_text(size = 14),
  411. axis.text = element_text(size = 10)
  412. )
  413. Volcano_plot_final
  414. #Barplot
  415. #Recombining the datasets
  416. GO_signif_Q1_BP$Category <- "Biological processes"
  417. GO_signif_Q1_MF$Category <- "Molecular functions"
  418. GO_signif_Q1_all <- rbind(GO_signif_Q1_BP, GO_signif_Q1_MF)
  419. GO_signif_Q1_all <- GO_signif_Q1_all %>%
  420. group_by(Category) %>%
  421. arrange(desc(Significant)) %>%
  422. ungroup() %>%
  423. mutate(Term = factor(Term, levels = rev(Term)))
  424. n_BP <- nrow(GO_signif_Q1_BP)
  425. n_all <- nrow(GO_signif_Q1_all)
  426. GO_signif_Q1_all <- GO_signif_Q1_all %>%
  427. rowwise() %>%
  428. mutate(
  429. FullTerm = Term(GOTERM[[GO.ID]]) %>% as.character()
  430. ) %>%
  431. ungroup()
  432. #Wrap for long names. Very useful
  433. GO_signif_Q1_all$FullTerm <- str_wrap(GO_signif_Q1_all$FullTerm, width = 30)
  434. GO_signif_Q1_all <- GO_signif_Q1_all %>%
  435. group_by(Category) %>%
  436. arrange(desc(Significant)) %>%
  437. ungroup() %>%
  438. mutate(FullTerm = factor(FullTerm, levels = rev(FullTerm)))
  439. GO_barplot <- ggplot(GO_signif_Q1_all, aes(x = Significant, y = FullTerm, fill = as.numeric(result1))) +
  440. geom_col() +
  441. scale_fill_gradient(low = "grey20", high = "grey90", name = "p-value") + #C1 Check p-value
  442. labs(x = "Number of genes in the list of DEG", y = NULL) +
  443. theme_classic() +
  444. theme(
  445. axis.text.y = element_text(size = 12),
  446. axis.text.x = element_text(size = 10),
  447. axis.title.x = element_text(size = 12)
  448. ) +
  449. geom_hline(yintercept = nrow(GO_signif_Q1_all %>% filter(Category == "Biological processes")) + 0.5, linewidth = 0.8) +
  450. annotate(
  451. "text",
  452. x = 9,
  453. y = nrow(GO_signif_Q1_all %>% filter(Category == "Biological processes")) - 0.1,
  454. label = "Biological processes",
  455. fontface = "bold",
  456. hjust = 0,
  457. size = 3
  458. ) +
  459. annotate(
  460. "text",
  461. x = 9,
  462. y = nrow(GO_signif_Q1_all %>% filter(Category == "Biological processes")) +1.1,
  463. label = "Molecular functions",
  464. fontface = "bold",
  465. hjust = 0,
  466. size = 3
  467. ) +
  468. scale_x_continuous(limits = c(0, max(GO_signif_Q1_all$Significant, 21)),breaks = seq(0,21,by = 3))
  469. GO_barplot
  470. Combined_plot <- ggarrange(
  471. Volcano_plot_final,
  472. GO_barplot,
  473. ncol = 2,
  474. widths = c(1.3, 1),
  475. labels = c("A", "B")
  476. )
  477. #Figure 1 Result
  478. Combined_plot
  479. #Export
  480. #ggsave(
  481. # filename = "Figure_1.svg",
  482. # plot = Combined_plot,
  483. # width = 14,
  484. # height = 7,
  485. # dpi = 600,
  486. # bg = "white",
  487. # device = "svg")
  488. #Part 2: Identification of genes of interests for the social regulation of queen brood care behavior-----
  489. ##2.1. List of genes of interest-----
  490. DEG_Q1_Genes_of_Interest=DEG_Q1
  491. DEG_Q1_Genes_of_Interest$Gene_name=rownames(DEG_Q1_Genes_of_Interest)
  492. Krh1=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g01448")
  493. dPDK1=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g11740")
  494. Insulin=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g17128")
  495. Juv_esterase=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g08195")
  496. E4=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g15862")
  497. Dsx=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g04508")
  498. Henna=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g13064")
  499. Prohormone=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g06899")
  500. GP9=subset(DEG_Q1_Genes_of_Interest, Gene_name=="gLnigOBP1")
  501. GR81= subset(DEG_Q1_Genes_of_Interest, Gene_name=="gLnigGR81")
  502. Tret1a=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g01559")
  503. Tret1b=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g11948")
  504. Tret1_2=subset(DEG_Q1_Genes_of_Interest, Gene_name=="Lnig_g10144")
  505. ##2.2. Produce Supplementary Figure 1 -----
  506. plot_info <- data.frame(
  507. gene_id = c("Lnig_g01448","Lnig_g11740","Lnig_g17128","Lnig_g08195",
  508. "Lnig_g15862","Lnig_g04508","Lnig_g13064",
  509. "Lnig_g06899","gLnigOBP1","gLnigGR81",
  510. "Lnig_g01559","Lnig_g11948","Lnig_g10144"),
  511. padj_key = c("Krh1","dPDK1","Insulin","JuvEsterase",
  512. "E4","Dsx","Henna",
  513. "Prohormone","GP9","GR81",
  514. "Tret1a","Tret1b","Tret1_2"),
  515. display_name = c("Kr-h1","PDK1","Insulin","Juvenile hormone esterase",
  516. "Esterase E4","Protein doublesex",
  517. "Protein henna","Prohormone-2",
  518. "Pheromone binding protein GP9",
  519. "GR81","Tret1a","Tret1b","Tret1-2 homolog"
  520. ),
  521. extra_desc = c("", "", "", "", "", "", "", "", "", "", "", "", ""),
  522. stringsAsFactors = FALSE
  523. )
  524. Genes_of_interest_Overexpressed_in_worker_presence= c("Lnig_g01448", "Lnig_g17128", "Lnig_g08195", "Lnig_g11948", "Lnig_g11740", "Lnig_g04508")
  525. Genes_of_interest_Overexpressed_in_worker_absence= c( "Lnig_g15862", "Lnig_g13064", "gLnigOBP1", "gLnigGR81", "Lnig_g01559", "Lnig_g10144", "Lnig_g06899")
  526. res_Q1_plot=as.data.frame(res_Q1)
  527. res_Q1_plot$gene=rownames(res_Q1_plot)
  528. res_Q1_plot$Interest=NA
  529. for (i in seq_len(nrow(res_Q1_plot))) {
  530. gene_name <- rownames(res_Q1_plot)[i]
  531. if(gene_name %in% Genes_of_interest_Overexpressed_in_worker_presence) {
  532. res_Q1_plot$Interest[i]="Genes of interest Overexpressed in worker presence"
  533. } else if (gene_name %in% Genes_of_interest_Overexpressed_in_worker_absence) {
  534. res_Q1_plot$Interest[i] = "Genes of interest Overexpressed in worker absence"
  535. }
  536. }
  537. res_Q1_plot <- res_Q1_plot %>%
  538. left_join(plot_info[, c("gene_id", "display_name")],
  539. by = c("gene" = "gene_id"))
  540. #Volcano plot with genes of interests
  541. Volcano_plot_Supplementary <- ggplot(data=res_Q1_plot, aes(x=log2FoldChange, y=-log10(padj), col=Interest)) +
  542. geom_point(size = 2, alpha = 0.8) +
  543. geom_hline(yintercept = -log10(0.05), color = "red", linetype = "dashed", size = 0.8) +
  544. geom_vline(xintercept = 0, color = "black", linetype = "dashed", size = 0.8) +
  545. geom_text_repel(data=res_Q1_plot[res_Q1_plot$padj < 0.05 & res_Q1_plot$Interest!="NA", ], aes(label=display_name),
  546. size=5, max.overlaps=15) +
  547. scale_color_manual(values=c("darkgreen","#0072B2"),na.value="lightgrey") +
  548. annotate("segment", x = 0, xend = 3.5, y = y_arrow, yend = y_arrow,
  549. color = "#0072B2", size = 1) +
  550. annotate("text", x = 1.75, y = y_arrow + 0.4,
  551. label = "Overexpressed in the brain of queens \nprovided with workers", color = "#0072B2", size = 4) +
  552. annotate("segment", x = 0, xend = -3.5, y = y_arrow, yend = y_arrow,
  553. color = "darkgreen", size = 1) +
  554. annotate("text", x = -1.75, y = y_arrow + 0.4,
  555. label = "Overexpressed in the brain of queens \nkept without workers", color = "darkgreen", size = 4) +
  556. scale_x_continuous(limits = c(-3.5, 3.5), breaks = seq(-3, 3, 1)) +
  557. labs(x = expression(log[2]~"fold change"), y = expression(-log[10]~"(adjusted p-value)")) +
  558. theme_minimal(base_size = 12) +
  559. theme(
  560. legend.position = "none",
  561. axis.title = element_text(size = 14),
  562. axis.text = element_text(size = 10)
  563. )
  564. Volcano_plot_Supplementary
  565. #ggsave(
  566. # filename = "Lasius_Supplementary_Figure_1.svg",
  567. # plot = Volcano_plot_Supplementary,
  568. # width = 12,
  569. # height = 7,
  570. # dpi = 600,
  571. # bg = "white",
  572. # device = "svg")
  573. ##2.3. Produce Figure 2-----
  574. padj_vals <- c(
  575. Krh1 = Krh1$padj,
  576. dPDK1 = dPDK1$padj,
  577. Insulin = Insulin$padj,
  578. JuvEsterase= Juv_esterase$padj,
  579. E4 = E4$padj,
  580. Dsx = Dsx$padj,
  581. GP9 = GP9$padj,
  582. GR81 = GR81$padj,
  583. Tret1a = Tret1a$padj,
  584. Tret1b = Tret1b$padj,
  585. Tret1_2 = Tret1_2$padj,
  586. Henna = Henna$padj,
  587. Prohormone = Prohormone$padj
  588. )
  589. worker_cols <- c("darkgreen","#0072B2")
  590. jitter_pt_size <- 3
  591. jitter_pt_colour <- "grey"
  592. jitter_pt_fill <- "grey"
  593. jitter_pt_shape <- 21
  594. mean_se_pt_size <- 2
  595. x_axis_txt_size <- 20
  596. y_axis_txt_size <- 20
  597. title_txt_size <- 20
  598. subtitle_txt_size<- 18
  599. axis_title_y_sz <- 20
  600. ncol_grid <- 3
  601. all_plots <- lapply(seq_len(nrow(plot_info)), function(i) {
  602. make_gene_plot(plot_info[i, ], i, ncol_grid = ncol_grid)
  603. })
  604. final_figure <- plot_grid(
  605. plotlist = all_plots,
  606. nrow = ceiling(length(all_plots) / ncol_grid),
  607. ncol = ncol_grid,
  608. labels = LETTERS[seq_along(all_plots)],
  609. label_size = 20,
  610. label_fontface = "bold",
  611. align = "hv"
  612. )
  613. final_figure
  614. #Export
  615. #ggsave(
  616. # filename = "Figure_2.svg",
  617. # plot = final_figure,
  618. # width = 50,
  619. # height = 75,
  620. # units = "cm",
  621. # device = "svg")
  622. #Part 3: Comparison of the genes of interest among queens with workers, without workers and with workers removed-----
  623. ##3.1. Load count table and metadata information------
  624. #Only the brain samples for experiment E11 are used in this analysis
  625. txi_with_without_removed_workers <- readRDS("txi_with_without_removed_workers.rds")
  626. ##3.2. Analysis of expression differences-----
  627. #Create dds
  628. dds_3.1=DESeqDataSetFromMatrix(countData = txi_with_without_removed_workers$counts,
  629. colData = txi_with_without_removed_workers$meta,
  630. design=~workers)
  631. #To only keep genes expressed in at least half of the samples,
  632. #we only keep rows at least 19 samples with a non-zero value
  633. dds50_3.1=dds_3.1[rowSums(counts(dds_3.1) >0)>= 19]
  634. #Normalized counts extraction
  635. dds_test_3.1=DESeq(dds50_3.1, test="LRT", reduced=~1)
  636. normalized_counts.3.1 <- counts(dds_test_3.1, normalized = TRUE)
  637. #Pivot longer the data frame
  638. normalized_counts_long.3.1 <- normalized_counts.3.1 %>%
  639. as.data.frame() %>%
  640. rownames_to_column(var = "gene") %>%
  641. pivot_longer(
  642. cols = -gene,
  643. names_to = "sample",
  644. values_to = "normalized_count"
  645. )
  646. normalized_counts_long.3.1 <- normalized_counts_long.3.1 %>%
  647. left_join(as.data.frame(txi_with_without_removed_workers$meta) %>% dplyr::select(sample, workers), by = "sample")
  648. #Comparison of the expression levels for each of the genes of interest
  649. ###Kr-h1
  650. #Subset Kr-h1 counts
  651. Krup.3.1=subset(normalized_counts_long.3.1, gene=="Lnig_g01448")
  652. #Model
  653. modK.3.1=lm(log(normalized_count+1)~workers, data=Krup.3.1)
  654. simulationOutput = simulateResiduals(fittedModel= modK.3.1, plot = T)
  655. #Results
  656. car::Anova(modK.3.1)
  657. emmeans(modK.3.1,pairwise~workers)
  658. ###PDK1
  659. #Subset the PDK1 counts
  660. dPDK1.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g11740")
  661. #Model
  662. moddPDK1.3.1=lm(normalized_count~workers, data=dPDK1.3.1)
  663. simulationOutput = simulateResiduals(fittedModel= moddPDK1.3.1, plot = T)
  664. #Results
  665. car::Anova(moddPDK1.3.1)
  666. emmeans(moddPDK1.3.1,pairwise~workers)
  667. ###Insulin
  668. #Subset the Insulin counts
  669. Ins.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g17128")
  670. #Model
  671. modIns.3.1=lm(normalized_count~workers, data=Ins.3.1)
  672. simulationOutput = simulateResiduals(fittedModel= modIns.3.1, plot = T)
  673. ##Results
  674. car::Anova(modIns.3.1)
  675. emmeans(modIns.3.1,pairwise~workers)
  676. ###Juvenile hormone esterase
  677. #Subset the Juvenile hormone esterase counts
  678. Carb.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g08195")
  679. #Model
  680. modCarb.3.1=lm(normalized_count~workers, data=Carb.3.1)
  681. simulationOutput = simulateResiduals(fittedModel= modCarb.3.1, plot = T)
  682. #Results
  683. car::Anova(modCarb.3.1)
  684. emmeans(modCarb.3.1,pairwise~workers)
  685. ###Esterase E4
  686. #Subset the Esterase E4 counts
  687. E4.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g15862")
  688. #Model
  689. modE4.3.1=lm(normalized_count~workers, data=E4.3.1)
  690. simulationOutput = simulateResiduals(fittedModel= modE4.3.1, plot = T)
  691. #Results
  692. car::Anova(modE4.3.1)
  693. emmeans(modE4.3.1,pairwise~workers)
  694. ###Protein doublesex
  695. #Subset the Doublesex counts
  696. Dbs.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g04508")
  697. #Model
  698. modDbs.3.1=lm(normalized_count~workers, data=Dbs.3.1)
  699. simulationOutput = simulateResiduals(fittedModel= modDbs.3.1, plot = T)
  700. #Results
  701. car::Anova(modDbs.3.1)
  702. ###Protein henna
  703. #Subset the Tret1-2 homolog counts
  704. Henna.3.1=subset(normalized_counts_long.3.1, gene=="Lnig_g13064")
  705. #Model
  706. modHenna.3.1=lm(normalized_count~workers, data=Henna.3.1)
  707. simulationOutput = simulateResiduals(fittedModel= modHenna.3.1, plot = T)
  708. #Results
  709. car::Anova(modHenna.3.1)
  710. emmeans(modHenna.3.1,pairwise~workers)
  711. ###Prohormone-2
  712. #Subset the Prohormone-2 counts
  713. Pro.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g06899")
  714. #Model
  715. modPro.3.1=lm(normalized_count~workers, data=Pro.3.1)
  716. simulationOutput = simulateResiduals(fittedModel= modPro.3.1, plot = T)
  717. #Results
  718. car::Anova(modPro.3.1)
  719. emmeans(modPro.3.1,pairwise~workers)
  720. ###Pheromone-binding protein Gp-9
  721. #Subset the GP9 counts
  722. GP9.3.1=subset(normalized_counts_long.3.1, gene=="gLnigOBP1")
  723. #Model
  724. modGP9.3.1=lm(normalized_count~workers, data=GP9.3.1)
  725. simulationOutput = simulateResiduals(fittedModel= modGP9.3.1, plot = T)
  726. #Results
  727. car::Anova(modGP9.3.1)
  728. emmeans(modGP9.3.1,pairwise~workers)
  729. ###GR81
  730. #Subset the GR81 counts
  731. GR81.3.1 =subset(normalized_counts_long.3.1, gene=="gLnigGR81")
  732. #Model
  733. modGR81.3.1=lm(normalized_count~workers, data=GR81.3.1)
  734. simulationOutput = simulateResiduals(fittedModel= modGR81.3.1, plot = T)
  735. #Results
  736. car::Anova(modGR81.3.1)
  737. emmeans(modGR81.3.1,pairwise~workers)
  738. ###Tret1a
  739. #Subset the Tret1a counts
  740. Tret1a.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g01559")
  741. #Model
  742. modTret1a.3.1=lm(normalized_count~workers, data=Tret1a.3.1)
  743. simulationOutput = simulateResiduals(fittedModel= modTret1a.3.1, plot = T)
  744. #Results
  745. car::Anova(modTret1a.3.1)
  746. ###Tret1b
  747. #Subset the Tret1b counts
  748. Tret1b.3.1 =subset(normalized_counts_long.3.1, gene=="Lnig_g11948")
  749. #Model
  750. modTret1b.3.1=lm(normalized_count~workers, data=Tret1b.3.1)
  751. simulationOutput = simulateResiduals(fittedModel= modTret1b.3.1, plot = T)
  752. #Results
  753. car::Anova(modTret1b.3.1)
  754. emmeans(modTret1b.3.1,pairwise~workers)
  755. ###Tret1-2 homolog
  756. #Subset the Tret1-2 homolog counts
  757. Tret1_2.3.1=subset(normalized_counts_long.3.1, gene=="Lnig_g10144")
  758. #Model
  759. modTret1_2.3.1=lm(normalized_count~workers, data=Tret1_2.3.1)
  760. simulationOutput = simulateResiduals(fittedModel= modTret1_2.3.1, plot = T)
  761. #Results
  762. car::Anova(modTret1_2.3.1)
  763. emmeans(modTret1_2.3.1,pairwise~workers)
  764. ##3.3. Produce Supplementary Figure 2----------
  765. #data
  766. models <- list(
  767. Krh1 = modK.3.1,
  768. dPDK1 = moddPDK1.3.1,
  769. Insulin = modIns.3.1,
  770. JuvEsterase= modCarb.3.1,
  771. E4 = modE4.3.1,
  772. Dsx = modDbs.3.1,
  773. GP9 = modGP9.3.1,
  774. GR81 = modGR81.3.1,
  775. Tret1a = modTret1a.3.1,
  776. Tret1b = modTret1b.3.1,
  777. Tret1_2 = modTret1_2.3.1,
  778. Henna = modHenna.3.1,
  779. Prohormone = modPro.3.1
  780. )
  781. #Color of titles and means. I did it manually here. Recheck important
  782. title_pat <- gsub(" ", "", "brb brr rbb rrr b")
  783. title_vec <- strsplit(title_pat, "")[[1]]
  784. title_col <- ifelse(title_vec == "r", "darkgrey", "black")
  785. cb_palette <- c("darkgreen", "#0072B2", "#E69F00")
  786. padj_3.1 = list(
  787. Krh1 = car::Anova(modK.3.1)[["Pr(>F)"]][1],
  788. dPDK1 = car::Anova(moddPDK1.3.1)[["Pr(>F)"]][1],
  789. Insulin = car::Anova(modIns.3.1)[["Pr(>F)"]][1],
  790. JuvEsterase = car::Anova(modCarb.3.1)[["Pr(>F)"]][1],
  791. E4 = car::Anova(modE4.3.1)[["Pr(>F)"]][1],
  792. Dsx = car::Anova(modDbs.3.1)[["Pr(>F)"]][1],
  793. GP9 = car::Anova(modGP9.3.1)[["Pr(>F)"]][1],
  794. GR81 = car::Anova(modGR81.3.1)[["Pr(>F)"]][1],
  795. Tret1a = car::Anova(modTret1a.3.1)[["Pr(>F)"]][1],
  796. Tret1b = car::Anova(modTret1b.3.1)[["Pr(>F)"]][1],
  797. Tret1_2 = car::Anova(modTret1_2.3.1)[["Pr(>F)"]][1],
  798. Henna = car::Anova(modHenna.3.1)[["Pr(>F)"]][1],
  799. Prohormone = car::Anova(modPro.3.1)[["Pr(>F)"]][1])
  800. padj_vec_3 <- setNames(padj_3.1, names(padj_3.1))
  801. names(padj_vec_3)[names(padj_vec_3) == "Doublesex"] <- "Dsx"
  802. padj_vec_3 <- unlist(padj_vec_3, use.names = TRUE)
  803. #Letter build
  804. raw_cld_list <- vector("list", nrow(plot_info))
  805. for (i in seq_len(nrow(plot_info))) {
  806. padj_val <- padj_vec_3[plot_info$padj_key[i]]
  807. if (!is.na(padj_val) && padj_val < 0.05) {
  808. tryCatch({
  809. emm <- emmeans(models[[plot_info$padj_key[i]]], pairwise ~ workers)
  810. cld <- cld(emm)
  811. cld$.group <- toupper(trimws(cld$.group))
  812. num2letter <- function(s) {
  813. s <- gsub(" ", "", s)
  814. if (s == "") return("")
  815. digits <- strsplit(s, "")[[1]]
  816. letters <- sapply(digits,
  817. function(d) {
  818. if (d %in% as.character(1:26)) LETTERS[as.integer(d)] else d
  819. })
  820. paste0(letters, collapse = "")
  821. }
  822. cld$.group <- sapply(cld$.group, num2letter)
  823. raw_cld_list[[i]] <- data.frame(
  824. gene_id = plot_info$gene_id[i],
  825. workers = cld$workers,
  826. letter = cld$.group,
  827. stringsAsFactors = FALSE
  828. )
  829. }, error = function(e) {
  830. raw_cld_list[[i]] <<- NULL
  831. message("CLD failed for ", plot_info$gene_id[i], ": ", e$message)
  832. })
  833. } else {
  834. raw_cld_list[[i]] <- NULL
  835. }
  836. }
  837. cld_list <- raw_cld_list
  838. #Manual adjustments to make without workers group assignment always A
  839. cld_list[[1]]$letter <- c("C", "A", "B")
  840. cld_list[[5]]$letter <- c("B", "B", "A")
  841. cld_list[[7]]$letter <- c("B", "B", "A")
  842. cld_list[[8]]$letter <- c("B", "A", "A")
  843. cld_list[[10]]$letter <- c("B", "AB", "A")
  844. cld_list[[13]]$letter <- c("B", "A", "A")
  845. if (length(title_col) < nrow(plot_info))
  846. title_col <- rep(title_col, length.out = nrow(plot_info))
  847. all_plots <- lapply(seq_len(nrow(plot_info)), function(i) {
  848. make_q3_panel(plot_info[i, ], i,
  849. ncol_grid = 3,
  850. cld_df = cld_list[[i]])
  851. })
  852. final_figure <- plot_grid(
  853. plotlist = all_plots,
  854. nrow = 5,
  855. ncol = 3,
  856. labels = LETTERS,
  857. label_size = 20,
  858. label_fontface = "bold",
  859. align = "hv"
  860. )
  861. print(final_figure)
  862. #ggsave(
  863. # filename = "Supplementary_Figure_2.svg",
  864. # plot = final_figure,
  865. # width = 50,
  866. # height = 75,
  867. # units = "cm",
  868. # device = "svg"
  869. #)
  870. #Part 4: Comparison of the genes of interest among queens with workers, without workers and with workers separated-----
  871. ##4.1. Load count table and metadata information------
  872. #Only the brain samples for experiment E4 are used in this analysis
  873. txi_with_without_separated_workers <- readRDS("txi_with_without_separated_workers.rds")
  874. ##4.2. Analysis of expression differences-----
  875. #Create dds
  876. dds_3.2=DESeqDataSetFromMatrix(countData = txi_with_without_separated_workers$counts,
  877. colData=txi_with_without_separated_workers$meta,
  878. design=~workers)
  879. #To only keep genes expressed in at least half of the samples, we only keep rows at least 19 samples with a non-zero value
  880. dds50_3.2=dds_3.2[rowSums(counts(dds_3.2) >0)>= 19]
  881. #Normalized counts extraction
  882. dds_test_3.2=DESeq(dds50_3.2, test="LRT", reduced=~1)
  883. normalized_counts.3.2 = counts(dds_test_3.2, normalized = TRUE)
  884. #Pivot longer the dataframe
  885. normalized_counts_long.3.2 = normalized_counts.3.2 %>%
  886. as.data.frame() %>%
  887. rownames_to_column(var = "gene") %>%
  888. pivot_longer(
  889. cols = -gene,
  890. names_to = "sample",
  891. values_to = "normalized_count"
  892. )
  893. normalized_counts_long.3.2 = normalized_counts_long.3.2 %>%
  894. left_join(as.data.frame(txi_with_without_separated_workers$meta) %>% dplyr::select(sample, workers), by = "sample")
  895. ###Kr-h1
  896. #Subset kr-h1 counts
  897. Krup.3.2=subset(normalized_counts_long.3.2, gene=="Lnig_g01448")
  898. #Model
  899. modK.3.2=lm(normalized_count~workers, data=Krup.3.2)
  900. simulationOutput = simulateResiduals(fittedModel= modK.3.2, plot = T)
  901. #Results
  902. car::Anova(modK.3.2)
  903. emmeans(modK.3.2,pairwise~workers)
  904. ###PDK1
  905. #Subset the PDK1 counts
  906. dPDK1.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g11740")
  907. #Model
  908. moddPDK1.3.2=lm(normalized_count~workers, data=dPDK1.3.2)
  909. simulationOutput = simulateResiduals(fittedModel= moddPDK1.3.2, plot = T)
  910. #Results
  911. car::Anova(moddPDK1.3.2)
  912. emmeans(moddPDK1.3.2,pairwise~workers)
  913. ###Insulin
  914. #Subset the Insulin counts
  915. Ins.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g17128")
  916. #Model
  917. modIns.3.2=lm(normalized_count~workers, data=Ins.3.2)
  918. simulationOutput = simulateResiduals(fittedModel= modIns.3.2, plot = T)
  919. ##Results
  920. car::Anova(modIns.3.2)
  921. emmeans(modIns.3.2,pairwise~workers)
  922. ###Juvenile hormone esterase
  923. #Subset the Juvenile hormone esterase counts
  924. Carb.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g08195")
  925. #Model
  926. modCarb.3.2=lm(normalized_count~workers, data=Carb.3.2)
  927. simulationOutput = simulateResiduals(fittedModel= modCarb.3.2, plot = T)
  928. #Results
  929. car::Anova(modCarb.3.2)
  930. emmeans(modCarb.3.2,pairwise~workers)
  931. ###Esterase E4
  932. #Subset the Esterase E4 counts
  933. E4.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g15862")
  934. #Model
  935. modE4.3.2=lm(normalized_count~workers, data=E4.3.2)
  936. simulationOutput = simulateResiduals(fittedModel= modE4.3.2, plot = T)
  937. #Results
  938. car::Anova(modE4.3.2)
  939. ###Protein doublesex
  940. #Subset the Doublesex counts
  941. Dbs.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g04508")
  942. #Model
  943. modDbs.3.2=lm(normalized_count~workers, data=Dbs.3.2)
  944. simulationOutput = simulateResiduals(fittedModel= modDbs.3.2, plot = T)
  945. #Results
  946. car::Anova(modDbs.3.2)
  947. emmeans(modDbs.3.2,pairwise~workers)
  948. ###Protein henna
  949. #Subset the Protein henna counts
  950. Henna.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g13064")
  951. #Model
  952. modHenna.3.2=lm(log(normalized_count+1)~workers, data=Henna.3.2)
  953. simulationOutput = simulateResiduals(fittedModel= modHenna.3.2, plot = T)
  954. #Results
  955. car::Anova(modHenna.3.2)
  956. emmeans(modHenna.3.2,pairwise~workers)
  957. ###Prohormone-2
  958. #Subset the Prohormone-2 counts
  959. Pro.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g06899")
  960. #Model
  961. modPro.3.2=lm(normalized_count~workers, data=Pro.3.2)
  962. simulationOutput = simulateResiduals(fittedModel= modPro.3.2, plot = T)
  963. #Results
  964. car::Anova(modPro.3.2)
  965. emmeans(modPro.3.2,pairwise~workers)
  966. ###Pheromone binding protein GP-9
  967. #Subset the GP9 counts
  968. GP9.3.2=subset(normalized_counts_long.3.2, gene=="gLnigOBP1")
  969. #Model
  970. modGP9.3.2=lm(normalized_count~workers, data=GP9.3.2)
  971. simulationOutput = simulateResiduals(fittedModel= modGP9.3.2, plot = T)
  972. #Results
  973. car::Anova(modGP9.3.2)
  974. ###GR81
  975. #Subset the GR81 counts
  976. GR81.3.2=subset(normalized_counts_long.3.2, gene=="gLnigGR81")
  977. #Model
  978. modGR81.3.2=lm(normalized_count~workers, data=GR81.3.2)
  979. simulationOutput = simulateResiduals(fittedModel= modGR81.3.2, plot = T)
  980. #Results
  981. car::Anova(modGR81.3.2)
  982. emmeans(modGR81.3.2,pairwise~workers)
  983. ###Tret1a
  984. #Subset the Tret1a counts
  985. Tret1a.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g01559")
  986. #Model
  987. modTret1a.3.2=lm(normalized_count~workers, data=Tret1a.3.2)
  988. simulationOutput = simulateResiduals(fittedModel= modTret1a.3.2, plot = T)
  989. #Results
  990. car::Anova(modTret1a.3.2)
  991. emmeans(modTret1a.3.2,pairwise~workers)
  992. ###Tret1b
  993. #Subset the Tret1b counts
  994. Tret1b.3.2 =subset(normalized_counts_long.3.2, gene=="Lnig_g11948")
  995. #Model
  996. modTret1b.3.2=lm(log(normalized_count+1)~workers, data=Tret1b.3.2)
  997. simulationOutput = simulateResiduals(fittedModel= modTret1b.3.2, plot = T)
  998. #Results
  999. car::Anova(modTret1b.3.2)
  1000. emmeans(modTret1b.3.2,pairwise~workers)
  1001. ###Tret1-2 homolog
  1002. #Subset the Tret1-2 homolog counts
  1003. Tret1_2.3.2=subset(normalized_counts_long.3.2, gene=="Lnig_g10144")
  1004. #Model
  1005. modTret1_2.3.2=lm(normalized_count~workers, data=Tret1_2.3.2)
  1006. simulationOutput = simulateResiduals(fittedModel= modTret1_2.3.2, plot = T)
  1007. #Results
  1008. car::Anova(modTret1_2.3.2)
  1009. emmeans(modTret1_2.3.2, pairwise~workers)
  1010. ##4.3. Produce Supplementary Figure 4-----
  1011. #Again manually. Important to check
  1012. title_pat <- gsub(" ", "", "bbb brb brr rbb b")
  1013. title_vec <- strsplit(title_pat, "")[[1]]
  1014. title_col <- ifelse(title_vec == "r", "darkgrey", "black")
  1015. title_col <- rep(title_col, length.out = 13)
  1016. cb_palette_4 <- c("darkgreen", "#0072B2", "purple")
  1017. models_4 <- list(
  1018. Krh1 = modK.3.2,
  1019. dPDK1 = moddPDK1.3.2,
  1020. Insulin = modIns.3.2,
  1021. JuvEsterase = modCarb.3.2,
  1022. E4 = modE4.3.2,
  1023. Dsx = modDbs.3.2,
  1024. Henna = modHenna.3.2,
  1025. Prohormone = modPro.3.2,
  1026. GP9 = modGP9.3.2,
  1027. GR81 = modGR81.3.2,
  1028. Tret1a = modTret1a.3.2,
  1029. Tret1b = modTret1b.3.2,
  1030. Tret1_2 = modTret1_2.3.2
  1031. )
  1032. padj_3.2 = list(
  1033. Krh1 = car::Anova(modK.3.2)[["Pr(>F)"]][1],
  1034. dPDK1 = car::Anova(moddPDK1.3.2)[["Pr(>F)"]][1],
  1035. Insulin = car::Anova(modIns.3.2)[["Pr(>F)"]][1],
  1036. JuvEsterase = car::Anova(modCarb.3.2)[["Pr(>F)"]][1],
  1037. E4 = car::Anova(modE4.3.2)[["Pr(>F)"]][1],
  1038. Dsx = car::Anova(modDbs.3.2)[["Pr(>F)"]][1],
  1039. Henna = car::Anova(modHenna.3.2)[["Pr(>F)"]][1],
  1040. Prohormone = car::Anova(modPro.3.2)[["Pr(>F)"]][1],
  1041. GP9 = car::Anova(modGP9.3.2)[["Pr(>F)"]][1],
  1042. GR81 = car::Anova(modGR81.3.2)[["Pr(>F)"]][1],
  1043. Tret1a = car::Anova(modTret1a.3.2)[["Pr(>F)"]][1],
  1044. Tret1b = car::Anova(modTret1b.3.2)[["Pr(>F)"]][1],
  1045. Tret1_2 = car::Anova(modTret1_2.3.2)[["Pr(>F)"]][1])
  1046. padj_vec_4 <- setNames(padj_3.2, names(padj_3.2))
  1047. padj_vec_4 <- unlist(padj_vec_4, use.names = TRUE)
  1048. #Letter build
  1049. raw_cld_list_4 <- vector("list", nrow(plot_info))
  1050. for (i in seq_len(nrow(plot_info))) {
  1051. padj_val <- padj_vec_4[plot_info$padj_key[i] ]
  1052. if (!is.na(padj_val) && padj_val < 0.05) {
  1053. cld_tmp <- tryCatch({
  1054. emm <- emmeans(models_4[[ plot_info$padj_key[i] ]], pairwise ~ workers)
  1055. cld <- cld(emm)
  1056. cld$.group <- toupper(trimws(cld$.group))
  1057. num2letter <- function(s) {
  1058. s <- gsub(" ", "", s)
  1059. if (s == "") return("")
  1060. digits <- strsplit(s, "")[[1]]
  1061. letters <- sapply(digits,
  1062. function(d) {
  1063. if (d %in% as.character(1:26))
  1064. LETTERS[as.integer(d)] else d
  1065. })
  1066. paste0(letters, collapse = "")
  1067. }
  1068. cld$.group <- sapply(cld$.group, num2letter)
  1069. data.frame(
  1070. gene_id = plot_info$gene_id[i],
  1071. workers = cld$workers,
  1072. letter = cld$.group,
  1073. stringsAsFactors = FALSE
  1074. )
  1075. }, error = function(e) {
  1076. message("CLD failed for ", plot_info$gene_id[i], ": ", e$message)
  1077. NULL
  1078. })
  1079. raw_cld_list_4[[i]] <- if (!is.null(cld_tmp)) cld_tmp else NULL
  1080. } else {
  1081. raw_cld_list_4[[i]] <- NULL
  1082. }
  1083. }
  1084. cld_list_4 <- raw_cld_list_4
  1085. #Manual adjustments to make without workers group assigment always A
  1086. cld_list_4[[7]]$letter <- c("B", "A", "A")
  1087. cld_list_4[[8]]$letter <- c("B", "AB", "A")
  1088. cld_list_4[[10]]$letter <- c("B", "AB", "A")
  1089. cld_list_4[[11]]$letter <- c("A", "B", "B")
  1090. cld_list_4[[13]]$letter <- c("B", "A", "A")
  1091. if (length(title_col) < nrow(plot_info))
  1092. title_col <- rep(title_col, length.out = nrow(plot_info))
  1093. all_plots_4 <- lapply(seq_len(nrow(plot_info)), function(i) {
  1094. make_q4_panel(plot_info[i, ], i,
  1095. ncol_grid = 3,
  1096. cld_df = cld_list_4[[i]])
  1097. })
  1098. final_figure_4 <- plot_grid(
  1099. plotlist = all_plots_4,
  1100. nrow = 5,
  1101. ncol = 3,
  1102. labels = LETTERS[1:13],
  1103. label_size = 20,
  1104. label_fontface = "bold",
  1105. align = "hv"
  1106. )
  1107. print(final_figure_4)
  1108. #ggsave(
  1109. # filename = "Supplementary_Figure_4.svg",
  1110. # plot = final_figure_4,
  1111. # width = 50,
  1112. # height = 75,
  1113. # units = "cm",
  1114. # device = "svg"
  1115. #)
  1116. ##4.4. Analysis of queen behavior and antennation through the wire mesh -----
  1117. #Import data
  1118. Antennation=read.table("data_queen_antennation_wire_mesh.txt", h=T, stringsAsFactors = T)
  1119. Antennation_subject=subset(as.data.frame(txi_with_without_separated_workers$meta), workers=="separated_workers")
  1120. Antennation_subject=sub("B$","",Antennation_subject$sample)
  1121. Antennation=subset(Antennation, Subject %in% Antennation_subject)
  1122. #Number of occurrence per behavior
  1123. Antennation_counts <- Antennation %>%
  1124. group_by(Subject , Behavior) %>%
  1125. summarise(nb_occurrences = n(), .groups = "drop")
  1126. All_queens <- data.frame(
  1127. Subject = unique(Antennation_subject)
  1128. )
  1129. #Number of each queen behavior
  1130. Antennation_all <- All_queens %>%
  1131. left_join(Antennation_counts, by = "Subject") %>%
  1132. mutate(nb_occurrences = ifelse(is.na(nb_occurrences), 0, nb_occurrences))
  1133. #Plot Number of antennation through the wire mesh for each queen
  1134. Antennation_Occurence <- ggplot(
  1135. Antennation_all,
  1136. aes(
  1137. x = Subject,
  1138. y = nb_occurrences,
  1139. fill = Behavior
  1140. )
  1141. ) +
  1142. geom_col() +
  1143. labs(
  1144. x = "Queen identity",
  1145. y = "Number of occurrences"
  1146. ) +
  1147. theme_classic(base_size = 18) +
  1148. scale_fill_viridis_d(
  1149. option = "E",
  1150. na.translate = FALSE,
  1151. name=NULL,
  1152. labels = c(
  1153. "Number of times that queens made antennal contact with workers" = "Number of times that queens made antennal\ncontact with workers",
  1154. "Number of times that queens passed their antennae through or above the wire mesh without touching a worker" = "Number of times that queens passed their antennae\nthrough or above the wire mesh without touching a worker"
  1155. )
  1156. ) +
  1157. theme(
  1158. plot.title = element_text(size = 20, face = "bold", hjust = 0.5),
  1159. strip.text = element_text(size = 16, face = "bold"),
  1160. panel.border = element_blank(),
  1161. panel.grid.major = element_blank(),
  1162. panel.grid.minor = element_blank(),
  1163. axis.line = element_line(colour = "black"),
  1164. legend.position = "top",
  1165. legend.justification = "center",
  1166. legend.direction = "horizontal"
  1167. )
  1168. print(Antennation_Occurence)
  1169. ###Duration of antennal contacts with workers
  1170. Duration=Antennation
  1171. Duration$Antennation=NA
  1172. #Creation of a new column with the duration of both antennation behaviors with the workers,
  1173. #and 0 for all other behaviors
  1174. Duration$Antennation <- ifelse(Duration$Behavior =="Number of times that queens made antennal contact with workers",
  1175. as.numeric(Duration$Duration),0)
  1176. #Addition of the E4_53 subject that did not perform any behavior
  1177. Duration <- Duration %>%
  1178. dplyr::bind_rows(data.frame(Subject = "E4_53",Antennation = 0)
  1179. )
  1180. Duration$Duration=as.numeric(Duration$Duration)
  1181. #Creation of a dataframe with the total duration of antennation for each queen
  1182. Duration_Both_Antennation <- All_queens %>%
  1183. left_join(
  1184. Duration %>%
  1185. group_by(Subject)%>%
  1186. summarise(
  1187. Total_duration = sum(Duration, na.rm = TRUE),
  1188. .groups = "drop"
  1189. ),
  1190. by = "Subject"
  1191. )
  1192. #Calculation of the mean and standard deviation of the d'antennation
  1193. Duration_mean=subset(Duration_Both_Antennation, Total_duration >0)
  1194. mean(Duration_mean$Total_duration)
  1195. sd(Duration_mean$Total_duration)
  1196. #Antennation duration plot
  1197. Duration_plot <- ggplot(
  1198. Duration_Both_Antennation,
  1199. aes(x = Subject, y = Total_duration, fill = Subject)
  1200. ) +
  1201. geom_col(width = 0.7, fill="#00204DFF") +
  1202. labs(
  1203. x = "Queen identity",
  1204. y = "Total duration of antennal contacts with workers (s)"
  1205. ) +
  1206. theme_classic(base_size = 18) +
  1207. theme(
  1208. axis.text.x = element_text(size = 14),
  1209. plot.title = element_text(color = "#00204DFF", size = 14, face = "bold"),
  1210. panel.border = element_blank(),
  1211. panel.grid.major = element_blank(),
  1212. panel.grid.minor = element_blank(),
  1213. axis.line = element_line(colour = "black"),
  1214. legend.position = "none"
  1215. )
  1216. print(Duration_plot)
  1217. #Creation of a panel with both plots
  1218. Antennation_2plots=ggarrange(Antennation_Occurence,Duration_plot, labels="AUTO")
  1219. Antennation_2plots
  1220. #Export
  1221. #ggsave(
  1222. # filename = "Supplementary_Figure_3.svg",
  1223. # plot = Antennation_2plots,
  1224. # width = 22,
  1225. # height = 10,
  1226. # dpi = 600,
  1227. # bg = "white",
  1228. # device = "svg")
  1229. #Part 5: Comparison of the genes of interest between queens with and without larvae-----
  1230. ##5.1. Load count table and metadata information------
  1231. txi_with_without_larvae <- readRDS("txi_with_without_larvae.rds")
  1232. ##5.2. Analysis of expression differences-----
  1233. #Create dds
  1234. dds_3.3=DESeqDataSetFromMatrix(countData = txi_with_without_larvae$counts,
  1235. colData=txi_with_without_larvae$meta,
  1236. design=~workers)
  1237. #To only keep genes expressed in at least half of the samples, we only keep rows with at least 13 samples with a non-zero value
  1238. dds50_3.3=dds_3.3[rowSums(counts(dds_3.3) >0)>= 13]
  1239. #Normalized counts extraction
  1240. dds_test_3.3=DESeq(dds50_3.3, test="LRT", reduced=~1)
  1241. normalized_counts.3.3 = counts(dds_test_3.3, normalized = TRUE)
  1242. #Pivot longer the dataframe
  1243. normalized_counts_long.3.3 = normalized_counts.3.3 %>%
  1244. as.data.frame() %>%
  1245. rownames_to_column(var = "gene") %>%
  1246. pivot_longer(
  1247. cols = -gene,
  1248. names_to = "sample",
  1249. values_to = "normalized_count"
  1250. )
  1251. normalized_counts_long.3.3 = normalized_counts_long.3.3 %>%
  1252. left_join(as.data.frame(txi_with_without_larvae$meta) %>% dplyr::select(sample, workers), by = "sample")
  1253. ###Kr-h1
  1254. #Subset Kr-h1 counts
  1255. Krup.3.3=subset(normalized_counts_long.3.3, gene=="Lnig_g01448")
  1256. #Model
  1257. modK.3.3=lm(normalized_count~workers, data=Krup.3.3)
  1258. simulationOutput = simulateResiduals(fittedModel= modK.3.3, plot = T)
  1259. #Results
  1260. car::Anova(modK.3.3)
  1261. summary(modK.3.3)
  1262. ###PDK1
  1263. #Subset the dPDK1 counts
  1264. dPDK1.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g11740")
  1265. #Model
  1266. moddPDK1.3.3=lm(normalized_count~workers, data=dPDK1.3.3)
  1267. simulationOutput = simulateResiduals(fittedModel= moddPDK1.3.3, plot = T)
  1268. #Results
  1269. car::Anova(moddPDK1.3.3)
  1270. summary(moddPDK1.3.3)
  1271. ###Insulin
  1272. #Subset the Insulin counts
  1273. Ins.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g17128")
  1274. #Model
  1275. modIns.3.3=lm(normalized_count~workers,data=Ins.3.3)
  1276. simulationOutput = simulateResiduals(fittedModel= modIns.3.3, plot = T)
  1277. ##Results
  1278. car::Anova(modIns.3.3)
  1279. summary(modIns.3.3)
  1280. ###Juvenile hormone esterase
  1281. #Subset the Juvenile hormone esterase counts
  1282. Carb.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g08195")
  1283. #Model
  1284. modCarb.3.3=lm(normalized_count~workers, data=Carb.3.3)
  1285. simulationOutput = simulateResiduals(fittedModel= modCarb.3.3, plot = T)
  1286. #Results
  1287. car::Anova(modCarb.3.3)
  1288. summary(modCarb.3.3)
  1289. ###Esterase E4
  1290. #Subset the Esterase E4 counts
  1291. E4.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g15862")
  1292. #Model
  1293. modE4.3.3=lm(normalized_count~workers, data=E4.3.3)
  1294. simulationOutput = simulateResiduals(fittedModel= modE4.3.3, plot = T)
  1295. #Results
  1296. car::Anova(modE4.3.3)
  1297. summary(modE4.3.3)
  1298. ###Doublesex
  1299. #Subset the Doublesex counts
  1300. Dbs.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g04508")
  1301. #Model
  1302. modDbs.3.3=lm(normalized_count~workers, data=Dbs.3.3)
  1303. simulationOutput = simulateResiduals(fittedModel= modDbs.3.3, plot = T)
  1304. #Results
  1305. car::Anova(modDbs.3.3)
  1306. summary(modDbs.3.3)
  1307. ###Protein henna
  1308. #Subset the Protein henna counts
  1309. Henna.3.3=subset(normalized_counts_long.3.3, gene=="Lnig_g13064")
  1310. #Model
  1311. modHenna.3.3=lm(normalized_count~workers, data=Henna.3.3)
  1312. simulationOutput = simulateResiduals(fittedModel= modHenna.3.3, plot = T)
  1313. #Results
  1314. car::Anova(modHenna.3.3)
  1315. summary(modHenna.3.3)
  1316. ###Prohormone-2
  1317. #Subset the Prohormone-2 counts
  1318. Pro.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g06899")
  1319. #Model
  1320. modPro.3.3=lm(normalized_count~workers, data=Pro.3.3)
  1321. simulationOutput = simulateResiduals(fittedModel= modPro.3.3, plot = T)
  1322. #Results
  1323. car::Anova(modPro.3.3)
  1324. summary(modPro.3.3)
  1325. ###Pheromone binding protein GP-9
  1326. #Subset the GP9 counts
  1327. GP9.3.3=subset(normalized_counts_long.3.3, gene=="gLnigOBP1")
  1328. #Model
  1329. modGP9.3.3=lm(normalized_count~workers, data=GP9.3.3)
  1330. simulationOutput = simulateResiduals(fittedModel= modGP9.3.3, plot = T)
  1331. #Results
  1332. car::Anova(modGP9.3.3)
  1333. summary(modGP9.3.3)
  1334. ### GR81
  1335. #Subset the GR81 counts
  1336. GR81.3.3=subset(normalized_counts_long.3.3, gene=="gLnigGR81")
  1337. #Model
  1338. modGR81.3.3=lm(normalized_count~workers, data=GR81.3.3)
  1339. simulationOutput = simulateResiduals(fittedModel= modGR81.3.3, plot = T)
  1340. #Results
  1341. car::Anova(modGR81.3.3)
  1342. summary(modGR81.3.3)
  1343. ###Tret1a
  1344. #Subset the Tret1a counts
  1345. Tret1a.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g01559")
  1346. #Model
  1347. modTret1a.3.3=lm(normalized_count~workers,data=Tret1a.3.3)
  1348. simulationOutput = simulateResiduals(fittedModel= modTret1a.3.3, plot = T)
  1349. #Results
  1350. car::Anova(modTret1a.3.3)
  1351. summary(modTret1a.3.3)
  1352. ###Tret1b
  1353. #Subset the Tret1b counts
  1354. Tret1b.3.3 =subset(normalized_counts_long.3.3, gene=="Lnig_g11948")
  1355. #Model
  1356. modTret1b.3.3=lm(normalized_count~workers, data=Tret1b.3.3)
  1357. simulationOutput = simulateResiduals(fittedModel= modTret1b.3.3, plot = T)
  1358. #Results
  1359. car::Anova(modTret1b.3.3)
  1360. summary(modTret1b.3.3)
  1361. ###Tret1-2 homolog
  1362. #Subset the Tret1-2 counts
  1363. Tret1_2.3.3=subset(normalized_counts_long.3.3, gene=="Lnig_g10144")
  1364. #Model
  1365. modTret1_2.3.3=lm(normalized_count~workers, data=Tret1_2.3.3)
  1366. simulationOutput = simulateResiduals(fittedModel= modTret1_2.3.3, plot = T)
  1367. #Results
  1368. car::Anova(modTret1_2.3.3)
  1369. summary(modTret1_2.3.3)
  1370. ##5.3. Produce Supplementary Figure 5-----
  1371. #Manual title colors
  1372. title_pat <- gsub(" ", "", "bbb bbb bbb bbb b")
  1373. title_vec <- strsplit(title_pat, "")[[1]]
  1374. title_col <- ifelse(title_vec == "r", "darkgrey", "black")
  1375. title_col <- rep(title_col, length.out = 13)
  1376. cb_palette_5 <- c(without_larvae = "#46D3E9" ,with_larvae = "#D55E00")
  1377. models_5 <- list(
  1378. Krh1 = modK.3.3,
  1379. dPDK1 = moddPDK1.3.3,
  1380. Insulin = modIns.3.3,
  1381. JHE = modCarb.3.3,
  1382. E4 = modE4.3.3,
  1383. Dsx = modDbs.3.3,
  1384. GP9 = modGP9.3.3,
  1385. GR81 = modGR81.3.3,
  1386. Tret1a = modTret1a.3.3,
  1387. Tret1b = modTret1b.3.3,
  1388. Tret1_2 = modTret1_2.3.3,
  1389. Henna = modHenna.3.3,
  1390. Prohormone = modPro.3.3
  1391. )
  1392. padj_3.3 <- list(
  1393. Krh1 = car::Anova(modK.3.3) [["Pr(>F)"]][1],
  1394. dPDK1 = car::Anova(moddPDK1.3.3)[["Pr(>F)"]][1],
  1395. Insulin = car::Anova(modIns.3.3)[["Pr(>F)"]][1],
  1396. JuvEsterase = car::Anova(modCarb.3.3)[["Pr(>F)"]][1],
  1397. E4 = car::Anova(modE4.3.3)[["Pr(>F)"]][1],
  1398. Dsx = car::Anova(modDbs.3.3)[["Pr(>F)"]][1],
  1399. GP9 = car::Anova(modGP9.3.3)[["Pr(>F)"]][1],
  1400. GR81 = car::Anova(modGR81.3.3)[["Pr(>F)"]][1],
  1401. Tret1a = car::Anova(modTret1a.3.3)[["Pr(>F)"]][1],
  1402. Tret1b = car::Anova(modTret1b.3.3)[["Pr(>F)"]][1],
  1403. Tret1_2 = car::Anova(modTret1_2.3.3)[["Pr(>F)"]][1],
  1404. Henna = car::Anova(modHenna.3.3)[["Pr(>F)"]][1],
  1405. Prohormone = car::Anova(modPro.3.3)[["Pr(>F)"]][1]
  1406. )
  1407. padj_vec_5 <- setNames(padj_3.3, names(padj_3.3))
  1408. padj_vec_5 <- unlist(padj_vec_5, use.names = TRUE)
  1409. all_plots_5 <- lapply(seq_len(nrow(plot_info)), function(i) {
  1410. make_q5_panel(plot_info[i, ], i, ncol_grid = 3)
  1411. })
  1412. final_figure_5 <- plot_grid(
  1413. plotlist = all_plots_5,
  1414. nrow = 5,
  1415. ncol = 3,
  1416. labels = LETTERS[1:13],
  1417. label_size = 20,
  1418. label_fontface = "bold",
  1419. align = "hv"
  1420. )
  1421. print(final_figure_5)
  1422. #ggsave(
  1423. # filename = "Supplementary_Figure_5.svg",
  1424. # plot = final_figure_5,
  1425. # width = 50,
  1426. # height = 75,
  1427. # units = "cm",
  1428. # device = "svg"
  1429. #)
  1430. #Production of Figure 3-----
  1431. #Addition of y axis label to plot D
  1432. all_plots[[3]] <- all_plots[[3]] +
  1433. ggplot2::ylab("Normalized read counts") +
  1434. ggplot2::theme(axis.title.y = ggplot2::element_text(angle = 90,size = 20))
  1435. figure3_plots <- c(
  1436. all_plots[1], all_plots_4[1],
  1437. all_plots_5[1],all_plots[3], all_plots_4[3],
  1438. all_plots_5[3],all_plots[4], all_plots_4[4],
  1439. all_plots_5[4],all_plots[13], all_plots_4[13],
  1440. all_plots_5[13]
  1441. )
  1442. keep_y <- seq(1, length(figure3_plots), by = 3)
  1443. figure3_plots_clean <- lapply(seq_along(figure3_plots), function(i) {
  1444. if (!(i %in% keep_y)) {
  1445. figure3_plots[[i]] +
  1446. ggplot2::theme(axis.title.y = ggplot2::element_blank())
  1447. } else {
  1448. figure3_plots[[i]]
  1449. }
  1450. })
  1451. figure3_grid <- cowplot::plot_grid(
  1452. plotlist = figure3_plots_clean,
  1453. nrow = 5,
  1454. ncol = 3,
  1455. labels = LETTERS,
  1456. label_size = 20,
  1457. label_fontface = "bold",
  1458. align = "hv"
  1459. )
  1460. col_titles <- plot_grid(
  1461. ggdraw() + draw_label("Experiment A", fontface = "bold", size = 30, x = 0.55),
  1462. ggdraw() + draw_label("Experiment B", fontface = "bold", size = 30, x = 0.55),
  1463. ggdraw() + draw_label("Experiment C", fontface = "bold", size = 30, x = 0.55),
  1464. ncol = 3
  1465. )
  1466. figure3 <- plot_grid(
  1467. col_titles,
  1468. figure3_grid,
  1469. ncol = 1,
  1470. rel_heights = c(0.05, 0.95)
  1471. )
  1472. figure3
  1473. #ggsave(
  1474. # filename = "Figure_3.svg",
  1475. # plot = figure3,
  1476. # width = 50,
  1477. # height = 75,
  1478. #units = "cm",
  1479. # device = "svg"
  1480. #)

R script_Mol_Ecol_2026.R, no license · at the source

Overview

  1. Insect Biology Research Institute, UMR 7261, CNRS, University of Tours, Tours, France
  2. Institute of Organismic and Molecular Evolution, Johannes Gutenberg University, Mainz, Germany
Journal: Molecular ecology, volume 35, issue 21, article e70614
Dates: received 7 May 2026; accepted 15 October 2026; published online 1 November 2026; in print November 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1111/mec.70614 · PMID 42968400 · PMCID PMC13724878 · OpenAlex W7225107947
Open access: hybrid, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: genetics / omics (modality), other (organism), cellular / molecular (subfield)
Methods: Statistics, Preprocessing
Keywords: colony foundation, division of labour, gene expression, social behaviour, social insects
MeSH: Ants*, Behavior, Animal*, Brain*, Social Behavior*, Transcriptome*, Animals, Female, Gene Expression Profiling, Juvenile Hormones (* major topic)
Topic: Insect and Arachnid Ecology and Behavior (Insect Science, Agricultural and Biological Sciences), according to OpenAlex
Funding: Deutsche Forschungsgemeinschaft (GRK2526/1-407023052); Agence Nationale de la Recherche (ANR-23-CE02-0004)
Citations: not cited yet (Europe PMC); 130 references in the paper

Abstract

Insect societies are characterised by a reproductive division of labour between queens that monopolise egg production and workers that perform all non‐reproductive tasks. The queen specialisation in egg laying is typically considered to be constitutive and independent of environmental conditions, but this common assumption was recently challenged by the finding that the presence of workers both induces and maintains the queen specialisation in several species of ants. However, the molecular changes that translate worker presence into queen behavioural specialisation remain unknown. To investigate the transcriptomic basis of the socially regulated specialisation of ant queens, we produced 93 individual brain transcriptomes from queens of the black garden ant Lasius niger that were experimentally maintained in different social environments. Our transcriptomic analyses revealed that 123 genes were differentially expressed between the brains of queens maintained with and without workers. These genes were enriched for processes related to brain energy metabolism and neuronal plasticity and signalling. By investigating the response of selected genes of interest to further manipulations of the social environment, we identified four candidate genes that fully mirrored the queen behavioural changes. Specifically, their expression changes in response to worker presence were reversible and they did not respond to mere cues of the worker presence. These candidate genes included the transcription factor Krüppel‐homologue 1 and other genes in the juvenile hormone and insulin signalling pathways, both known to regulate multiple aspects of division of labour in social insects. By producing a robust, well‐replicated and well‐controlled investigation of the transcriptomic basis of the behavioural specialisation of ant queens, this study provides a better understanding of the emergence and maintenance of reproductive division of labour in insect societies.

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

Repository

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

supp:PMC13724878/MEC-35-e70614-s003.zip

License: none: the authors keep all their rights
State: the link answers, verified on 3 November 2026
Evidence: files inventoried
Languages: R (1)
Size: 1 file, 1 script
Software Heritage: not checked
Found in: the supplementary material
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: car (1 file), cowplot (1 file), DESeq2 (1 file), emmeans (1 file), ggplot2 (1 file), ggpubr (1 file), lme4 (1 file), multcomp (1 file), patchwork (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 3 November 2026: the link answers (HTTP 200)
  • 3 November 2026: the link answers (HTTP 200)
1 file

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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 1 script, each with its path and the digest of its content;
  • 9 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 Statement

The code and input data for all analyses are provided in Data S1. The raw RNA‐seq sequences used in this study are publicly available from the NCBI BioProject PRJNA1548310.

Reproduced under the paper's license (CC BY), 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 2, 11 November 2026

  • Publisher: n/a → Wiley

Version 1, 3 November 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 5 keywords, 9 MeSH terms, 2 funders, 125 references.

Cite

This paper

Le Lay, H. E., Bolder, M. F., Colgan, T. J., & Libbrecht, R. (2026). Brain Transcriptomic Changes Associated With the Socially Regulated Behavioural Specialisation of Ant Queens. Molecular ecology, 35(21), e70614. https://doi.org/10.1111/mec.70614

BibTeX

@article{lelay2026brain,
author = {Le Lay, Hugo E and Bolder, Maximilian F and Colgan, Thomas J and Libbrecht, Romain},
title = {{Brain Transcriptomic Changes Associated With the Socially Regulated Behavioural Specialisation of Ant Queens}},
journal = {Molecular ecology},
year = {2026},
month = nov,
volume = {35},
number = {21},
pages = {e70614},
publisher = {Wiley},
issn = {0962-1083},
doi = {10.1111/mec.70614},
url = {https://doi.org/10.1111/mec.70614},
pmid = {42968400},
pmcid = {PMC13724878}
}

RIS

TY - JOUR
AU - Le Lay, Hugo E
AU - Bolder, Maximilian F
AU - Colgan, Thomas J
AU - Libbrecht, Romain
TI - Brain Transcriptomic Changes Associated With the Socially Regulated Behavioural Specialisation of Ant Queens
T2 - Molecular ecology
J2 - Mol Ecol
PY - 2026
DA - 2026/11/01
VL - 35
IS - 21
SP - e70614
SN - 0962-1083
PB - Wiley
DO - 10.1111/mec.70614
UR - https://doi.org/10.1111/mec.70614
LA - en
ER -

CSL-JSON

{
"id": "10.1111/mec.70614",
"type": "article-journal",
"title": "Brain Transcriptomic Changes Associated With the Socially Regulated Behavioural Specialisation of Ant Queens",
"container-title": "Molecular ecology",
"author": [
{
"family": "Le Lay",
"given": "Hugo E"
},
{
"family": "Bolder",
"given": "Maximilian F"
},
{
"family": "Colgan",
"given": "Thomas J"
},
{
"family": "Libbrecht",
"given": "Romain"
}
],
"container-title-short": "Mol Ecol",
"volume": "35",
"issue": "21",
"page": "e70614",
"DOI": "10.1111/mec.70614",
"PMID": "42968400",
"PMCID": "PMC13724878",
"ISSN": "0962-1083",
"publisher": "Wiley",
"URL": "https://doi.org/10.1111/mec.70614",
"language": "en",
"issued": {
"date-parts": [
[
2026,
11,
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.1111/mec.70360 [code]
Unmated Queens Show Worker-Like Behaviour and Gene Expression in Polygynous Colonies of the Ant Stigmatomma pallipes.
Journal: Molecular ecology
In common: other, genetics / omics, cellular / molecular, 16 references, 2 authors
[2] doi:10.1038/s42003-023-05273-4 [code]
Task-specific odorant receptor expression in worker antennae indicates that sensory filters regulate division of labor in ants.
Journal: Communications biology
In common: other, genetics / omics, cellular / molecular, 16 references, author Romain Libbrecht
[3] doi:10.1371/journal.pbio.3002763 [code]
An evolutionarily conserved pathway mediated by neuroparsin-A regulates reproductive plasticity in ants.
Journal: PLoS biology
In common: other, cellular / molecular, 12 references
[4] doi:10.1242/jeb.246785
Effects of microRNA-305 knockdown on brain gene expression associated with division of labor in honey bee colonies (Apis mellifera).
Journal: The Journal of experimental biology
In common: other, genetics / omics, cellular / molecular, 9 references
[5] doi:10.1038/s41586-026-10747-6 [code]
Ancient feeding-related neuropeptides regulate alloparenting in ants.
Journal: Nature
In common: patchwork, ggpubr, ggplot2, 1 other tool, other, 7 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: multcomp, emmeans, car, 7 other tools, genetics / omics, 2 references
[7] doi:10.1038/s43587-025-00816-2 [code]
Interleukin-12 signaling drives Alzheimer's disease pathology through disrupting neuronal and oligodendrocyte homeostasis.
Journal: Nature aging
In common: multcomp, car, DESeq2, 6 other tools, cellular / molecular, 1 reference
[8] doi:10.1111/gbb.70007 [code]
The brain atlas of a subsocial bee reflects that of eusocial Hymenoptera.
Journal: Genes, brain, and behavior
In common: patchwork, cowplot, ggplot2, 1 other tool, other, genetics / omics, 4 references
[9] doi:10.1371/journal.pone.0353990 [code]
Positive mood enhances accessibility of unrelated concepts in the first language but not in the foreign language.
Journal: PloS one
In common: multcomp, emmeans, car, 5 other tools, 1 reference
[10] doi:10.1002/aur.70042 [code]
Multisensory Integration of Naturalistic Speech and Gestures in Autistic Adults.
Journal: Autism research : official journal of the International Society for Autism Research
In common: multcomp, emmeans, car, 6 other tools

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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1111/mec.70614.svg)](https://openscicode.org/badge/doi/10.1111/mec.70614)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1111/mec.70614.svg
   :target: https://openscicode.org/badge/doi/10.1111/mec.70614
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1111/mec.70614"><img src="https://openscicode.org/badge/doi/10.1111/mec.70614.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

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