OSCR

A Multi-Network Approach Identifies Proteins Related to Dendritic Spines in Alzheimer's Disease.

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] § Materials and Methods › Weighted coexpression network analysis ↔ eNeuro_paper_code_wgcna_SE2.R, lines 552–590 · score 0.92 · merge cut height, pamRespectsDendro, pamStage, reassignment threshold, deepSplit, blockwiseModules
  2. [2] § Materials and Methods › Dendritic spine morphometry analysis ↔ eNeuro_paper_code_wgcna_SE2.R, lines 765–822 · score 0.83 · neck ratio, head ratio, backbone length, Head diameter, Spine density, filopodia
  3. [3] § Materials and Methods › Batch correction and data preprocessing ↔ eNeuro_paper_code_wgcna_SE2.R, lines 3–58 · score 0.78 · nonparametric bootstrap regression, Median Polish, confounding, variance, TAMPOR, covariates
  4. [4] § Results › Weighted coexpression network analysis identifies modules associated with AD neuropathology ↔ eNeuro_paper_code_wgcna_SE2.R, lines 2120–2208 · score 0.73 · cellular components, molecular functions, biological processes, GO terms, ranked, biology
  5. [5] § Results › SpeakEasy2 network analysis identifies modules associated with AD neuropathology ↔ eNeuro_paper_code_wgcna_SE2.R, lines 552–590 · score 0.61 · merge cut height, deepSplit, smaller, threshold, connecting, power
  6. [6] § Results › Protein associations with dendritic spine density and morphology ↔ eNeuro_paper_code_wgcna_SE2.R, lines 1363–1423 · score 0.56 · head diameter, spine density, spine trait, spine length, stubby, thin
  7. [7] § Results › Aβ42 peptide measurements align with AD neuropathology and cognitive scores ↔ eNeuro_paper_code_wgcna_SE2.R, lines 1310–1361 · score 0.55 · stubby spines, thin spines, mushroom spines, DP, filopodia, frontal
  8. [8] § Results › Aβ42 peptide measurements align with AD neuropathology and cognitive scores ↔ eNeuro_paper_code_wgcna_SE2.R, lines 3–58 · score 0.53 · outlier removal, confounding, variance, Protein abundances, Emory, covariates
  9. [9] § Results › SE2 network integration of dendritic spine density and morphology identifies synapse module ↔ eNeuro_paper_code_wgcna_SE2.R, lines 1363–1423 · score 0.52 · thin spine density, spine traits, spine length, volume, eigenproteins, correlated

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 · 2,241 lines · 104 KB · MIT · 9 matches

  1. # eNeuro paper code
  2. ##########################################################################################################
  3. ## Eric Dammer - adapted code for WGCNA from Neelroop Parikshak, Vivek Swarup, and Divya Nandakumar
  4. ## SeyfriedLab&ProteomicsCorePipeline.R
  5. ##
  6. ## Applied to Emory 41 BULK Data from Herskowitz Lab at UAB
  7. ##
  8. ## Goal: This code will carry out coexpression network analysis and systems biology for Protein Abundance
  9. ##########################################################################################################
  10. ## CODE BLOCKS:
  11. # Set up environment, load data,
  12. # Clean up abundance matrix of rows/gene products with too many 0 FPKM/TPM or NA protein abundance values
  13. # Perform TAMPOR robust median polish removal of unwanted batch covariance (preserving wanted biological variance)
  14. # Outlier Removal using WGCNA z.k connectivity detection per each case-sample
  15. # Nonparametric Bootstrap Regression of unwanted variance (covariance with confounding traits)
  16. # WGCNA blockwiseModules network construction
  17. # GlobalNetworkPlots and kMEtable Output
  18. # One-Step GO-ELITE -- configurable user parameter section at top of code block
  19. # Speakeasy2 code
  20. ## Set up environment
  21. # (Always run the below code after loading a saved.image .Rdata)
  22. #############################################################################################
  23. ## Set folders and libraries
  24. rm(list=ls())
  25. options(stringsAsFactors=FALSE)
  26. rootdir <- "C:/Users/ehobby/Documents/EH_Emory41_Update/"
  27. #"C:/Users/liuey/Documents/ROSMAP_WPCNA/" # This is the folder containing all of the analysis scripts, input, and output for this project
  28. #functiondir <- "Code"
  29. datadir <- "Input"
  30. outputfigs <- "Figures"
  31. outputtabs <- "Tables"
  32. setwd(rootdir)
  33. #functiondir = paste0(rootdir,functiondir,"/")
  34. datadir = paste0(rootdir,datadir,"/")
  35. outputfigs = paste0(rootdir,outputfigs,"/")
  36. outputtabs = paste0(rootdir,outputtabs,"/")
  37. dir.create(file.path(outputfigs)) #harmless warning given if already exists
  38. dir.create(file.path(outputtabs))
  39. library(WGCNA) # Network analysis package
  40. library(NMF) # this package has a great annotated heatmap function - aheatmap
  41. library(igraph)
  42. library(ggplot2)
  43. library(RColorBrewer)
  44. library(Cairo) # nicer graphics, anti-aliased, etc. --text from windows output PDFs using CairoPDF() function may not load in Illustrator, though -- so also use the pdf() standard output function when generating PDF figures
  45. ##Only for macs:
  46. #CairoFonts(regular="Arial:style=Regular",bold="Arial:style=Bold",italic="Arial:style=Italic",bolditalic="Arial:style=Bold Italic,BoldItalic",symbol="Symbol")
  47. #note: other libraries are loaded as needed in certain blocks but also listed below for completeness
  48. library(reshape2) #TAMPOR block
  49. library(gridExtra) #TAMPOR block
  50. library(ggpubr) #TAMPOR block
  51. library("doParallel") #Bootstrap Regression block and GlobalNetworkPlots
  52. library("biomaRt") #Fisher Exact Test/list overlap block (enables cross-species lookup)
  53. library("NMF") #WGCNA GlobalNetworkPlots - eigengene heatmap and clustering
  54. library("plotly") #Volcano Figure Generation
  55. library("stringr") #GO-Elite block
  56. library(boot) #bootstrap regression block
  57. library(tidyverse)
  58. library(gplots) #for col2hex() fn (module plots at end)
  59. ##Check and change your input filenames below
  60. inputAbundanceFile="post_TAMPOR_cleanDat.csv" #abundance.CSV: rows are genes/proteins, columns are samples
  61. inputTraitsFile="post_TAMPOR_traits.csv" #traits.CSV: rows are samples, columns are traits (as many as possible should be numerically coded)
  62. #IMPORTANT: Make sure your inputTraitsFile has a "Group" column calling out (expected) different subsets of case-samples. Groups can be numerically coded if a single severity scale applies, but each group (number) should be used for at least 3 samples.
  63. ##Part of most output filenames specific to this project
  64. projectFilesOutputTag="EH_41BULK_update"
  65. ## Load and clean the data
  66. ################################################################################################
  67. cleanDat <- read.csv(file=paste0(datadir,inputAbundanceFile),header=TRUE,row.names=1)
  68. range(cleanDat, na.rm=T) #get a sense of whether these are all positive >0 #will not be all >0 if already TAMPORed
  69. # cleanDat<-log2(cleanDat) #only if input protein abundance data not already log2 transformed
  70. # colnames(cleanDat)<-gsub("\\.","-",colnames(cleanDat))
  71. GI <- rownames(cleanDat)
  72. ## Match up metadata and plot it for visualization - we want to know what variables are correlated with other variables
  73. numericMeta <- read.csv(paste0(datadir,inputTraitsFile),header=TRUE,row.names=1)
  74. rownames(numericMeta)==colnames(cleanDat) #sanity check -- are sample names in same order?
  75. colnames(cleanDat) <- rownames(numericMeta)
  76. cleanDat<-cleanDat[,match(rownames(numericMeta),colnames(cleanDat))] #cull to keep only samples we have traits for, and match the column (sample) order of cleanDat to the row order of numericMeta
  77. #numericMeta <- numericMeta[match(colnames(cleanDat),rownames(numericMeta)),] #use this line instead of above if you have more samples in your traits file than you do in abundance data; trait sample (row) order will be matched to column names of cleanDat
  78. rownames(numericMeta)==colnames(cleanDat) #sanity check -- are sample names in same order?
  79. ## If working on log2(protein abundance or ratio) with NA missing values; Enforce <50% missingness (1 less than half of cleanDat columns (or round down half if odd number of columns))
  80. LThalfSamples<-length(colnames(cleanDat))/2
  81. LThalfSamples<-LThalfSamples - if ((length(colnames(cleanDat)) %% 2)==1) { 0.5 } else { 1.0 }
  82. #remove rows with >=50% missing values (only if there are some rows to be removed)
  83. IndexHighMissing<-rowsRemoved<-zeroVarRows<-vector()
  84. temp2<-as.data.frame(cleanDat[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples),])
  85. #handle condition if temp2 is for one row of cleandat (a vector instead of a DF)
  86. if (ncol(temp2)==1) {
  87. temp2<-t(temp2)
  88. rownames(temp2)=rownames(cleanDat)[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples)]
  89. }
  90. if (nrow(temp2)>0) { IndexHighMissing=which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples); rowsRemoved<-rownames(cleanDat)[IndexHighMissing]; cleanDat<-cleanDat[-IndexHighMissing,]; }
  91. dim(cleanDat)
  92. #8212 41
  93. #write cleanDat abundance data to CSV file
  94. write.csv(cleanDat,file=paste0(outputtabs,"cleanDat.log2-MissingData50pctControlled",projectFilesOutputTag,".csv"))
  95. #save session data structures to Rdata for reloading intermittently
  96. save.image(paste0("saved.image.",projectFilesOutputTag,".Rdata"))
  97. ##########################################################################################################
  98. #=============================#
  99. # Check and Remove Outliers #
  100. #=============================#
  101. if(!exists("numericMeta")) numericMeta<-traits
  102. library(WGCNA)
  103. sdout=3 #Z.k SD fold for outlier threshold
  104. outliers.noOLremoval<-outliers.All<-vector()
  105. cleanDat.noOLremoval<-cleanDat
  106. for (repeated in 1:5) {
  107. normadj <- (0.5+0.5*bicor(cleanDat,use="pairwise.complete.obs")^2)
  108. ## Calculate connectivity
  109. netsummary <- fundamentalNetworkConcepts(normadj)
  110. ku <- netsummary$Connectivity
  111. z.ku <- ku-(mean(ku))/sqrt(var(ku))
  112. ## Declare as outliers those samples which are more than sdout sd above the mean connectivity based on the chosen measure
  113. outliers <- (z.ku > mean(z.ku)+sdout*sd(z.ku))|(z.ku < mean(z.ku)-sdout*sd(z.ku))
  114. print(paste0("There are ",sum(outliers)," outlier samples based on a bicor distance sample network connectivity standard deviation above ",sdout,". [Round ",repeated,"]"))
  115. targets.All=numericMeta
  116. cleanDat <- cleanDat[,!outliers]
  117. numericMeta <- targets <- targets.All[!outliers,]
  118. outliers.All<-c(outliers.All,outliers)
  119. } #repeat 5 times
  120. #All outliers removed
  121. print(paste0("There are ",sum(outliers.All)," total outlier samples removed in ",repeated," iterations:"))
  122. names(which(outliers.All))
  123. outliersRemoved<-names(which(outliers.All))
  124. #Note outliers as comment below, copied from R session.
  125. ## Enforce <50% missingness (1 less than half of cleanDat columns (or round down half if odd number of columns))
  126. LThalfSamples<-length(colnames(cleanDat))/2
  127. LThalfSamples<-LThalfSamples - if ((length(colnames(cleanDat)) %% 2)==1) { 0.5 } else { 1.0 }
  128. ## If operating on log2(FPKM) data, remove rows with >=50% originally 0 FPKM values (only if there are some rows to be removed)
  129. #IndexHighMissing<-rowsRemoved<-zeroVarRows<-vector()
  130. #temp2<-data.frame(ThrowOut=apply(cleanDat,1,function(x) length(x[x==log2(0+0.05)])>LThalfSamples))
  131. #cleanDat<-cleanDat[!temp2$ThrowOut,]
  132. #dim(cleanDat) #still have x genes, now for y total samples
  133. ## If working on log2(protein abundance or ratio) with NA missing values; Enforce <50% missingness (1 less than half of cleanDat columns (or round down half if odd number of columns))
  134. #remove rows with >=50% missing values (only if there are some rows to be removed)
  135. IndexHighMissing<-rowsRemoved<-zeroVarRows<-vector()
  136. temp2<-as.data.frame(cleanDat[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples),])
  137. #handle condition if temp2 is for one row of cleandat (a vector instead of a data frame)
  138. if (ncol(temp2)==1) {
  139. temp2<-t(temp2)
  140. rownames(temp2)=rownames(cleanDat)[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples)]
  141. }
  142. if (nrow(temp2)>0) { IndexHighMissing=which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples); rowsRemoved<-rownames(cleanDat)[IndexHighMissing]; cleanDat<-cleanDat[-IndexHighMissing,]; }
  143. dim(cleanDat)
  144. #Remove "Exclude" cases before regression
  145. #Exclude may not be a homogeneous condition so remove
  146. ######################################
  147. traits <- numericMeta
  148. excludeVec <- grepl("Exclude", traits$Group)
  149. cleanDat <- cleanDat[,!excludeVec]
  150. traits <- traits[!excludeVec,]
  151. rownames(traits) == colnames(cleanDat)
  152. dim(cleanDat)
  153. dim(traits)
  154. na.coltest <- function(x) {
  155. w <- sapply(x, function(x)all(is.na(x)))
  156. vectorNA <- as.vector(w)
  157. return(vectorNA)
  158. }
  159. colnames(cleanDat) == rownames(traits)
  160. bool_vectorNA <- na.coltest(cleanDat) #removes allNA columns
  161. cleanDat <- cleanDat[,!bool_vectorNA]
  162. traits <- traits[!bool_vectorNA,]
  163. dim(cleanDat)
  164. dim(traits)
  165. LThalfSamples<-length(colnames(cleanDat))/2
  166. LThalfSamples<-LThalfSamples - if ((length(colnames(cleanDat)) %% 2)==1) { 0.5 } else { 1.0 }
  167. #remove rows with >=50% missing values (only if there are some rows to be removed)
  168. IndexHighMissing<-rowsRemoved<-zeroVarRows<-vector()
  169. temp2<-as.data.frame(cleanDat[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples),])
  170. #handle condition if temp2 is for one row of cleandat (a vector instead of a data frame)
  171. if (ncol(temp2)==1) {
  172. temp2<-t(temp2)
  173. rownames(temp2)=rownames(cleanDat)[which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples)]
  174. }
  175. if (nrow(temp2)>0) { IndexHighMissing=which(rowSums(as.matrix(is.na(cleanDat)))>LThalfSamples); rowsRemoved<-rownames(cleanDat)[IndexHighMissing]; cleanDat<-cleanDat[-IndexHighMissing,]; }
  176. dim(cleanDat)
  177. ## Bootstrap regression set up EH edits
  178. # # fix batch.channel - if batch channel is b01.126.AD we need it to be b01
  179. # # Split the 'batch.channel' column into parts
  180. # split_info <- do.call(rbind, strsplit(as.character(numericMeta$batch.channel), "\\."))
  181. #
  182. # # Assign to new variables
  183. # batch <- as.factor(split_info[, 1])
  184. # channel <- as.factor(split_info[, 2])
  185. # #diagnosis <- as.factor(split_info[, 3])
  186. #
  187. # #sanity check
  188. # table(batch)
  189. # table(channel)
  190. # #table(diagnosis)
  191. #
  192. # #add back to numericMeta
  193. # numericMeta$batch <- batch
  194. # numericMeta$channel <- channel
  195. # #numericMeta$diagnosis <- diagnosis
  196. #######################
  197. #Parallel Bootstrap Regression
  198. cleanDat.unreg<-cleanDat
  199. numericMeta <- traits
  200. dim(numericMeta)
  201. library("doParallel")
  202. #when Eric is running at Emory (requires RSA public key-ssh, & manual run of shell script from command prompt to start server backend):
  203. # parallelThreads=30
  204. # clusterLocal <- makeCluster(c(rep("haplotein.biochem.emory.edu",parallelThreads)), type = "SOCK", port=10191, user="edammer", rscript="/usr/bin/Rscript",rscript_args="OUT=/dev/null SNOWLIB=/usr/lib64/R/library",manual=FALSE)
  205. # ##OR to run parallel processing threads for regression: :
  206. parallelThreads=20 #max is number of processes that can run on your computer at one time
  207. clusterLocal <- makeCluster(c(rep("localhost",parallelThreads)),type="SOCK")
  208. registerDoParallel(clusterLocal)
  209. ##OR for no parallel processing skip all doParallel functions (much slower):
  210. # parallelThreads=8
  211. library(boot)
  212. boot <- TRUE
  213. numboot <- 1000
  214. bs <- function(formula, data, indices) {
  215. d <- data[indices,] # allows bootstrap function to select samples
  216. fit <- lm(formula, data=d)
  217. return(coef(fit))
  218. }
  219. condition <- as.factor(numericMeta$Group)
  220. # colnames(numericMeta)[grep("age", colnames(numericMeta))] = "Age"
  221. # colnames(numericMeta)[grep("msex", colnames(numericMeta))] = "SEX"
  222. # colnames(numericMeta)[grep("pmi", colnames(numericMeta))] = "PMI"
  223. #regvars <- as.data.frame(cbind(condition, batch,PMI))
  224. condition <- as.factor(numericMeta$Group)
  225. batch <- as.factor(numericMeta$Batch) # preserves "b01", "b02", etc.
  226. PMI <- as.numeric(numericMeta$PMI)
  227. regvars <- as.data.frame(cbind(condition, batch,PMI))
  228. ## Run the regression
  229. normExpr.reg <- matrix(NA,nrow=nrow(cleanDat),ncol=ncol(cleanDat))
  230. rownames(normExpr.reg) <- rownames(cleanDat)
  231. colnames(normExpr.reg) <- colnames(cleanDat)
  232. coefmat <- matrix(NA,nrow=nrow(cleanDat),ncol=ncol(regvars)+2) ## change this to ncol(regvars)+2 when condition has 2 levels if BOOT=TRUE, +1 if BOOT=FALSE
  233. #another RNG seed set for reproducibility
  234. set.seed(8675309);
  235. if (parallelThreads > 1) {
  236. if (boot==TRUE) { #ORDINARY NONPARAMETRIC BOOTSTRAP
  237. set.seed(8675309)
  238. cat('[bootstrap-PARALLEL] Working on ORDINARY NONPARAMETRIC BOOTSTRAP regression with ', parallelThreads, ' threads over ', nrow(cleanDat), ' iterations.\n Estimated time to complete:', round(120/parallelThreads*nrow(cleanDat)/2736,1), ' minutes.\n') #intermediate progress printouts would not be visible in parallel mode
  239. coefmat <- foreach (i=1:nrow(cleanDat), .combine=rbind) %dopar% {
  240. set.seed(8675309)
  241. options(stringsAsFactors=FALSE)
  242. library(boot)
  243. thisexp <- as.numeric(cleanDat[i,])
  244. bs.results <- boot(data=data.frame(thisexp,regvars), statistic=bs,
  245. R=numboot, formula=thisexp~ condition +batch+PMI) ## run 1000 resamplings
  246. ## get the median - we can sometimes get NA values here... so let's exclude these - old code #bs.stats <- apply(bs.results$t,2,median)
  247. bs.stats <- rep(NA,ncol(bs.results$t)) ##ncol is 3 here (thisexp, construct and extracted)
  248. for (n in 1:ncol(bs.results$t)) {
  249. bs.stats[n] <- median(na.omit(bs.results$t[,n]))
  250. }
  251. bs.stats
  252. #cat('[bootstrap] Done for Protein ',i,'\n') #will not be visible
  253. }
  254. # normExpr.reg <- matrix(NA,nrow=nrow(cleanDat),ncol=ncol(cleanDat))
  255. normExpr.reg <-foreach (i=1:nrow(cleanDat), .combine=rbind) %dopar% { cleanDat[i,]- coefmat[i,3]*regvars[,"batch"] - coefmat[i,4]*regvars[,"PMI"] }
  256. } else { #linear model regression; faster but incomplete regression of Age, Sex, PMI effects, SO NOT USED WITH boot=TRUE (requires changing coefmat matrix ncol to 1 less above)
  257. coefmat<-coefmat[,-ncol(coefmat)] #handles different column requirement for lm regression method
  258. for (i in 1:nrow(cleanDat)) {
  259. if (i%%1000 == 0) {print(i)}
  260. lmmod1 <- lm(as.numeric(cleanDat[i,])~condition +age+sex+PMI,data=regvars) #+PMI
  261. ##datpred <- predict(object=lmmod1,newdata=regvars)
  262. coef <- coef(lmmod1)
  263. coefmat[i,] <- coef
  264. normExpr.reg[i,] <- coef[1] + coef[2]*regvars[,"condition"] + lmmod1$residuals ## The full data - the undesired covariates
  265. ## Also equivalent to <- thisexp - coef*var expression above
  266. #cat('Done for Protein ',i,'\n')
  267. }
  268. } #end parallel option -- Average run time estimate printed in console in minutes is calculated based on benchmark of a 2+ GHz intel Xeon with 30 threads
  269. } else {
  270. if (boot==TRUE) { #ORDINARY NONPARAMETRIC BOOTSTRAP
  271. for (i in 1:nrow(cleanDat)) {
  272. if (i%%1000 == 0) {print(i)}
  273. thisexp <- as.numeric(cleanDat[i,])
  274. bs.results <- boot(data=data.frame(thisexp,regvars), statistic=bs,
  275. R=numboot, formula=thisexp~ condition +age+sex+PMI) ## run 1000 resamplings
  276. # R=numboot, formula=thisexp~ condition +age+sex+PMI) ## run 1000 resamplings
  277. ## get the median - we can sometimes get NA values here... so let's exclude these - old code #bs.stats <- apply(bs.results$t,2,median)
  278. bs.stats <- rep(NA,ncol(bs.results$t)) ##ncol is 3 here (thisexp, construct and extracted)
  279. for (n in 1:ncol(bs.results$t)) {
  280. bs.stats[n] <- median(na.omit(bs.results$t[,n]))
  281. }
  282. coefmat[i,] <- bs.stats
  283. normExpr.reg[i,] <- thisexp - bs.stats[3]*regvars[,"batch"]- bs.stats[4]*regvars[,"PMI"]
  284. cat('[bootstrap] Done for Protein ',i,'\n')
  285. }
  286. } else { #linear model regression; faster but NOT USED WITH boot=TRUE (requires changing coefmat matrix ncol to 1 less above)
  287. # coefmat<-coefmat[,-ncol(coefmat)] #handles different column requirement for lm regression method
  288. for (i in 1:nrow(cleanDat)) {
  289. if (i%%1000 == 0) {print(i)}
  290. lmmod1 <- lm(as.numeric(cleanDat[i,])~condition +age+sex+PMI,data=regvars)
  291. ##datpred <- predict(object=lmmod1,newdata=regvars)
  292. coef <- coef(lmmod1)
  293. coefmat[i,] <- coef
  294. normExpr.reg[i,] <- coef[1] + coef[2]*regvars[,"condition"] + lmmod1$residuals ## The full data - the undesired covariates
  295. ## Also equivalent to <- thisexp - coef*var expression above
  296. cat('Done for Protein ',i,'\n')
  297. }
  298. } #end nonparallel option
  299. }
  300. #quantiles let us check if our regression worked
  301. ## Sanity Check -- Did regression do something unexpected to our abundance data? EH this should be the same
  302. quantile(cleanDat[,1],c(0,0.025,0.25,0.5,0.75,0.975,1),na.rm=TRUE)
  303. #Emorys
  304. # 0% 2.5% 25% 50% 75% 97.5% 100%
  305. # -3.1232752 -0.7816937 -0.1445080 0.0000000 0.1286251 0.5280247 3.8222000
  306. #EH
  307. # 0% 2.5% 25% 50% 75% 97.5% 100%
  308. # -3.1232752 -0.7816937 -0.1445080 0.0000000 0.1286251 0.5280247 3.8222000
  309. quantile(normExpr.reg[,1],c(0,0.025,0.25,0.5,0.75,0.975,1),na.rm=TRUE) #This should be slightly different
  310. #EH
  311. # 0% 2.5% 25% 50% 75% 97.5% 100%
  312. # -3.066539173 -0.825317463 -0.160689774 -0.001135936 0.138690652 0.588191765 3.634146427
  313. ##Overwrite cleanDat with regressed data
  314. ##(DO NOT RERUN OUT OF CONTEXT)
  315. ############################################
  316. cleanDatReg<-normExpr.reg
  317. rownames(cleanDat)<-rownames(cleanDat.unreg)
  318. ############################################
  319. save.image(paste0("regressed.batchandPMI.saved.image.",projectFilesOutputTag,".Rdata")) #overwrites
  320. #importantly, now contains final cleanDat, cleanDat.unreg, final numericMeta, outliersRemoved
  321. write.table(cleanDatReg,file=paste0(outputtabs,"/cleanDatReg.final_",projectFilesOutputTag,"_",nrow(cleanDatReg),"x",ncol(cleanDatReg),"_BootAgeSexPMIregr_GroupProtected.txt"),sep="\t") #check and apply changes to static tail of filename if necessary
  322. colnames(cleanDatReg) == rownames(numericMeta)
  323. bool_vectorNA <- na.coltest(cleanDatReg) # check again for NA columns
  324. cleanDatReg <- cleanDatReg[,!bool_vectorNA]
  325. numericMeta <- numericMeta[!bool_vectorNA,]
  326. traits <- numericMeta
  327. dim(cleanDatReg)
  328. dim(numericMeta)
  329. LThalfSamples<-length(colnames(cleanDatReg))/2
  330. LThalfSamples<-LThalfSamples - if ((length(colnames(cleanDatReg)) %% 2)==1) { 0.5 } else { 1.0 }
  331. #remove rows with >=50% missing values (only if there are some rows to be removed)
  332. IndexHighMissing<-rowsRemoved<-zeroVarRows<-vector()
  333. temp2<-as.data.frame(cleanDatReg[which(rowSums(as.matrix(is.na(cleanDatReg)))>LThalfSamples),])
  334. #handle condition if temp2 is for one row of cleandat (a vector instead of a data frame)
  335. if (ncol(temp2)==1) {
  336. temp2<-t(temp2)
  337. rownames(temp2)=rownames(cleanDatReg)[which(rowSums(as.matrix(is.na(cleanDatReg)))>LThalfSamples)]
  338. }
  339. if (nrow(temp2)>0) { IndexHighMissing=which(rowSums(as.matrix(is.na(cleanDatReg)))>LThalfSamples); rowsRemoved<-rownames(cleanDatReg)[IndexHighMissing]; cleanDatReg<-cleanDatReg[-IndexHighMissing,]; }
  340. dim(cleanDatReg)
  341. ## WGCNA blockwiseModules (for signed bicor coexpression network)
  342. ##############################################################################
  343. ##Above code may have borrowed server for parallel processing of bootstrap regression -- return parallel processing to local workstation
  344. library("doParallel")
  345. library("snow")
  346. # stopCluster(clusterLocal)
  347. parallelThreads=20 #set to # of threads on your computer
  348. clusterLocal <- makeCluster(c(rep("localhost",parallelThreads)),type="SOCK")
  349. registerDoParallel(clusterLocal)
  350. allowWGCNAThreads() #speeds the pickSoftThreshold function
  351. powers <- seq(4,12,by=1) #initial power check -- try to get SFT.R.sq to go > 0.80
  352. sft <- pickSoftThreshold(t(cleanDatReg),blockSize=nrow(cleanDatReg)+1000, #always calculate power within a single block (blockSize > # of rows in cleanDat)
  353. powerVector=powers,
  354. corFnc="bicor",networkType="signed")
  355. #EH 04.24.2025
  356. # Power SFT.R.sq slope truncated.R.sq mean.k. median.k. max.k.
  357. # 1 4 0.697 -5.18 0.920 722.0 700.0 1070
  358. # 2 5 0.799 -4.05 0.950 443.0 420.0 800
  359. # 3 6 0.857 -3.43 0.963 282.0 260.0 626
  360. # 4 7 0.884 -3.00 0.962 185.0 166.0 506
  361. # 5 8 0.907 -2.73 0.964 126.0 108.0 419
  362. # 6 9 0.907 -2.58 0.959 87.6 71.4 353
  363. # 7 10 0.904 -2.46 0.946 62.6 48.3 302
  364. # 8 11 0.911 -2.33 0.946 45.8 33.3 261
  365. # 9 12 0.918 -2.23 0.952 34.2 23.2 228
  366. # Power SFT.R.sq slope truncated.R.sq mean.k. median.k. max.k.
  367. # 1 4 0.747 -17.40 0.966 566.00 560.00 716.0
  368. # 2 5 0.789 -11.80 0.973 312.00 306.00 452.0
  369. # 3 6 0.822 -8.69 0.974 176.00 171.00 298.0
  370. # 4 7 0.843 -6.99 0.973 101.00 96.90 204.0
  371. # 5 8 0.864 -5.71 0.973 59.50 55.90 144.0
  372. # 6 9 0.897 -4.73 0.978 35.80 32.80 105.0
  373. # 7 10 0.926 -4.03 0.985 22.00 19.60 77.8
  374. # 8 11 0.948 -3.53 0.988 13.80 11.80 59.1
  375. # 9 12 0.960 -3.24 0.993 8.86 7.22 47.5
  376. #plot initial SFT.R.sq vs. power curve
  377. tableSFT<-sft[[2]]
  378. plot(tableSFT[,1],tableSFT[,2],xlab="Power (Beta)",ylab="SFT R?")
  379. # Plot the results
  380. # sizeGrWindow(9, 5)
  381. par(mfrow = c(1,2));
  382. cex1 = 0.9;
  383. # Scale-free topology fit index as a function of the soft-thresholding power
  384. plot(tableSFT[,1], -sign(tableSFT[,3])*tableSFT[,2],xlab="Soft Threshold (power)",ylab="Scale Free Topology Model Fit, signed R^2",type="n", main = paste("Scale independence"));
  385. text(tableSFT[,1], -sign(tableSFT[,3])*tableSFT[,2],labels=powers,cex=cex1,col="red");
  386. # Red line corresponds to using an R^2 cut-off
  387. abline(h=0.80,col="red")
  388. # Mean connectivity as a function of the soft-thresholding power
  389. plot(tableSFT[,1], tableSFT[,5],xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",main = paste("Mean connectivity"))
  390. text(tableSFT[,1], tableSFT[,5], labels=powers, cex=cex1,col="red")
  391. allowWGCNAThreads() #speeds the pickSoftThreshold function
  392. powers <- seq(5,12,by=0.5) #finer grained check over a honed range of power values. No need to ever go above 30 (problem with data if so); higher power gives lower connectivity (k) and therefore more uncorrelated gene products in the network (grey with no coex module)
  393. sft <- pickSoftThreshold(t(cleanDatReg),blockSize=nrow(cleanDatReg)+1000, #always calculate power within a single block (blockSize > # of rows in cleanDat)
  394. powerVector=powers,
  395. corFnc="bicor",networkType="signed")
  396. #EH 04.24.2025 same on 5.3.25 - chose power 8
  397. # Power SFT.R.sq slope truncated.R.sq mean.k. median.k. max.k.
  398. # 1 5.0 0.799 -4.05 0.950 443.0 420.0 800
  399. # 2 5.5 0.829 -3.71 0.957 351.0 329.0 704
  400. # 3 6.0 0.857 -3.43 0.963 282.0 260.0 626
  401. # 4 6.5 0.869 -3.19 0.961 227.0 207.0 561
  402. # 5 7.0 0.884 -3.00 0.962 185.0 166.0 506
  403. # 6 7.5 0.896 -2.86 0.963 152.0 133.0 459
  404. # 7 8.0 0.907 -2.73 0.964 126.0 108.0 419
  405. # 8 8.5 0.898 -2.67 0.955 105.0 87.5 384
  406. # 9 9.0 0.907 -2.58 0.959 87.6 71.4 353
  407. # 10 9.5 0.912 -2.50 0.958 73.9 58.5 326 << power
  408. # 11 10.0 0.904 -2.46 0.946 62.6 48.3 302
  409. # 12 10.5 0.911 -2.38 0.949 53.4 40.0 280
  410. # 13 11.0 0.911 -2.33 0.946 45.8 33.3 261
  411. # 14 11.5 0.912 -2.29 0.947 39.5 27.7 243
  412. # 15 12.0 0.918 -2.23 0.952 34.2 23.2 228
  413. #Evans
  414. # Power SFT.R.sq slope truncated.R.sq mean.k. median.k. max.k.
  415. # 1 5.0 0.789 -11.80 0.973 312.00 306.00 452.0
  416. # 2 5.5 0.807 -10.00 0.975 233.00 228.00 365.0
  417. # 3 6.0 0.822 -8.69 0.974 176.00 171.00 298.0
  418. # 4 6.5 0.841 -7.66 0.976 133.00 128.00 246.0
  419. # 5 7.0 0.843 -6.99 0.973 101.00 96.90 204.0
  420. # 6 7.5 0.853 -6.29 0.972 77.40 73.50 171.0 << power
  421. # 7 8.0 0.864 -5.71 0.973 59.50 55.90 144.0
  422. # 8 8.5 0.873 -5.25 0.971 46.10 42.80 122.0
  423. # 9 9.0 0.897 -4.73 0.978 35.80 32.80 105.0
  424. # 10 9.5 0.907 -4.38 0.980 28.00 25.30 90.0
  425. # 11 10.0 0.926 -4.03 0.985 22.00 19.60 77.8
  426. # 12 10.5 0.940 -3.74 0.988 17.40 15.20 67.6
  427. # 13 11.0 0.948 -3.53 0.988 13.80 11.80 59.1
  428. # 14 11.5 0.956 -3.37 0.991 11.00 9.22 52.7
  429. # 15 12.0 0.960 -3.24 0.993 8.86 7.22 47.5
  430. #plot fine-grained results, looking for first power where SFT.R.sq has approached an asymptote
  431. tableSFT<-sft[[2]]
  432. plot(tableSFT[,1],tableSFT[,2],xlab="Power (Beta)",ylab="SFT R?")
  433. #Notes on this data: looks choppy and SFT R^2 not improving in this range ... asymptote reached.
  434. #choose power 10 elbow of SFT R? curve approaching asymptote near or ideally above 0.80
  435. power=8
  436. ## Run an automated network analysis (ds=4 and mergeCutHeight=0.07, more liberal)
  437. # choose parameters deepSplit and mergeCutHeight to get respectively more modules and more stringency sending more low connectivity genes to grey (not in modules).
  438. allowWGCNAThreads(nThreads = 16)
  439. net <- blockwiseModules(t(cleanDatReg),power=power,deepSplit=1,minModuleSize=30,
  440. mergeCutHeight=0.07,TOMdenom="mean", #detectCutHeight=0.9999, #TOMdenom="mean" may get more small modules here.
  441. corType="bicor",networkType="signed",pamStage=TRUE,pamRespectsDendro=TRUE,
  442. verbose=3,saveTOMs=FALSE,maxBlockSize=nrow(cleanDatReg)+1000,reassignThresh=0.05) #maxBlockSize always more than the number of rows in cleanDat
  443. #blockwiseModules can take 30 min+ for large numbers of gene products/proteins (10000s of rows); much quicker for smaller proteomic data sets
  444. net <- net.ds2.V2
  445. # net.ds2.V2 is ds2 run on dec 9th, i think the other ds2 something is wrong
  446. # net.ds1 <- net
  447. # net.ds2.V2 <- net
  448. # net.ds3 <- net
  449. # net.ds4 <- net
  450. # net.ds1.pwr6 <- net
  451. # net.ds2.pwr6 <- net
  452. # net.ds3.pwr6 <- net
  453. # net.ds4.pwr6 <- net
  454. # net.ds1.pwr10 <- net
  455. # net.ds2.pwr10 <- net
  456. # net.ds3.pwr10 <- net
  457. # net.ds4.pwr10 <- net
  458. nModules<-length(table(net$colors))-1
  459. modules<-cbind(colnames(as.matrix(table(net$colors))),table(net$colors))
  460. orderedModules<-cbind(Mnum=paste("M",seq(1:nModules),sep=""),Color=labels2colors(c(1:nModules)))
  461. modules<-modules[match(as.character(orderedModules[,2]),rownames(modules)),]
  462. as.data.frame(cbind(orderedModules,Size=modules))
  463. ##copy R session output;
  464. # EH 5.03.2025 power 8
  465. # Mnum Color Size
  466. # turquoise M1 turquoise 1389
  467. # blue M2 blue 432
  468. # brown M3 brown 426
  469. # yellow M4 yellow 314
  470. # green M5 green 303
  471. # red M6 red 279
  472. # black M7 black 264
  473. # pink M8 pink 255
  474. # magenta M9 magenta 253
  475. # purple M10 purple 245
  476. # greenyellow M11 greenyellow 236
  477. # tan M12 tan 220
  478. # salmon M13 salmon 200
  479. # cyan M14 cyan 141
  480. # midnightblue M15 midnightblue 131
  481. # lightcyan M16 lightcyan 128
  482. # grey60 M17 grey60 116
  483. # lightgreen M18 lightgreen 108
  484. # lightyellow M19 lightyellow 101
  485. # royalblue M20 royalblue 93
  486. # darkred M21 darkred 87
  487. # darkgreen M22 darkgreen 86
  488. # darkturquoise M23 darkturquoise 84
  489. # darkgrey M24 darkgrey 81
  490. # orange M25 orange 78
  491. # darkorange M26 darkorange 77
  492. # white M27 white 74
  493. # skyblue M28 skyblue 70
  494. # saddlebrown M29 saddlebrown 64
  495. # steelblue M30 steelblue 62
  496. # paleturquoise M31 paleturquoise 60
  497. # violet M32 violet 57
  498. # darkolivegreen M33 darkolivegreen 53
  499. # darkmagenta M34 darkmagenta 51
  500. # sienna3 M35 sienna3 51
  501. # yellowgreen M36 yellowgreen 50
  502. # skyblue3 M37 skyblue3 50
  503. # plum1 M38 plum1 50
  504. # orangered4 M39 orangered4 49
  505. # mediumpurple3 M40 mediumpurple3 46
  506. # lightsteelblue1 M41 lightsteelblue1 44
  507. # lightcyan1 M42 lightcyan1 43
  508. # ivory M43 ivory 37
  509. #clean dat 8218 41
  510. ## saved image of R session after running and finalizing blockwiseModules() function WGCNA output (now includes net data structure)
  511. save.image(paste0("modules.saved.image.",projectFilesOutputTag,".Rdata")) #overwrites
  512. numericMeta1 <- numericMeta
  513. limma::plotMDS(cleanDatReg, col=numericMeta$Color, main="After regressing out Batch/PMI and TAMPOR")
  514. # Generate a boxplot for protein of interest. (preferentially uses numericMeta to pair to cleanDat, but numericMeta exists only after removing outliers)
  515. protein <- "UNC5B"
  516. idx <- grepl(protein,rownames(cleanDatReg))
  517. data_sub <- as.data.frame(cleanDatReg[idx,])
  518. # data_sub <- data_sub[2,]
  519. data_sub <- as.data.frame(t(data_sub))
  520. # traits$Batch <- sampleIndex$batch[match(rownames(traits), rownames(numericMeta1))]
  521. vectorGISdatasub <- grepl("GIS", rownames(data_sub))
  522. colnames(data_sub) <- "Ratio"
  523. data_sub$Group <- numericMeta$Group[match(rownames(data_sub),rownames(numericMeta1))]
  524. # data_sub$Batch <- sampleIndex$batch[match(rownames(data_sub),rownames(numericMeta1))]
  525. data_sub <- as.data.frame(data_sub[!vectorGISdatasub,])
  526. plot <- ggplot(data_sub, aes(group = data_sub$Group, y = Ratio, fill = data_sub$Group)) + geom_boxplot(outlier.colour = "black", outlier.shape = 20, outlier.size = 1) + scale_fill_ghibli_d("MarnieLight1", -1)
  527. plot + ggtitle(protein) +
  528. theme(
  529. plot.title = element_text(hjust = 0.5, color = "black", size = 11, face = "bold"),
  530. axis.title.x = element_text(color = "black", size = 11, face = "bold"),
  531. axis.title.y = element_text(color = "black", size = 11, face = "bold")
  532. )
  533. ## Output GlobalNetworkPlots and kMEtable
  534. ####################################################################################################################
  535. numericMeta1 <- numericMeta
  536. ##############################
  537. #EH added new spine traits and took out the incorrect ones
  538. #EH removes unwanted columns from traits - numericMeta1
  539. numericMeta1 <- numericMeta1[, -c(25:110)]
  540. #only pick the spine traits that you are interested in merging
  541. newTraits <- newTraits[, -c(2,3,5,8,10,11,12,17:22,31:34)]
  542. #JH said get rid of these
  543. # Total length
  544. # Total spines
  545. # X10
  546. # Surface area
  547. # Neck diameter
  548. # Both ratios
  549. # All percents
  550. # Num dendrites
  551. #E05-130 0.8498237 1.2989037 0.4129278
  552. # Read with custom column names
  553. newTraits <- read.csv(
  554. file = file.path(datadir, "CW_spines_PFC.csv"),
  555. header = TRUE,
  556. fileEncoding = "UTF-8-BOM",
  557. check.names = TRUE
  558. )
  559. library(dplyr)
  560. newTraits <- newTraits %>%
  561. rename(SampleID = X)
  562. newTraits <- newTraits[, -c(2:19,22,26,28:30,35:40,49:52)]
  563. colnames(newTraits) <- make.names(colnames(newTraits), unique = TRUE)
  564. #sanity check
  565. str(newTraits)
  566. head(colnames(newTraits), 10)
  567. # Ensure SampleID formats match
  568. numericMeta1$SampleID <- as.character(numericMeta1$SampleID)
  569. newTraits$SampleID <- as.character(newTraits$SampleID)
  570. # Match new spine traits to numericMeta1 by SampleID
  571. matched_spineTraits <- newTraits[match(numericMeta1$SampleID, newTraits$SampleID), ]
  572. # Check: all SampleIDs aligned?
  573. if (!all(numericMeta1$SampleID == matched_spineTraits$SampleID)) {
  574. warning("⚠️ SampleID mismatch! Double check before merging.")
  575. } else {
  576. message("✅ SampleIDs aligned safe to merge.")
  577. }
  578. # Drop SampleID from the trait data before merging
  579. matched_spineTraits <- matched_spineTraits[, -which(colnames(matched_spineTraits) == "SampleID")]
  580. # Merge into numericMeta1
  581. numericMeta1 <- cbind(numericMeta1, matched_spineTraits)
  582. #get rid of missing spine data
  583. numericMeta1[numericMeta1 == "#DIV/0!"] <- NA
  584. # EH i did this before merging 5.3.2025
  585. # fix names in numericMeta1 if needed
  586. # 1) The 36 old names
  587. oldNames <- c(
  588. "Total.length..um.", "Total.spines", "Spine.Density.per.1um",
  589. "Spine.Density.per.10um","Backbone.Length.µm.", "Volume.µm..",
  590. "Surface.Area.µm..", "Head.Diameter.µm.", "Neck.Diameter",
  591. "Head.Diameter.Neck.Diameter.µm.","Backbone.Length.Head.Diameter.µm",
  592. "Thin.spine.density", "Stubby.spine.density", "Mushroom.spine.density",
  593. "Filopodia.spine.density","Thin.spine..", "Stubby.spine..",
  594. "Mushroom.spine..", "Filopodia.spine..", "Braak",
  595. "Dendrites", "Length.of.Thin", "Length.of.stubby",
  596. "Length.of.Mushroom", "Length.of.Filopodia", "Head.D...thin",
  597. "Head.D...stubby", "Head.D...mushroom", "Head.D...filopodia",
  598. "Neck.Diameter...thin", "Neck.Diameter...stubby", "Neck.Diameter...Mush",
  599. "Neck.Diameter...f", "Volume...T", "Volume...S",
  600. "Volume...M", "Volume...F"
  601. )
  602. # 2) Your desired new names, same length/order (fill in with whatever you like):
  603. newNames <- c(
  604. "Total.Length", "Total.Spines", "Spine.Density",
  605. "X10","Backbone.Length", "Volume",
  606. "Surface.Area", "Head.Diameter", "Neck.Diameter",
  607. "Head.Vs.Neck.Ratio", "Backbone.To.Head.Ratio", "Thin.Spine.Density",
  608. "Stubby.Spine.Density", "Mushroom.Spine.Density", "Filopodia.Spine.Density",
  609. "Thin.Spine.Percent", "Stubby.Spine.Percent", "Mushroom.Spine.Percent",
  610. "Filopodia.Spine.Percent", "Braak", "Num.Dendrites",
  611. "Length.of.Thin.Spines", "Length.of.Stubby.Spines", "Length.of.Mushroom.Spines",
  612. "Length.of.Filopodia.Spines", "Thin.Head.Diameter", "Stubby.Head.Diameter",
  613. "Mush.Head.Diameter", "Filopodia.Head.Diameter", "Thin.Neck.Diameter",
  614. "Stubby.Neck.Diameter", "Mush.Neck.Diameter", "Filopodia.Neck.Diameter",
  615. "Volume.Thin", "Volume.Stubby", "Volume.Mushroom",
  616. "Volume.Filopodia"
  617. )
  618. # 3) Match and rename in numericMeta1
  619. idx <- match(oldNames, names(newTraits))
  620. names(newTraits)[idx] <- newNames
  621. # 4) Verify
  622. all(newNames %in% names(newTraits)) # should return TRUE
  623. saveRDS(numericMeta1, file = "numericMeta1.rds")
  624. ################
  625. FileBaseName=paste0(projectFilesOutputTag,power,"_MergeHeight_")
  626. CairoPDF(file=paste0(rootdir,"01.30.2026.Global_plots_41Bulk.pdf"),width=16,height=12)
  627. # # Open a larger plotting window
  628. # dev.new(width = 16, height = 12) # adjust width and height as needed
  629. ## Plot dendrogram with module colors and trait correlations
  630. MEs<-tmpMEs<-data.frame()
  631. MEList = moduleEigengenes(t(cleanDatReg), colors = net$colors)
  632. MEs = orderMEs(MEList$eigengenes)
  633. colnames(MEs)<-gsub("ME","",colnames(MEs)) #let's be consistent in case prefix was added, remove it.
  634. rownames(MEs)<-rownames(numericMeta1)
  635. numericIndices<-unique(c( which(!is.na(apply(numericMeta1,2,function(x) sum(as.numeric(x))))), which(!(apply(numericMeta1,2,function(x) sum(as.numeric(x),na.rm=T)))==0) ))
  636. #Warnings OK; This determines which traits are numeric and if forced to numeric values, non-NA values do not sum to 0
  637. geneSignificance <- cor(sapply(numericMeta1[,numericIndices],as.numeric),t(cleanDatReg),use="pairwise.complete.obs")
  638. rownames(geneSignificance) <- colnames(numericMeta1)[numericIndices]
  639. geneSigColors <- t(numbers2colors(t(geneSignificance),signed=TRUE,lim=c(-1,1),naColor="black"))
  640. rownames(geneSigColors) <- colnames(numericMeta1)[numericIndices]
  641. plotDendroAndColors(dendro=net$dendrograms[[1]],
  642. colors=t(rbind(net$colors,geneSigColors)),
  643. cex.dendroLabels=1.2,addGuide=FALSE,
  644. dendroLabels=FALSE,
  645. groupLabels = rep("", nrow(geneSigColors) + 1))
  646. #groupLabels=c("Module Colors",colnames(numericMeta1)[numericIndices]))
  647. ## Plot eigengene dendrogram/heatmap - using bicor
  648. tmpMEs <- MEs #net$MEs
  649. colnames(tmpMEs) <- paste("ME",colnames(MEs),sep="")
  650. MEs[,"grey"] <- NULL
  651. tmpMEs[,"MEgrey"] <- NULL
  652. plotEigengeneNetworks(tmpMEs, "Eigengene Network", marHeatmap = c(3,4,2,2), marDendro = c(0,4,2,0),plotDendrograms = TRUE, xLabelsAngle = 90,heatmapColors=blueWhiteRed(50))
  653. # #ANOVA
  654. # numericMeta1$AD <- 0
  655. # numericMeta1$AsymAD <- 0
  656. # numericMeta1$Control <- 0
  657. #
  658. # numericMeta1$AD [ numericMeta1$Group == "AD" ] <- 1
  659. # numericMeta1$AsymAD [ numericMeta1$Group == "AsymAD" ] <- 1
  660. # numericMeta1$Control[ numericMeta1$Group == "Control" ] <- 1
  661. #
  662. # Grouping <- character(nrow(numericMeta1))
  663. #
  664. # Grouping[ numericMeta1$AD == 1 ] <- "AD"
  665. # Grouping[ numericMeta1$AsymAD == 1 ] <- "AsymAD"
  666. # Grouping[ numericMeta1$Control == 1 ] <- "Control"
  667. #
  668. # Grouping <- factor(Grouping, levels = c("Control","AsymAD","AD"))
  669. ## new
  670. ######################
  671. ## Find differences between Groups (as defined in Traits input file); Finalize Grouping of Samples for ANOVA
  672. #Set a vector of strings that represent each sample in order, calling out each sample as a member of named groups (used by GlobalNetworkPlot boxplots, and later, ANOVA DiffEx)
  673. # Create binary indicator columns
  674. numericMeta1$AD <- as.numeric(numericMeta1$Group == "AD")
  675. numericMeta1$AsymAD <- as.numeric(numericMeta1$Group == "AsymAD")
  676. numericMeta1$Control <- as.numeric(numericMeta1$Group == "CT") # CT = Control in the raw data
  677. # Create the 'Grouping' factor with readable labels
  678. Grouping <- character(nrow(numericMeta1))
  679. Grouping[numericMeta1$AD == 1] <- "AD"
  680. Grouping[numericMeta1$AsymAD == 1] <- "AsymAD"
  681. Grouping[numericMeta1$Control == 1] <- "Control"
  682. # Convert to factor with desired level order
  683. Grouping <- factor(Grouping, levels = c("Control", "AsymAD", "AD"))
  684. # This gets ANOVA (Kruskal-Wallis) nonparametric p-values for groupwise comparison of interest.
  685. # look at numericMeta (traits data) and choose traits to use for linear model-determination of p value
  686. head(numericMeta1)
  687. # 1. Build your covariate data frame properly
  688. regvars2 <- data.frame(
  689. AD = as.factor(numericMeta1$AD),
  690. AsymAD = as.numeric(numericMeta1$AsymAD),
  691. Control = as.numeric(numericMeta1$Control)
  692. )
  693. # 2. Compute one-way group p-values for each module eigengene
  694. pvec <- sapply(seq_len(ncol(MEs)), function(i) {
  695. fit <- lm(MEs[, i] ~ AD, data = regvars2)
  696. f <- summary(fit)$fstatistic
  697. pf(f[1], f[2], f[3], lower.tail = FALSE)
  698. })
  699. names(pvec) <- colnames(MEs)
  700. # Inspect your results
  701. head(pvec)
  702. head(sort(pvec))
  703. ## should match these
  704. #d2 5.03.2025
  705. # darkgreen darkturquoise magenta midnightblue salmon lightcyan
  706. # 0.601497159 0.166752398 0.006907908 0.009415648 0.061151141 0.004737429
  707. #d4 5.03.2025
  708. # lightgreen turquoise cyan midnightblue magenta salmon
  709. # 0.94697770 0.38526605 0.01072184 0.06083762 0.47142577 0.91066440
  710. # head(sort(pvec))
  711. # red darkgreen darkgrey greenyellow lightcyan1 yellowgreen
  712. # 5.039833e-05 2.708332e-04 3.420530e-04 1.174826e-03 1.217107e-03 3.280813e-03
  713. # OLD
  714. #d2 5.01.2025
  715. # midnightblue black darkgreen pink royalblue cyan
  716. # 0.0002823662 0.7497950572 0.0331756359 0.5619077440 0.0533683103 0.0380083421
  717. #d2 upd
  718. # green lightgreen brown yellow midnightblue cyan
  719. # 0.9872114945 0.4083586816 0.0001702289 0.4890324201 0.0298237801 0.2372072603
  720. #d2
  721. # green tan lightgreen blue brown royalblue
  722. # 0.4860135460 0.1885660078 0.2883007302 0.0426817366 0.0001788748 0.5958606283
  723. #d4
  724. # turquoise greenyellow grey60 magenta tan orangered4
  725. # 0.315514402 0.005006144 0.071038995 0.579607308 0.024336486 0.002676361
  726. ApoE<- numericMeta1$ApoE
  727. ApoE[numericMeta1$ApoE==22]<-"2/2"
  728. ApoE[numericMeta1$ApoE==23]<-"2/3"
  729. ApoE[numericMeta1$ApoE==24]<-"2/4"
  730. ApoE[numericMeta1$ApoE==33]<-"3/3"
  731. ApoE[numericMeta1$ApoE==34]<-"3/4"
  732. ApoE[numericMeta1$ApoE==44]<-"4/4"
  733. # drop empty apoe
  734. numericMeta1$ApoE[numericMeta1$ApoE == ""] <- NA
  735. numericMeta1$ApoE <- factor(numericMeta1$ApoE)
  736. regvars3$ApoE <- numericMeta1$ApoE
  737. ## Find differences between APOE Risk Groups
  738. regvars3 <- data.frame(as.factor(numericMeta1[,"ApoE"]),as.numeric(numericMeta1[,"AD"]),as.factor(numericMeta1[,"AsymAD"]),as.numeric(numericMeta1[,"Control"]))
  739. colnames(regvars3) <- c("ApoE","AD","AsymAD","Control") ## data frame with covaraites in case we want to try multivariate regression
  740. #aov1 <- aov(data.matrix(MEs)~Group,data=regvars) ## ANOVA framework yields same results
  741. lm1 <- lm(data.matrix(MEs)~ApoE,data=regvars3)
  742. pvec.Apoe <- rep(NA,ncol(MEs))
  743. for (i in 1:ncol(MEs)) {
  744. f <- summary(lm1)[[i]]$fstatistic ## Get F statistics
  745. pvec.Apoe[i] <- pf(f[1],f[2],f[3],lower.tail=F) ## Get the p-value corresponding to the whole model
  746. }
  747. names(pvec.Apoe) <- colnames(MEs)
  748. head(pvec.Apoe)
  749. head(sort(pvec.Apoe))
  750. # 01.30.26
  751. # head(pvec.Apoe)
  752. # lightgreen turquoise cyan midnightblue magenta salmon
  753. # 0.07444765 0.14021615 0.50427188 0.61755890 0.43258865 0.24780579
  754. # head(sort(pvec.Apoe))
  755. # lightyellow violet royalblue lightgreen turquoise yellow
  756. # 0.02821897 0.05202414 0.05870586 0.07444765 0.14021615 0.21311128
  757. #
  758. ## OLD
  759. #d2
  760. # turquoise greenyellow grey60 magenta tan orangered4
  761. # 0.17238305 0.32075923 0.50401965 0.46350051 0.36929353 0.04307129
  762. #d4
  763. # turquoise greenyellow grey60 magenta tan orangered4
  764. # 8.404509e-05 3.851860e-05 2.389466e-02 4.179531e-01 7.645752e-02 1.152232e-02
  765. ## this code does the exact same as above as confirmed by modules bellow
  766. # 1) Recode APOE using the vector you already created
  767. ApoE_char <- as.character(numericMeta1$ApoE) # e.g. "E3/4", "E2/4"
  768. # strip the leading "E" so you get "3/4", "2/4", etc.
  769. ApoE_clean <- sub("^E", "", ApoE_char)
  770. # 2) Build a factor in the exact order you care about:
  771. ApoE_factor <- factor(
  772. ApoE_clean,
  773. levels = c("2/2","2/3","2/4","3/3","3/4","4/4")
  774. )
  775. # 3) Build a data.frame for the model — include any covariates if you like:
  776. regvars_ApoE <- data.frame(
  777. ApoE = ApoE_factor,
  778. Age = as.numeric(numericMeta1$Age), # if you want to adjust for age
  779. Sex = factor(numericMeta1$Sex), # or keep as numeric 0/1
  780. PMI = as.numeric(numericMeta1$PMI)
  781. )
  782. # 4) Fit one LM per module eigengene
  783. pvec.Apoe <- sapply(seq_len(ncol(MEs)), function(i) {
  784. fit <- lm(MEs[, i] ~ ApoE, data = regvars_ApoE)
  785. f <- summary(fit)$fstatistic
  786. pf(f[1], f[2], f[3], lower.tail = FALSE)
  787. })
  788. names(pvec.Apoe) <- colnames(MEs)
  789. # 5) Quick check
  790. print(head(pvec.Apoe))
  791. head(sort(pvec.Apoe))
  792. # print(head(pvec.Apoe))
  793. # lightgreen turquoise cyan midnightblue magenta salmon
  794. # 0.07444765 0.14021615 0.50427188 0.61755890 0.43258865 0.24780579
  795. #
  796. #
  797. # head(sort(pvec.Apoe))
  798. # lightyellow violet royalblue lightgreen turquoise yellow
  799. # 0.02821897 0.05202414 0.05870586 0.07444765 0.14021615 0.21311128
  800. ######################
  801. ## Get sigend kME values
  802. kMEdat <- signedKME(t(cleanDatReg), tmpMEs, corFnc="bicor")
  803. ######################
  804. ## Plot eigengene-trait correlations - using p value of bicor for heatmap scale
  805. library(RColorBrewer)
  806. MEcors <- bicorAndPvalue(MEs,numericMeta1[,numericIndices])
  807. moduleTraitCor <- MEcors$bicor
  808. moduleTraitPvalue <- MEcors$p
  809. textMatrix = apply(moduleTraitCor,2,function(x) signif(x, 2))
  810. #textMatrix = paste(signif(moduleTraitCor, 2), " (",
  811. # signif(moduleTraitPvalue, 1), ")", sep = "");
  812. #dim(textMatrix) = dim(moduleTraitCor)
  813. par(mfrow=c(1,1))
  814. par(mar = c(6, 8.5, 3, 3));
  815. ## Display the correlation values within a heatmap plot
  816. cexy <- if(nModules>75) { 0.8 } else { 1 }
  817. colvec <- rep("white",1500)
  818. colvec[1:500] <- colorRampPalette(rev(brewer.pal(8,"BuPu")[2:8]))(500)
  819. colvec[501:1000]<-colorRampPalette(c("white",brewer.pal(8,"BuPu")[2]))(3)[2] #interpolated color for 0.05-0.1 p
  820. labeledHeatmap(Matrix = apply(moduleTraitPvalue,2,as.numeric),
  821. xLabels = colnames(numericMeta1)[numericIndices],
  822. yLabels = paste0("ME",names(MEs)),
  823. ySymbols = names(MEs),
  824. colorLabels = FALSE,
  825. colors = colvec,
  826. textMatrix = textMatrix,
  827. setStdMargins = FALSE,
  828. cex.text = 0.5,
  829. cex.lab.y= cexy,
  830. zlim = c(0,0.15),
  831. main = paste("Module-trait relationships\n bicor r-value shown as text\nHeatmap scale: Student correlation p value"),
  832. cex.main=0.8)
  833. ######################
  834. ## Plot eigengene-trait heatmap custom - using bicor color scale
  835. numericMetaCustom<-numericMeta1[,numericIndices]
  836. MEcors <- bicorAndPvalue(MEs,numericMetaCustom)
  837. moduleTraitCor <- MEcors$bicor
  838. moduleTraitPvalue <- MEcors$p
  839. moduleTraitPvalue<-signif(moduleTraitPvalue, 1)
  840. moduleTraitPvalue[moduleTraitPvalue > as.numeric(0.05)]<-as.character("")
  841. textMatrix = moduleTraitPvalue; #paste(signif(moduleTraitCor, 2), " / (", moduleTraitPvalue, ")", sep = "");
  842. dim(textMatrix) = dim(moduleTraitCor)
  843. #textMatrix = gsub("()", "", textMatrix,fixed=TRUE)
  844. labelMat<-matrix(nrow=(length(names(MEs))), ncol=2,data=c(rep(1:(length(names(MEs)))),labels2colors(1:(length(names(MEs))))))
  845. labelMat<-labelMat[match(names(MEs),labelMat[,2]),]
  846. for (i in 1:(length(names(MEs)))) { labelMat[i,1]<-paste("M",labelMat[i,1],sep="") }
  847. for (i in 1:length(names(MEs))) { labelMat[i,2]<-paste("ME",labelMat[i,2],sep="") }
  848. #rowMin(moduleTraitPvalue) # if we want to resort rows by min P value in the row
  849. xlabAngle <- if(nModules>75) { 90 } else { 45 }
  850. par(mar=c(16, 12, 3, 3) )
  851. par(mfrow=c(1,1))
  852. bw<-colorRampPalette(c("#0058CC", "white"))
  853. wr<-colorRampPalette(c("white", "#CC3300"))
  854. colvec<-c(bw(50),wr(50))
  855. labeledHeatmap(Matrix = t(moduleTraitCor)[,],
  856. yLabels = colnames(numericMetaCustom),
  857. xLabels = labelMat[,2],
  858. xSymbols = labelMat[,1],
  859. xColorLabels=TRUE,
  860. colors = colvec,
  861. textMatrix = t(textMatrix)[,],
  862. setStdMargins = FALSE,
  863. cex.text = 0.5,
  864. cex.lab.x = cexy,
  865. xLabelsAngle = xlabAngle,
  866. verticalSeparator.x=c(rep(c(1:length(colnames(MEs))),as.numeric(ncol(MEs)))),
  867. verticalSeparator.col = 1,
  868. verticalSeparator.lty = 1,
  869. verticalSeparator.lwd = 1,
  870. verticalSeparator.ext = 0,
  871. horizontalSeparator.y=c(rep(c(1:ncol(numericMetaCustom)),ncol(numericMetaCustom))),
  872. horizontalSeparator.col = 1,
  873. horizontalSeparator.lty = 1,
  874. horizontalSeparator.lwd = 1,
  875. horizontalSeparator.ext = 0,
  876. zlim = c(-1,1),
  877. main = "Module-trait Relationships\n Heatmap scale: signed bicor r-value", # \n (Signif. p-values shown as text)"),
  878. cex.main=0.8)
  879. # # turn your grouping column into an ordered factor
  880. # Group <- factor(numericMeta1$Group,
  881. # levels = c("CT", "AsymAD", "AD"))
  882. #
  883. # # then build your metadata frame
  884. # metdat <- data.frame(
  885. # Group = Group,
  886. # Age = as.numeric(numericMeta1$Age),
  887. # Gender = Gender
  888. # )
  889. #
  890. # # quick check
  891. # str(metdat)
  892. #
  893. ## Plot annotated heatmap - annotate all the metadata, plot the eigengenes!
  894. # This is where we will first use the Grouping vector of string group descriptions we set above.
  895. toplot <- MEs
  896. colnames(toplot) <- colnames(MEs)
  897. rownames(toplot) <- rownames(MEs)
  898. toplot <- t(toplot)
  899. pvec <- pvec[match(names(pvec),rownames(toplot))]
  900. #rownames(toplot) <- paste(rownames(toplot),"\np = ",signif(pvec,2),sep="")
  901. rownames(toplot) <- paste(orderedModules[match(colnames(MEs),orderedModules[,2]),1]," ",rownames(toplot),"p=",signif(pvec,2),sep="")
  902. # add any traits of interest you want to be in the legend
  903. Gender=as.numeric(numericMeta1$Sex)
  904. Gender[Gender==0]<-"Female"
  905. Gender[Gender==1]<-"Male"
  906. metdat=data.frame(Group=Grouping,Age=as.numeric(numericMeta1$Age), Gender=Gender)
  907. # set colors for the traits in the legend
  908. dev.new(width = 10, height = 10) #opens in bigger ploting window
  909. heatmapLegendColors=list('Group'=c("dodgerblue","goldenrod","seagreen3"), #,"hotpink","purple"),
  910. 'Age'=c("white","darkgreen"), #young to old
  911. 'Gender'=c("pink","dodgerblue"), #F, M
  912. 'Modules'=sort(colnames(MEs)))
  913. library(NMF)
  914. par(mfrow=c(1,1))
  915. aheatmap(x=toplot, ## Numeric Matrix
  916. main="Plot of Eigengene-Trait Relationships - SAMPLES IN ORIGINAL, e.g. BATCH OR REGION ORDER",
  917. annCol=metdat,
  918. annRow=data.frame(Modules=colnames(MEs)),
  919. annColors=heatmapLegendColors,
  920. border=list(matrix = TRUE),
  921. scale="row",
  922. distfun="correlation",hclustfun="average", ## Clustering options
  923. cexRow=0.8, ## Character sizes
  924. cexCol=0.8,
  925. col=blueWhiteRed(100), ## Color map scheme
  926. treeheight=80,
  927. Rowv=TRUE, Colv=NA) ## Do not cluster columns - keep given order
  928. aheatmap(x=toplot, ## Numeric Matrix
  929. main="Plot of Eigengene-Trait Relationships - SAMPLES CLUSTERED",
  930. annCol=metdat,
  931. annRow=data.frame(Modules=colnames(MEs)),
  932. annColors=heatmapLegendColors,
  933. border=list(matrix = TRUE),
  934. scale="row",
  935. distfun="correlation",hclustfun="average", ## Clustering options
  936. cexRow=0.8, ## Character sizes
  937. cexCol=0.8,
  938. col=blueWhiteRed(100), ## Color map scheme
  939. treeheight=80,
  940. Rowv=TRUE,Colv=TRUE) ## Cluster columns
  941. dev.off()
  942. # library(dplyr)
  943. #
  944. # numericMeta1 <- numericMeta1 %>%
  945. # rename(
  946. # FontralDP = FrontalDP,
  947. # )
  948. ######################################
  949. ## Change the below code in the for loop using the following session output
  950. library(gplots) #for col2hex() fn
  951. library(beeswarm)
  952. ## Get module-trait bicor correlations (append to verboseScatterplot title below)
  953. #numericMetaCustom<-numericMeta[,numericIndices]
  954. MEcors <- bicorAndPvalue(MEs,numericMetaCustom)
  955. moduleTraitCor <- MEcors$bicor
  956. moduleTraitPvalue <- MEcors$p
  957. #These are your numerically coded traits:
  958. colnames(numericMeta1)[numericIndices] #choose traits for correlation scatterplots (verboseScatterplot functions below)
  959. #These are your ANOVA sample groups and the number of samples in each
  960. table(Grouping) #alphabetically ordered, you choose the order of groups in the boxplot function by typing them in
  961. ## Make changes after checking output on console for the above 2 lines
  962. par(mfrow=c(4,6))
  963. par(mar=c(4.5,6,4.5,1.5))
  964. for (i in 1:(nrow(toplot))) { # grey already excluded, no -1
  965. titlecolor<-if(signif(pvec,2)[i] <0.05) { "red" } else { "black" }
  966. boxplot(toplot[i,]~factor(Grouping,c("Control","AsymAD","AD")),col=colnames(MEs)[i],ylab="Eigenprotein Value",main=paste0(orderedModules[match(colnames(MEs)[i],orderedModules[,2]),1]," ",colnames(MEs)[i],"\np = ",signif(pvec,2)[i]),xlab=NULL,las=2,col.main=titlecolor) #no outliers: ,outline=FALSE)
  967. transcol=paste0(col2hex(colnames(MEs)[i]),"99")
  968. beeswarm(toplot[i,]~factor(Grouping,c("Control","AsymAD","AD")),method="swarm",add=TRUE,corralWidth=0.5,vertical=TRUE,pch=21,bg=transcol,col="black",cex=0.8,corral="gutter") #more like prism ; #bg=goldenrod #DDA43B "#DDA43B99"
  969. # titlecolor<-if(signif(pvec.Apoe,2)[i] <0.05) { "red" } else { "black" }
  970. # boxplot(toplot[i,]~factor(ApoE,c("2/3","3/3","3/4","4/4")),col=colnames(MEs)[i],ylab="Eigenprotein Value",main=paste0(orderedModules[match(colnames(MEs)[i],orderedModules[,2]),1]," ",colnames(MEs)[i],"\nK-W p = ",signif(pvec.Apoe,2)[i]),xlab="APOE genotype",las=2,col.main=titlecolor) #no outliers: ,outline=FALSE)
  971. # transcol=paste0(col2hex(colnames(MEs)[i]),"99")
  972. # beeswarm(toplot[i,]~factor(ApoE,c("2/3","3/3","3/4","4/4")),method="swarm",add=TRUE,corralWidth=0.5,vertical=TRUE,pch=21,bg=transcol,col="black",cex=0.8,corral="gutter") #more like prism ; #bg=goldenrod #DDA43B "#DDA43B99"
  973. verboseScatterplot(x=numericMeta1[,"CERAD"],y=toplot[i,],xlab="CERAD Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"CERAD"],2),", p=",signif(moduleTraitPvalue[i,"CERAD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"CERAD"]<0.05) { "red" } else { "black" })
  974. verboseScatterplot(x=numericMeta1[,"BRAAK"],y=toplot[i,],xlab="Braak Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"BRAAK"],2),", p=",signif(moduleTraitPvalue[i,"BRAAK"],2),"\n"),col.main=if(moduleTraitPvalue[i,"BRAAK"]<0.05) { "red" } else { "black" })
  975. verboseScatterplot(x=numericMeta1[,"FrontalNP"],y=toplot[i,],xlab="FrontalNP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNP"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNP"]<0.05) { "red" } else { "black" })
  976. verboseScatterplot(x=numericMeta1[,"FontralDP"],y=toplot[i,],xlab="FrontalDP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FontralDP"],2),", p=",signif(moduleTraitPvalue[i,"FontralDP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FontralDP"]<0.05) { "red" } else { "black" })
  977. verboseScatterplot(x=numericMeta1[,"FrontalNFT"],y=toplot[i,],xlab="FrontalNFT",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNFT"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNFT"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNFT"]<0.05) { "red" } else { "black" })
  978. verboseScatterplot(x=numericMeta1[,"MMSE"],y=toplot[i,],xlab="MMSE",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"MMSE"],2),", p=",signif(moduleTraitPvalue[i,"MMSE"],2),"\n"),col.main=if(moduleTraitPvalue[i,"MMSE"]<0.05) { "red" } else { "black" })
  979. #verboseScatterplot(x=numericMeta[,"ABC"],y=toplot[i,],xlab="ABC",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"ABC"],2),", p=",signif(moduleTraitPvalue[i,"ABC"],2),"\n"),col.main=if(moduleTraitPvalue[i,"ABC"]<0.05) { "red" } else { "black" })
  980. }
  981. dev.off()
  982. # Open PDF device
  983. pdf("01.30.26.updated_module_plots.pdf", width = 14, height = 10) # adjust width/height as needed
  984. # Set plotting layout
  985. par(mfrow=c(4,6))
  986. par(mar=c(4.5,6,4.5,1.5))
  987. # Your existing for loop
  988. for (i in 1:(nrow(toplot))) { # grey already excluded, no -1
  989. titlecolor <- if(signif(pvec,2)[i] < 0.05) { "red" } else { "black" }
  990. # Boxplot + beeswarm
  991. boxplot(
  992. toplot[i,] ~ factor(Grouping, c("Control","AsymAD","AD")),
  993. col = colnames(MEs)[i],
  994. ylab = "Eigenprotein Value",
  995. main = paste0(orderedModules[match(colnames(MEs)[i], orderedModules[,2]),1], " ", colnames(MEs)[i], "\np = ", signif(pvec,2)[i]),
  996. xlab = NULL,
  997. las = 2,
  998. col.main = titlecolor
  999. )
  1000. transcol = paste0(col2hex(colnames(MEs)[i]), "99")
  1001. beeswarm(
  1002. toplot[i,] ~ factor(Grouping, c("Control","AsymAD","AD")),
  1003. method = "swarm",
  1004. add = TRUE,
  1005. corralWidth = 0.5,
  1006. vertical = TRUE,
  1007. pch = 21,
  1008. bg = transcol,
  1009. col = "black",
  1010. cex = 0.8,
  1011. corral = "gutter"
  1012. )
  1013. # Verbose scatterplots (your existing calls)
  1014. verboseScatterplot(x=numericMeta1[,"CERAD"],y=toplot[i,],xlab="CERAD Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"CERAD"],2),", p=",signif(moduleTraitPvalue[i,"CERAD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"CERAD"]<0.05) { "red" } else { "black" })
  1015. verboseScatterplot(x=numericMeta1[,"BRAAK"],y=toplot[i,],xlab="Braak Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"BRAAK"],2),", p=",signif(moduleTraitPvalue[i,"BRAAK"],2),"\n"),col.main=if(moduleTraitPvalue[i,"BRAAK"]<0.05) { "red" } else { "black" })
  1016. verboseScatterplot(x=numericMeta1[,"FrontalNP"],y=toplot[i,],xlab="FrontalNP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNP"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNP"]<0.05) { "red" } else { "black" })
  1017. verboseScatterplot(x=numericMeta1[,"FontralDP"],y=toplot[i,],xlab="FrontalDP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FontralDP"],2),", p=",signif(moduleTraitPvalue[i,"FontralDP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FontralDP"]<0.05) { "red" } else { "black" })
  1018. verboseScatterplot(x=numericMeta1[,"FrontalNFT"],y=toplot[i,],xlab="FrontalNFT",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNFT"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNFT"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNFT"]<0.05) { "red" } else { "black" })
  1019. verboseScatterplot(x=numericMeta1[,"MMSE"],y=toplot[i,],xlab="MMSE",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"MMSE"],2),", p=",signif(moduleTraitPvalue[i,"MMSE"],2),"\n"),col.main=if(moduleTraitPvalue[i,"MMSE"]<0.05) { "red" } else { "black" })
  1020. # Repeat for the other numeric traits (BRAAK, FrontalNP, etc.)
  1021. # ... your existing verboseScatterplot calls ...
  1022. }
  1023. # Close PDF device
  1024. dev.off()
  1025. # clean spine names
  1026. numericMeta1 <- numericMeta1 %>%
  1027. rename(
  1028. FrontalDP = FontralDP,
  1029. total_length_um = Total.length..um.,
  1030. total_spines = Total.spines,
  1031. spine_density = Spine.Density.per.10um,
  1032. spine_length = Backbone.Length.µm.,
  1033. volume = Volume.µm..,
  1034. head_diameter = Head.Diameter.µm.,
  1035. thin_spine_density = Thin.spine.density,
  1036. stubby_spine_density = Stubby.spine.density,
  1037. mushroom_spine_density = Mushroom.spine.density,
  1038. filopodia_spine_density = Filopodia.spine.density,
  1039. thin_spine_length = Length.of.Thin,
  1040. stubby_spine_length = Length.of.stubby,
  1041. mushroom_spine_length = Length.of.Mushroom,
  1042. filopodia_spine_length = Length.of.Filopodia,
  1043. thin_head_diameter = Head.D...thin,
  1044. stubby_head_diameter = Head.D...stubby,
  1045. mushroom_head_diameter = Head.D...mushroom,
  1046. filopodia_head_diameter = Head.D...filopodia,
  1047. thin_volume = Volume...T,
  1048. stubby_volume = Volume...S,
  1049. mushroom_volume = Volume...M,
  1050. filopodia_volume = Volume...F
  1051. )
  1052. numericIndices <- match(allTraits, colnames(numericMeta1))
  1053. library(gplots) #for col2hex() fn
  1054. library(WGCNA)
  1055. library(Cairo)
  1056. library(beeswarm)
  1057. # Define all spine traits to loop over
  1058. allTraits <- c(
  1059. "total_length_um", "total_spines", "spine_density", "spine_length",
  1060. "volume", "head_diameter",
  1061. "thin_spine_density", "stubby_spine_density", "mushroom_spine_density", "filopodia_spine_density",
  1062. "thin_spine_length", "stubby_spine_length", "mushroom_spine_length", "filopodia_spine_length",
  1063. "thin_head_diameter", "stubby_head_diameter", "mushroom_head_diameter", "filopodia_head_diameter",
  1064. "thin_volume", "stubby_volume", "mushroom_volume", "filopodia_volume"
  1065. )
  1066. #sanity check
  1067. setdiff(allTraits, colnames(numericMeta1))
  1068. # Open PDF device
  1069. # CairoPDF(file = paste0(rootdir, "01.30.26_spine_plots.pdf"), width = 16, height = 12)
  1070. # Set up layout and margins
  1071. par(mfrow = c(4, 6))
  1072. par(mar = c(4.5, 6, 4.5, 1.5))
  1073. # Loop through each module
  1074. for (i in seq_len(nrow(toplot))) {
  1075. y <- toplot[i, ]
  1076. moduleCol <- colnames(MEs)[i]
  1077. moduleName <- rownames(toplot)[i]
  1078. # Panel 1: Boxplot + Beeswarm by group
  1079. boxplot(y ~ factor(Grouping, c("Control", "AsymAD", "AD")),
  1080. col = moduleCol, ylab = "Eigengene Value",
  1081. main = moduleName, xlab = NULL, las = 2)
  1082. beeswarm(y ~ factor(Grouping, c("Control", "AsymAD", "AD")),
  1083. add = TRUE, method = "swarm", pch = 21,
  1084. bg = paste0(col2hex(moduleCol), "99"), corral = "gutter")
  1085. # Panels 2–N: Trait correlations
  1086. for (trait in allTraits) {
  1087. if (!trait %in% colnames(numericMeta1)) next
  1088. x_raw <- numericMeta1[[trait]]
  1089. x <- suppressWarnings(as.numeric(as.character(x_raw)))
  1090. ok <- is.finite(x) & is.finite(y)
  1091. if (sum(ok) < 2) next
  1092. verboseScatterplot(
  1093. x = x[ok],
  1094. y = y[ok],
  1095. xlab = trait,
  1096. ylab = "Eigenprotein",
  1097. abline = TRUE,
  1098. cex.axis = 1, cex.lab = 1, cex = 1,
  1099. col = "black", bg = moduleCol, pch = 21,
  1100. main = paste0(
  1101. "bicor=", if (is.numeric(moduleTraitCor[i, trait]) && is.finite(moduleTraitCor[i, trait])) signif(moduleTraitCor[i, trait], 2) else "NA",
  1102. ", p=", if (is.numeric(moduleTraitPvalue[i, trait]) && is.finite(moduleTraitPvalue[i, trait])) signif(moduleTraitPvalue[i, trait], 2) else "NA"
  1103. ),
  1104. col.main = if (is.numeric(moduleTraitPvalue[i, trait]) && moduleTraitPvalue[i, trait] < 0.05) "red" else "black"
  1105. )
  1106. }
  1107. frame() # starts a new page of 24 panels if layout is full
  1108. }
  1109. # Close the PDF device
  1110. dev.off()
  1111. for (i in 1:(nrow(toplot))) { # grey already excluded, no -1
  1112. verboseScatterplot(
  1113. x = numericMeta1$spine_density,
  1114. y = toplot[i,],
  1115. xlab = "Spine Density 10um",
  1116. ylab = "Eigenprotein",
  1117. abline = TRUE,
  1118. cex.axis = 1, cex.lab = 1, cex = 1,
  1119. col = "black",
  1120. bg = colnames(MEs)[i],
  1121. pch = 21,
  1122. main = paste0(
  1123. "bicor=", signif(moduleTraitCor[i, "spine_density"], 2),
  1124. ", p=", signif(moduleTraitPvalue[i, "spine_density"], 2), "\n"
  1125. ),
  1126. col.main = if (moduleTraitPvalue[i, "spine_density"] < 0.05) { "red" } else { "black" }
  1127. )
  1128. }
  1129. library(RColorBrewer)
  1130. library(Cairo)
  1131. library(beeswarm)
  1132. # Define all spine traits to loop over
  1133. allTraits <- c(
  1134. "total_length_um", "total_spines", "spine_density", "spine_length",
  1135. "volume", "head_diameter",
  1136. "thin_spine_density", "stubby_spine_density", "mushroom_spine_density", "filopodia_spine_density",
  1137. "thin_spine_length", "stubby_spine_length", "mushroom_spine_length", "filopodia_spine_length",
  1138. "thin_head_diameter", "stubby_head_diameter", "mushroom_head_diameter", "filopodia_head_diameter",
  1139. "thin_volume", "stubby_volume", "mushroom_volume", "filopodia_volume"
  1140. )
  1141. # Sanity check
  1142. setdiff(allTraits, colnames(numericMeta1)) # should be empty
  1143. # Open PDF device
  1144. CairoPDF(file = paste0(rootdir, "01.30.26_spine_plots_fixed.pdf"), width = 16, height = 12)
  1145. # Set up layout and margins
  1146. par(mfrow = c(4, 6))
  1147. par(mar = c(4.5, 6, 4.5, 1.5))
  1148. # Loop through each module
  1149. for (i in seq_len(nrow(toplot))) {
  1150. # Extract module vector
  1151. y <- toplot[i, ]
  1152. # Extract module color (column of MEs)
  1153. moduleCol <- colnames(MEs)[i]
  1154. # Extract module name (just the color, remove any "M# " prefix or "p=" suffix)
  1155. moduleName <- rownames(toplot)[i]
  1156. moduleColor <- sub(".* ", "", moduleName) # remove M# prefix
  1157. moduleColor <- sub("p=.*$", "", moduleColor) # remove p-value suffix
  1158. # Panel 1: Boxplot + Beeswarm by group
  1159. boxplot(y ~ factor(Grouping, c("Control", "AsymAD", "AD")),
  1160. col = moduleCol, ylab = "Eigengene Value",
  1161. main = moduleName, xlab = NULL, las = 2)
  1162. beeswarm(y ~ factor(Grouping, c("Control", "AsymAD", "AD")),
  1163. add = TRUE, method = "swarm", pch = 21,
  1164. bg = paste0(col2hex(moduleCol), "99"), corral = "gutter")
  1165. # Panels 2–N: Trait correlations
  1166. for (trait in allTraits) {
  1167. # Skip if trait doesn't exist
  1168. if (!trait %in% colnames(numericMeta1)) next
  1169. # Get numeric trait vector
  1170. x_raw <- numericMeta1[[trait]]
  1171. x <- suppressWarnings(as.numeric(as.character(x_raw)))
  1172. ok <- is.finite(x) & is.finite(y)
  1173. if (sum(ok) < 2) next
  1174. # Extract correlation and p-value
  1175. bicor_val <- moduleTraitCor[moduleColor, trait]
  1176. pval_val <- moduleTraitPvalue[moduleColor, trait]
  1177. # Plot
  1178. verboseScatterplot(
  1179. x = x[ok],
  1180. y = y[ok],
  1181. xlab = trait,
  1182. ylab = "Eigenprotein",
  1183. abline = TRUE,
  1184. cex.axis = 1, cex.lab = 1, cex = 1,
  1185. col = "black", bg = moduleCol, pch = 21,
  1186. main = paste0(
  1187. "bicor=", if (is.numeric(bicor_val) && is.finite(bicor_val)) signif(bicor_val, 2) else "NA",
  1188. ", p=", if (is.numeric(pval_val) && is.finite(pval_val)) signif(pval_val, 2) else "NA"
  1189. ),
  1190. col.main = if (is.numeric(pval_val) && pval_val < 0.05) "red" else "black"
  1191. )
  1192. }
  1193. frame() # start new page if layout full
  1194. }
  1195. # Close the PDF device
  1196. dev.off()
  1197. ## EH tells us the module’s overall expression is associated with disease status
  1198. # Sort all modules by ascending p-value
  1199. sortedPV <- sort(pvec)
  1200. # View the five smallest
  1201. head(sortedPV, 42)
  1202. #d2 5.3.2025
  1203. # brown royalblue lightcyan magenta midnightblue
  1204. # 0.0001046431 0.0003628619 0.0047374295 0.0069079084 0.0094156482
  1205. #d4 5.3.2025
  1206. # red darkgreen darkgrey greenyellow lightcyan1
  1207. # 5.039833e-05 2.708332e-04 3.420530e-04 1.174826e-03 1.217107e-03
  1208. #d4
  1209. # black cyan darkgrey yellow orangered4
  1210. # 0.0001534039 0.0001678707 0.0010807062 0.0022883984 0.0026763605
  1211. ########################################
  1212. #Write Module Membership/kME table
  1213. orderedModulesWithGrey=rbind(c("M0","grey"),orderedModules)
  1214. kMEtableSortVector<-apply( as.data.frame(cbind(net$colors,kMEdat)),1,function(x) if(!x[1]=="grey") { paste0(paste(orderedModulesWithGrey[match(x[1],orderedModulesWithGrey[,2]),],collapse=" "),"|",round(as.numeric(x[which(colnames(kMEdat)==paste0("kME",x[1]))+1]),4)) } else { paste0("grey|AllKmeAvg:",round(mean(as.numeric(x[-1],na.rm=TRUE)),4)) } )
  1215. kMEtable=cbind(c(1:nrow(cleanDatReg)),rownames(cleanDatReg),net$colors,kMEdat,kMEtableSortVector)[order(kMEtableSortVector,decreasing=TRUE),]
  1216. write.table(kMEtable,file=paste0(outputtabs,"/01.30.26.power8.ModuleAssignments-",FileBaseName,".txt"),sep="\t",row.names=FALSE)
  1217. #(load above file in excel and apply green-yellow-red conditional formatting heatmap to the columns with kME values); then save as excel.
  1218. ## saved image of R session
  1219. save.image(paste0("01.30.26.module_membership_saved.image.",projectFilesOutputTag,".Rdata")) #overwrites
  1220. # GO ELITE #
  1221. ######################## EDIT THESE VARIABLES (USER PARAMETERS SET IN GLOBAL ENVIRONMENT) ############################################
  1222. #inputFile <- "ENDO_MG_TWO_WAY_LIST_NTS_v02b_forGOelite.csv" #Sample File 1 - has full human background
  1223. #inputFile <- "ModuleAssignments_Jingting32TH_BOOTaspRegr_power8_MergeHeight0.07_PAMstageTRUE_ds2.csv" #Sample File 2 - WGCNA kME table for (Dai, et al, 2019)
  1224. #INPUT CSV FILE - in the filePath folder.
  1225. #Can be formatted as Kme table from WGCNA pipeline, or
  1226. #can be a CSV of columns, one symbol or UniqueID (Symbol|...) list per column, with the LIST NAMEs in row 1
  1227. #in this case, the longest list is used as background or the "universe" for the FET contingencies
  1228. # For simple columnwise list input, DON'T FORGET TO PUT THE APPROPRIATE BACKGROUND LIST IN, OR RESULTS WILL BE UNRELIABLE.
  1229. filePath <- "C:/Users/ehobby/Documents/EH_Emory41_Update/GO_Emory41" #gsub("//","/",outputfigs)
  1230. #Folder that (may) contain the input file specified above, and which will contain the outFilename project Folder.
  1231. outFilename <- "GO_Emory41_Results_d4"
  1232. #SUBFOLDER WITH THIS NAME WILL BE CREATED, and .PDF + .csv file using the same name will be created within this folder.
  1233. outputGOeliteInputs=FALSE
  1234. #If TRUE, GO Elite background file and module or list-specific input files will be created in the outFilename subfolder.
  1235. maxBarsPerOntology=5
  1236. #Ontologies per ontology type, used for generating the PDF report; does not limit tabled output
  1237. GMTdatabaseFile="C:/Users/ehobby/Documents/EH_Emory41_Update/GO_Emory41/Human_GO_AllPathways_noPFOCR_with_GO_iea_September_16_2024_symbol.gmt" # e.g. "Human_GO_AllPathways_with_GO_iea_June_01_2022_symbol.gmt"
  1238. # Current month release will be downloaded if file does not exist.
  1239. # **Specify a nonexistent file to always download the current database to this folder.**
  1240. # Database .GMT file will be saved to the specified folder with its original date-specific name.
  1241. #path/to/filename of ontology database for the appropriate species (no conversion is performed)
  1242. #BaderLab website links to their current monthly update of ontologies for Human, Mouse, and Rat, minimally
  1243. #http://download.baderlab.org/EM_Genesets/current_release/
  1244. #For more information, see documentation: http://baderlab.org/GeneSets
  1245. panelDimensions=c(3,2) #dimensions of the individual parblots within a page of the main barplot PDF output
  1246. pageDimensions=c(8.5,11) #main barplot PDF output page dimensions, in inches
  1247. color=c("darkseagreen3","lightsteelblue1","lightpink4","goldenrod","darkorange","gold")
  1248. # color <- c("#D0E3CA", "#A4D4A0", "#76C37A", "#4FA554", "#347C3A", "#1B4E23")
  1249. #colors respectively for ontology Types:
  1250. #"Biological Process","Molecular Function","Cellular Component","Reactome","WikiPathways","MSig.C2"
  1251. #must be valid R colors
  1252. modulesInMemory=TRUE
  1253. #uses cleanDat, net, and kMEdat from WGCNA systems biology pipeline already run, and these variables must be in memory
  1254. #inputFile will be ignored
  1255. ANOVAgroups=FALSE
  1256. #if true, modulesInMemory ignored. Volcano pipeline code should already have been run!
  1257. #inputFile will be ignored
  1258. ############ MUST HAVE AT LEAST 2 THREADS ENABLED TO RUN ############################################################################
  1259. parallelThreads=20
  1260. removeRedundantGOterms=TRUE
  1261. #if true, the 3 GO ontology types are collapased into a minimal set of less redundant terms using the below OBO file
  1262. GO.OBOfile<-"C:/Users/ehobby/Documents/EH_Emory41_Update/GO_Emory41/go.obo"
  1263. #only used and needed if above flag to remove redundant GO terms is TRUE.
  1264. #Download from http://current.geneontology.org/ontology/go.obo will commence into the specified folder if the specified filename does not exist.
  1265. #Does not appear to be species specific, stores all GO term relations and is periodically updated.
  1266. cocluster=TRUE
  1267. #If TRUE, output PDF of signed Zscore coclustering on GO:cellular component terms (useful for WGCNA modules)
  1268. ######################## END OF PARAMETER VARIABLES ###################################################################################
  1269. # colnames(MEs)[19] <- "lightyellow"
  1270. # colnames(MEs)[27] <- "white"
  1271. library(piano)
  1272. source("C:/Users/ehobby/Documents/EH_Emory41_Update/GO_Emory41/GOparallel-FET.R")
  1273. GOparallel() # parameters are set in global environment as above; if not set, the function falls back to defaults and looks for all inputs available.
  1274. # priority is given to modulesInMemory
  1275. ## saved image of R session
  1276. save.image(paste0("d4.power8.final.saved.image.",projectFilesOutputTag,".Rdata")) #overwrites
  1277. #############################################
  1278. ## speakeasy 2 code
  1279. #load Rdata file from WGCNA aka d4.power8.final.saved.image.EH_41BULK
  1280. library(speakeasyR)
  1281. library(WGCNA) # Network analysis package
  1282. library(NMF) # this package has a great annotated heatmap function - aheatmap
  1283. library(igraph)
  1284. library(ggplot2)
  1285. library(RColorBrewer)
  1286. library(Cairo) # nicer graphics, anti-aliased, etc. --text from windows output PDFs using CairoPDF() function may not load in Illustrator, though -- so also use the pdf() standard output function when generating PDF figures
  1287. ##Only for macs:
  1288. #CairoFonts(regular="Arial:style=Regular",bold="Arial:style=Bold",italic="Arial:style=Italic",bolditalic="Arial:style=Bold Italic,BoldItalic",symbol="Symbol")
  1289. adj_cleanDatReg <- adjacency(t(cleanDatReg), type="signed", power=1) #calculates signed adjacency
  1290. #Used one level of subclustering b/c gaiteri paper shows 1 level of subclustering could seperate "large communities into smaller communities"
  1291. #SE2 provides subclustering where the individual communities of the initial clustering will in turn be clustered into smaller communities.
  1292. #This behavior can be turned on by setting the subclusters to parameter to a value greater than 1.
  1293. #(The min_clust parameter determines the smallest community to consider for subclustering, if a community has fewer than min_clust nodes, it will not be subclustered further.)
  1294. set.seed(111)
  1295. se_mod <- speakeasyR::cluster(adj_cleanDatReg, seed = 111, subcluster = 2, min_clust = 100, verbose = TRUE, is_directed = TRUE)
  1296. ordering <- speakeasyR::order_nodes(adj_cleanDatReg, se_mod)
  1297. # confirm levels
  1298. dim(se_mod) # should be 3 x N
  1299. length(unique(se_mod[1, ]))
  1300. length(unique(se_mod[2, ]))
  1301. length(unique(se_mod[3, ]))
  1302. level <- 2
  1303. level_order <- ordering[level,]
  1304. level_memb <- se_mod[level, level_order]
  1305. color <- labels2colors(level_memb)
  1306. #save clustering heatmap as PNG, PDF literally does not load
  1307. heatmap(adj_cleanDatReg[level_order, level_order], scale = "none", Rowv = NA, Colv = NA,
  1308. RowSideColors = color, xlab = "SE2 Module")
  1309. # labeling the se2 modules with the protein names from cleanDatReg
  1310. SE2modColor <- labels2colors(se_mod[level, ])
  1311. names(SE2modColor) <- rownames(cleanDatReg)
  1312. stopifnot(identical(colnames(adj_cleanDatReg), rownames(adj_cleanDatReg)))
  1313. table(SE2modColor)
  1314. # black blue brown cyan green greenyellow magenta midnightblue
  1315. # 2 1254 3 1069 987 2 645 1034
  1316. # pink purple red salmon tan turquoise yellow
  1317. # 657 565 4 1164 1 822 3
  1318. # modules with binned grey module
  1319. # EH
  1320. # blue cyan green grey magenta midnightblue pink
  1321. # 1254 1069 987 15 645 1034 657
  1322. # purple salmon turquoise
  1323. # 565 1164 822
  1324. ##assign color to each protein species similar to net$colors in WGCNA and run plots below
  1325. ##if <50 species in a cluster (module) then re-allocate to grey
  1326. allocate_grey_SE2 <- names(table(SE2modColor)[sapply(table(SE2modColor), FUN = function(x)x<50)])
  1327. SE2modColor[SE2modColor %in% allocate_grey_SE2] <- "grey"
  1328. orderedModules <- matrix(nrow=length(unique(SE2modColor)), ncol=2)
  1329. colnames(orderedModules) <- c("Mnum", "Color")
  1330. orderedModules[,1] <- paste0("M", 1:length(unique(SE2modColor)))
  1331. orderedModules[,2] <- c("blue", "green", "turquoise", "purple", "magenta", "pink", "midnightblue", "cyan", "salmon", "grey")
  1332. orderedModules[match("grey", orderedModules[,2]),1] <- NA
  1333. numericMeta2 <- readRDS("numericMetawSpines.rds")
  1334. numericMeta <- numericMeta1
  1335. numericMeta <- numericMeta1_CW_spines_01.31.26
  1336. ## 02.01.26
  1337. ## spine data fix
  1338. numericMeta1 <- readRDS("numericMeta1_CW_spines_01.31.26.rds")
  1339. numericMeta <- numericMeta1
  1340. saveRDS(cleanDatReg, file = "cleanDatReg")
  1341. ######################################################################################################
  1342. ## Output GlobalNetworkPlots and kMEtable
  1343. ####################################################################################################################
  1344. projectFilesOutputTag = "EH_41BULK"
  1345. FileBaseName=paste0(projectFilesOutputTag,"_SE2")
  1346. out_dir <- "C:/Users/ehobby/Documents/SE2_rerun_02.01.26"
  1347. out_file <- file.path(out_dir, "02.16.26_GlobalNetworkPlots-41Bulk.pdf")
  1348. CairoPDF(file = out_file, width = 16, height = 12)
  1349. #plot(1:10, 1:10, main = "TEST")
  1350. ## Plot dendrogram with module colors and trait correlations
  1351. MEs<-tmpMEs<-data.frame()
  1352. MEList = moduleEigengenes(t(cleanDatReg), colors = SE2modColor)
  1353. MEs = orderMEs(MEList$eigengenes)
  1354. colnames(MEs)<-gsub("ME","",colnames(MEs)) #let's be consistent in case prefix was added, remove it.
  1355. rownames(MEs)<-rownames(numericMeta)
  1356. #numericIndices<-unique(c( which(!is.na(apply(numericMeta,2,function(x) sum(as.numeric(x))))), which(!(apply(numericMeta,2,function(x) sum(as.numeric(x),na.rm=T)))==0) ))
  1357. #Warnings OK; This determines which traits are numeric and if forced to numeric values, non-NA values do not sum to 0
  1358. #custom column selection in graphs
  1359. numericIndices<-c(8:10, 14:21, 25:46)
  1360. geneSignificance <- cor(sapply(numericMeta[,numericIndices],as.numeric),t(cleanDatReg),use="pairwise.complete.obs")
  1361. rownames(geneSignificance) <- colnames(numericMeta)[numericIndices]
  1362. geneSigColors <- t(numbers2colors(t(geneSignificance),,signed=TRUE,lim=c(-1,1),naColor="black"))
  1363. rownames(geneSigColors) <- colnames(numericMeta)[numericIndices]
  1364. ######################
  1365. ## Find differences between Groups (as defined in Traits input file); Finalize Grouping of Samples for ANOVA
  1366. #Set a vector of strings that represent each sample in order, calling out each sample as a member of named groups (used by GlobalNetworkPlot boxplots, and later, ANOVA DiffEx)
  1367. Grouping<-numericMeta$Group #typically there is a column "Group" loaded as a column in the traits.csv file
  1368. ApoE<- numericMeta$ApoE
  1369. ABC<- numericMeta$ABC
  1370. #ABC[numericMeta$ABC==0]<-"None"
  1371. #ABC[numericMeta$ABC==1]<-"Low"
  1372. #ABC[numericMeta$ABC==2]<-"Intermediate"
  1373. #ABC[numericMeta$ABC==3]<-"High"
  1374. # This gets ANOVA (Kruskal-Wallis) nonparametric p-values for groupwise comparison of interest.
  1375. # look at numericMeta (traits data) and choose traits to use for linear model-determination of p value
  1376. head(numericMeta)
  1377. # # Change below line to point to a factored trait, which will define groups for ANOVA
  1378. # regvars <- data.frame(as.factor( Grouping ), as.numeric(numericMeta$Age), as.numeric(numericMeta$Sex))
  1379. # colnames(regvars) <- c("Grouping","Age","Sex") ## data frame with covaraites incase we want to try multivariate regression
  1380. # ##aov1 <- aov(data.matrix(MEs)~AD,data=regvars) ## ANOVA framework yields same results
  1381. # lm1 <- lm(data.matrix(MEs)~Grouping,data=regvars) #sex and age effects are removed by the linear model
  1382. #
  1383. # pvec <- rep(NA,ncol(MEs))
  1384. # for (i in 1:ncol(MEs)) {
  1385. # f <- summary(lm1)[[i]]$fstatistic ## Get F statistics
  1386. # pvec[i] <- pf(f[1],f[2],f[3],lower.tail=F) ## Get the p-value corresponding to the whole model
  1387. # }
  1388. # names(pvec) <- colnames(MEs)
  1389. #
  1390. # ## Find differences between APOE Risk Groups
  1391. # regvars <- data.frame(as.factor(numericMeta[,"ApoE"]),as.numeric(numericMeta[,"Age"]),as.factor(numericMeta[,"Sex"]),as.numeric(numericMeta[,"PMI"]))
  1392. # colnames(regvars) <- c("Grouping","Age","batch","PMI") ## data frame with covaraites in case we want to try multivariate regression
  1393. # #aov1 <- aov(data.matrix(MEs)~Group,data=regvars) ## ANOVA framework yields same results
  1394. # lm1 <- lm(data.matrix(MEs)~Grouping,data=regvars)
  1395. #
  1396. # pvec.Apoe <- rep(NA,ncol(MEs))
  1397. # for (i in 1:ncol(MEs)) {
  1398. # f <- summary(lm1)[[i]]$fstatistic ## Get F statistics
  1399. # pvec.Apoe[i] <- pf(f[1],f[2],f[3],lower.tail=F) ## Get the p-value corresponding to the whole model
  1400. # }
  1401. # names(pvec.Apoe) <- colnames(MEs)
  1402. ## EH editts - changed covariates to Batch and PMI
  1403. # Create the covariate data frame
  1404. regvars <- data.frame(
  1405. Grouping = as.factor(numericMeta$Group),
  1406. Batch = as.factor(numericMeta$Batch), # treat Batch as a factor
  1407. PMI = as.numeric(numericMeta$PMI) # treat PMI as numeric
  1408. )
  1409. colnames(regvars) <- c("Grouping","Batch","PMI") ## data frame with covaraites incase we want to try multivariate regression
  1410. # Fit the multivariate linear model (one model across all SE2 modules)
  1411. lm1 <- lm(data.matrix(MEs) ~ Grouping + Batch + PMI, data = regvars)
  1412. # Extract p-values for each module
  1413. pvec <- rep(NA, ncol(MEs))
  1414. for (i in 1:ncol(MEs)) {
  1415. f <- summary(lm1)[[i]]$fstatistic
  1416. pvec[i] <- pf(f[1], f[2], f[3], lower.tail = FALSE)
  1417. }
  1418. names(pvec) <- colnames(MEs)
  1419. # (Optional) Adjust for multiple comparisons
  1420. pvec_fdr <- p.adjust(pvec, method = "fdr")
  1421. ## Find differences between APOE Risk Groups
  1422. regvars2 <- data.frame(
  1423. ApoE = as.factor(numericMeta$ApoE),
  1424. Batch = as.factor(numericMeta$Batch), # treat Batch as a factor
  1425. PMI = as.numeric(numericMeta$PMI) # treat PMI as numeric
  1426. )
  1427. colnames(regvars2) <- c("Grouping","Batch","PMI") ## data frame with covaraites incase we want to try multivariate regression
  1428. # Fit the multivariate linear model (one model across all SE2 modules)
  1429. lm1 <- lm(data.matrix(MEs) ~ Grouping + Batch + PMI, data = regvars2)
  1430. pvec.Apoe <- rep(NA,ncol(MEs))
  1431. for (i in 1:ncol(MEs)) {
  1432. f <- summary(lm1)[[i]]$fstatistic ## Get F statistics
  1433. pvec.Apoe[i] <- pf(f[1],f[2],f[3],lower.tail=F) ## Get the p-value corresponding to the whole model
  1434. }
  1435. names(pvec.Apoe) <- colnames(MEs)
  1436. ######################
  1437. ## Get sigend kME values
  1438. kMEdat <- signedKME(t(cleanDatReg), MEList$eigengenes, corFnc="bicor")
  1439. ######################
  1440. ## Plot eigengene-trait correlations - using p value of bicor for heatmap scale
  1441. library(RColorBrewer)
  1442. MEcors <- bicorAndPvalue(MEs,numericMeta[,numericIndices])
  1443. moduleTraitCor <- MEcors$bicor
  1444. moduleTraitPvalue <- MEcors$p
  1445. textMatrix = apply(moduleTraitCor,2,function(x) signif(x, 2))
  1446. #textMatrix = paste(signif(moduleTraitCor, 2), " (",
  1447. # signif(moduleTraitPvalue, 1), ")", sep = "");
  1448. #dim(textMatrix) = dim(moduleTraitCor)
  1449. par(mfrow=c(1,1))
  1450. par(mar = c(6, 8.5, 3, 3));
  1451. ## Display the correlation values within a heatmap plot
  1452. cexy <- if(nModules>75) { 0.8 } else { 1 }
  1453. colvec <- rep("white",1500)
  1454. colvec[1:500] <- colorRampPalette(rev(brewer.pal(8,"BuPu")[2:8]))(500)
  1455. colvec[501:1000]<-colorRampPalette(c("white",brewer.pal(8,"BuPu")[2]))(3)[2] #interpolated color for 0.05-0.1 p
  1456. labeledHeatmap(Matrix = apply(moduleTraitPvalue,2,as.numeric),
  1457. xLabels = colnames(numericMeta)[numericIndices],
  1458. yLabels = paste0("ME",names(MEs)),
  1459. ySymbols = names(MEs),
  1460. colorLabels = FALSE,
  1461. colors = colvec,
  1462. textMatrix = textMatrix,
  1463. setStdMargins = FALSE,
  1464. cex.text = 0.5,
  1465. cex.lab.y= cexy,
  1466. zlim = c(0,0.15),
  1467. main = paste("Module-trait relationships\n bicor r-value shown as text\nHeatmap scale: Student correlation p value"),
  1468. cex.main=0.8)
  1469. ######################
  1470. ## Plot eigengene-trait heatmap custom - using bicor color scale
  1471. numericMetaCustom<-numericMeta[,numericIndices]
  1472. MEcors <- bicorAndPvalue(MEs,numericMetaCustom)
  1473. moduleTraitCor <- MEcors$bicor
  1474. moduleTraitPvalue <- MEcors$p
  1475. moduleTraitPvalue<-signif(moduleTraitPvalue, 1)
  1476. moduleTraitPvalue[moduleTraitPvalue > as.numeric(0.05)]<-as.character("")
  1477. textMatrix = moduleTraitPvalue; #paste(signif(moduleTraitCor, 2), " / (", moduleTraitPvalue, ")", sep = "");
  1478. dim(textMatrix) = dim(moduleTraitCor)
  1479. #textMatrix = gsub("()", "", textMatrix,fixed=TRUE)
  1480. labelMat<-matrix(nrow=(length(names(MEs))), ncol=2,data=c(rep(1:(length(names(MEs)))),names(MEs)))
  1481. labelMat<-labelMat[match(names(MEs),labelMat[,2]),]
  1482. for (i in 1:(length(names(MEs)))) { labelMat[i,1]<-paste("M",labelMat[i,1],sep="") }
  1483. for (i in 1:length(names(MEs))) { labelMat[i,2]<-paste("ME",labelMat[i,2],sep="") }
  1484. #rowMin(moduleTraitPvalue) # if we want to resort rows by min P value in the row
  1485. xlabAngle <- if(nModules>75) { 90 } else { 45 }
  1486. par(mar=c(16, 12, 3, 3) )
  1487. par(mfrow=c(1,1))
  1488. bw<-colorRampPalette(c("#0058CC", "white"))
  1489. wr<-colorRampPalette(c("white", "#CC3300"))
  1490. colvec<-c(bw(50),wr(50))
  1491. labeledHeatmap(Matrix = t(moduleTraitCor)[,],
  1492. yLabels = colnames(numericMetaCustom),
  1493. xLabels = labelMat[,2],
  1494. xSymbols = labelMat[,1],
  1495. xColorLabels=TRUE,
  1496. colors = colvec,
  1497. textMatrix = t(textMatrix)[,],
  1498. setStdMargins = FALSE,
  1499. cex.text = 0.5,
  1500. cex.lab.x = cexy,
  1501. xLabelsAngle = xlabAngle,
  1502. verticalSeparator.x=c(rep(c(1:length(colnames(MEs))),as.numeric(ncol(MEs)))),
  1503. verticalSeparator.col = 1,
  1504. verticalSeparator.lty = 1,
  1505. verticalSeparator.lwd = 1,
  1506. verticalSeparator.ext = 0,
  1507. horizontalSeparator.y=c(rep(c(1:ncol(numericMetaCustom)),ncol(numericMetaCustom))),
  1508. horizontalSeparator.col = 1,
  1509. horizontalSeparator.lty = 1,
  1510. horizontalSeparator.lwd = 1,
  1511. horizontalSeparator.ext = 0,
  1512. zlim = c(-1,1),
  1513. main = "Module-trait Relationships\n Heatmap scale: signed bicor r-value", # \n (Signif. p-values shown as text)"),
  1514. cex.main=0.8)
  1515. ## Plot annotated heatmap - annotate all the metadata, plot the eigengenes!
  1516. # This is where we will first use the Grouping vector of string group descriptions we set above.
  1517. toplot <- MEs
  1518. colnames(toplot) <- colnames(MEs)
  1519. rownames(toplot) <- rownames(MEs)
  1520. toplot <- t(toplot)
  1521. pvec <- pvec[match(names(pvec),rownames(toplot))]
  1522. #rownames(toplot) <- paste(rownames(toplot),"\np = ",signif(pvec,2),sep="")
  1523. rownames(toplot) <- paste(orderedModules[match(colnames(MEs),orderedModules[,2]),1]," ",rownames(toplot)," | K-W p=",signif(pvec,2),sep="")
  1524. # add any traits of interest you want to be in the legend
  1525. Gender=as.numeric(numericMeta$Sex)
  1526. Gender[Gender==0]<-"Female"
  1527. Gender[Gender==1]<-"Male"
  1528. metdat=data.frame(Group=Grouping,Age=as.numeric(numericMeta$Age), Gender=Gender)
  1529. # set colors for the traits in the legend
  1530. heatmapLegendColors=list('Group'=c("dodgerblue","goldenrod","seagreen3","hotpink","purple"),
  1531. 'Age'=c("white","darkgreen"), #young to old
  1532. 'Gender'=c("pink","dodgerblue"), #F, M
  1533. 'Modules'=sort(colnames(MEs)))
  1534. library(NMF)
  1535. par(mfrow=c(1,1))
  1536. aheatmap(x=toplot, ## Numeric Matrix
  1537. main="Plot of Eigengene-Trait Relationships - SAMPLES IN ORIGINAL, e.g. BATCH OR REGION ORDER",
  1538. annCol=metdat,
  1539. annRow=data.frame(Modules=colnames(MEs)),
  1540. annColors=heatmapLegendColors,
  1541. border=list(matrix = TRUE),
  1542. scale="row",
  1543. distfun="correlation",hclustfun="average", ## Clustering options
  1544. cexRow=0.8, ## Character sizes
  1545. cexCol=0.8,
  1546. col=blueWhiteRed(100), ## Color map scheme
  1547. treeheight=80,
  1548. Rowv=TRUE, Colv=NA) ## Do not cluster columns - keep given order
  1549. aheatmap(x=toplot, ## Numeric Matrix
  1550. main="Plot of Eigengene-Trait Relationships - SAMPLES CLUSTERED",
  1551. annCol=metdat,
  1552. annRow=data.frame(Modules=colnames(MEs)),
  1553. annColors=heatmapLegendColors,
  1554. border=list(matrix = TRUE),
  1555. scale="row",
  1556. distfun="correlation",hclustfun="average", ## Clustering options
  1557. cexRow=0.8, ## Character sizes
  1558. cexCol=0.8,
  1559. col=blueWhiteRed(100), ## Color map scheme
  1560. treeheight=80,
  1561. Rowv=TRUE,Colv=TRUE) ## Cluster columns
  1562. ######################################
  1563. ## Change the below code in the for loop using the following session output
  1564. library(gplots) #for col2hex() fn
  1565. library(beeswarm)
  1566. ## Get module-trait bicor correlations (append to verboseScatterplot title below)
  1567. numericMetaCustom<-numericMeta[,numericIndices]
  1568. MEcors <- bicorAndPvalue(MEs,numericMetaCustom)
  1569. moduleTraitCor <- MEcors$bicor
  1570. moduleTraitPvalue <- MEcors$p
  1571. #These are your numerically coded traits:
  1572. colnames(numericMeta)[numericIndices] #choose traits for correlation scatterplots (verboseScatterplot functions below)
  1573. #These are your ANOVA sample groups and the number of samples in each
  1574. table(Grouping) #alphabetically ordered, you choose the order of groups in the boxplot function by typing them in
  1575. ## Make changes after checking output on console for the above 2 lines
  1576. par(mfrow=c(4,6))
  1577. par(mar=c(4.5,6,4.5,1.5))
  1578. for (i in 1:(nrow(toplot))) { # grey already excluded, no -1
  1579. titlecolor<-if(signif(pvec,2)[i] <0.05) { "red" } else { "black" }
  1580. boxplot(toplot[i,]~factor(Grouping,c("CT","AsymAD","AD")),col=colnames(MEs)[i],ylab="Eigenprotein Value",main=paste0(orderedModules[match(colnames(MEs)[i],orderedModules[,2]),1]," ",colnames(MEs)[i],"\nK-W p = ",signif(pvec,2)[i]),xlab=NULL,las=2,col.main=titlecolor) #no outliers: ,outline=FALSE)
  1581. transcol=paste0(col2hex(colnames(MEs)[i]),"99")
  1582. beeswarm(toplot[i,]~factor(Grouping,c("CT","AsymAD","AD")),method="swarm",add=TRUE,corralWidth=0.5,vertical=TRUE,pch=21,bg=transcol,col="black",cex=0.8,corral="gutter") #more like prism ; #bg=goldenrod #DDA43B "#DDA43B99"
  1583. titlecolor<-if(signif(pvec.Apoe,2)[i] <0.05) { "red" } else { "black" }
  1584. boxplot(toplot[i,]~factor(ApoE,c("E2/3","E3/3","E3/4","E4/4")),col=colnames(MEs)[i],ylab="Eigenprotein Value",main=paste0(orderedModules[match(colnames(MEs)[i],orderedModules[,2]),1]," ",colnames(MEs)[i],"\nK-W p = ",signif(pvec.Apoe,2)[i]),xlab="APOE genotype",las=2,col.main=titlecolor) #no outliers: ,outline=FALSE)
  1585. transcol=paste0(col2hex(colnames(MEs)[i]),"99")
  1586. beeswarm(toplot[i,]~factor(ApoE,c("E2/3","E3/3","E3/4","E4/4")),method="swarm",add=TRUE,corralWidth=0.5,vertical=TRUE,pch=21,bg=transcol,col="black",cex=0.8,corral="gutter") #more like prism ; #bg=goldenrod #DDA43B "#DDA43B99"
  1587. verboseScatterplot(x=numericMeta[,"CERAD"],y=toplot[i,],xlab="CERAD Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"CERAD"],2),", p=",signif(moduleTraitPvalue[i,"CERAD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"CERAD"]<0.05) { "red" } else { "black" })
  1588. verboseScatterplot(x=numericMeta[,"BRAAK"],y=toplot[i,],xlab="Braak Score",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"BRAAK"],2),", p=",signif(moduleTraitPvalue[i,"BRAAK"],2),"\n"),col.main=if(moduleTraitPvalue[i,"BRAAK"]<0.05) { "red" } else { "black" })
  1589. verboseScatterplot(x=numericMeta[,"FrontalNP"],y=toplot[i,],xlab="FrontalNP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNP"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNP"]<0.05) { "red" } else { "black" })
  1590. verboseScatterplot(x=numericMeta[,"FrontalDP"],y=toplot[i,],xlab="FrontalDP",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalDP"],2),", p=",signif(moduleTraitPvalue[i,"FrontalDP"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalDP"]<0.05) { "red" } else { "black" })
  1591. verboseScatterplot(x=numericMeta[,"FrontalNFT"],y=toplot[i,],xlab="FrontalNFT",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"FrontalNFT"],2),", p=",signif(moduleTraitPvalue[i,"FrontalNFT"],2),"\n"),col.main=if(moduleTraitPvalue[i,"FrontalNFT"]<0.05) { "red" } else { "black" })
  1592. verboseScatterplot(x=numericMeta[,"MMSE"],y=toplot[i,],xlab="MMSE",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"MMSE"],2),", p=",signif(moduleTraitPvalue[i,"MMSE"],2),"\n"),col.main=if(moduleTraitPvalue[i,"MMSE"]<0.05) { "red" } else { "black" })
  1593. }
  1594. ##outputs sample-by-sample eigenprotein barplots (not useful for large number of samples)
  1595. #while(!par('page')) plot.new()
  1596. #for (i in 1:nrow(toplot)) {
  1597. # barplot(height=rev(toplot[i,]),width=5,col=colnames(MEs)[i],xlab=paste(colnames(MEs)[i]," Eigenprotein Relative Expression"),main=rownames(toplot)[i],ylab=NULL,las=2,space=0.4,horiz=TRUE) #las=2 for rotated 90° X-axis labels main=rownames(toplot)[i]
  1598. ## text(bargr,par("usr")[3] - 0.025, srt=45, adj =1, labels= c(colnames(toplot)),xpd=TRUE,font=2) # bargr <- barplot(... above; gives rotated 45° x-axis labels but overwrites on top of existing ones
  1599. #}
  1600. dev.off()
  1601. # #### All spine corr plots
  1602. #
  1603. # CairoPDF(file=paste0(rootdir,"41Bulk-module-spine-plots.pdf"),width=16,height=12)
  1604. #
  1605. #
  1606. ## Make changes after checking output on console for the above 2 lines
  1607. par(mfrow=c(4,6))
  1608. par(mar=c(4.5,6,4.5,1.5))
  1609. for (i in 1:(nrow(toplot))) { # grey already excluded, no -1
  1610. verboseScatterplot(x=numericMeta$Spine.Density.10?m,y=toplot[i,],xlab="Spine Density 10um",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Spine.Density.10?m"],2),", p=",signif(moduleTraitPvalue[i,"Spine.Density.10?m"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Spine.Density.10?m"]<0.05) { "red" } else { "black" })
  1611. verboseScatterplot(x=numericMeta$Spine.Length..?m.,y=toplot[i,],xlab="Spine.Length..?m.",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Spine.Length..?m."],2),", p=",signif(moduleTraitPvalue[i,"Spine.Length..?m."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Spine.Length..?m."]<0.05) { "red" } else { "black" })
  1612. verboseScatterplot(x=numericMeta$Head.Diameter.?m.,y=toplot[i,],xlab="Head.Diameter.?m.",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Head.Diameter.?m."],2),", p=",signif(moduleTraitPvalue[i,"Head.Diameter.?m."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Head.Diameter.?m."]<0.05) { "red" } else { "black" })
  1613. verboseScatterplot(x=numericMeta$Neck.Diameter,y=toplot[i,],xlab="Neck.Diameter",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Neck.Diameter"],2),", p=",signif(moduleTraitPvalue[i,"Neck.Diameter"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Neck.Diameter"]<0.05) { "red" } else { "black" })
  1614. verboseScatterplot(x=numericMeta$Thin.SD,y=toplot[i,],xlab="Thin.SD",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Thin.SD"],2),", p=",signif(moduleTraitPvalue[i,"Thin.SD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Thin.SD"]<0.05) { "red" } else { "black" })
  1615. verboseScatterplot(x=numericMeta$Stubby.SD,y=toplot[i,],xlab="Stubby.SD",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Stubby.SD"],2),", p=",signif(moduleTraitPvalue[i,"Stubby.SD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Stubby.SD"]<0.05) { "red" } else { "black" })
  1616. verboseScatterplot(x=numericMeta$Mushroom.SD,y=toplot[i,],xlab="Mushroom.SD",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Mushroom.SD"],2),", p=",signif(moduleTraitPvalue[i,"Mushroom.SD"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Mushroom.SD"]<0.05) { "red" } else { "black" })
  1617. verboseScatterplot(x=numericMeta$Filopodia.D,y=toplot[i,],xlab="Filopodia.D",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Filopodia.D"],2),", p=",signif(moduleTraitPvalue[i,"Filopodia.D"],2),"\n"),col.main=if(moduleTraitPvalue[i,"Filopodia.D"]<0.05) { "red" } else { "black" })
  1618. verboseScatterplot(x=numericMeta$Thin.spine..,y=toplot[i,],xlab="Thin.spine..",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Thin.spine.."],2),", p=",signif(moduleTraitPvalue[i,"Thin.spine.."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Thin.spine.."]<0.05) { "red" } else { "black" })
  1619. verboseScatterplot(x=numericMeta$Stubby.spine..,y=toplot[i,],xlab="Stubby.spine..",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Stubby.spine.."],2),", p=",signif(moduleTraitPvalue[i,"Stubby.spine.."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Stubby.spine.."]<0.05) { "red" } else { "black" })
  1620. verboseScatterplot(x=numericMeta$Mushroom.spine..,y=toplot[i,],xlab="Mushroom.spine..",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Mushroom.spine.."],2),", p=",signif(moduleTraitPvalue[i,"Mushroom.spine.."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Mushroom.spine.."]<0.05) { "red" } else { "black" })
  1621. verboseScatterplot(x=numericMeta$Filopodia.spine..,y=toplot[i,],xlab="Filopodia.spine..",ylab="Eigenprotein",abline=TRUE,cex.axis=1,cex.lab=1,cex=1,col="black",bg=colnames(MEs)[i],pch=21,main=paste0("bicor=",signif(moduleTraitCor[i,"Filopodia.spine.."],2),", p=",signif(moduleTraitPvalue[i,"Filopodia.spine.."],2),"\n"),col.main=if(moduleTraitPvalue[i,"Filopodia.spine.."]<0.05) { "red" } else { "black" })
  1622. }
  1623. dev.off()
  1624. ########################################
  1625. #Write Module Membership/kME table
  1626. orderedModulesWithGrey=rbind(c("M0","grey"),orderedModules)
  1627. kMEtableSortVector<-apply( as.data.frame(cbind(SE2modColor,kMEdat)),1,function(x) if(!x[1]=="grey") { paste0(paste(orderedModulesWithGrey[match(x[1],orderedModulesWithGrey[,2]),],collapse=" "),"|",round(as.numeric(x[which(colnames(kMEdat)==paste0("kME",x[1]))+1]),4)) } else { paste0("grey|AllKmeAvg:",round(mean(as.numeric(x[-1],na.rm=TRUE)),4)) } )
  1628. kMEtable=cbind(c(1:nrow(cleanDatReg)),rownames(cleanDatReg),SE2modColor,kMEdat,kMEtableSortVector)[order(kMEtableSortVector,decreasing=TRUE),]
  1629. write.table(kMEtable,file=paste0(rootdir,"/02.17.26_ModuleAssignments-41Bulk_SE2.txt"),sep="\t",row.names=FALSE)
  1630. #(load above file in excel and apply green-yellow-red conditional formatting heatmap to the columns with kME values); then save as excel.
  1631. ## saved image of R session
  1632. save.image(paste0("02.01.26_saved.image.",projectFilesOutputTag,".Rdata")) #overwrites
  1633. #######WRAPPER CALL GO-ELITE GENE ONTOLOGIES
  1634. #######***NOTE FROM EVAN LIU - I HAD TO EDIT LINE 350 TO MAKE THE LIST COLOR FROM SPEAKEASY2 AND NOT WGCNA
  1635. ######################## EDIT THESE VARIABLES (USER PARAMETERS SET IN GLOBAL ENVIRONMENT) ############################################
  1636. colnames(kMEtable)[3] <- "net.colors"
  1637. write.csv(kMEtable, file=paste0(rootdir,"/ModuleAssignmentsForGOElite.csv"),row.names=FALSE)
  1638. NETcolors= SE2modColor #module color assignments, vector of length equal to number of rows in cleanDat; should have all colors for modules from 1:minimumSizeRank as printed by WGCNA::labels2colors(1:nModules)
  1639. nModules <- 9
  1640. #inputFile <- "ENDO_MG_TWO_WAY_LIST_NTS_v02b_forGOelite.csv" #Sample File 1 - has full human background
  1641. inputFile <- "ModuleAssignmentsForGOElite.csv" #Sample File 2 - WGCNA kME table for (Dai, et al, 2019)
  1642. #INPUT CSV FILE - in the filePath folder.
  1643. #Can be formatted as Kme table from WGCNA pipeline, or
  1644. #can be a CSV of columns, one symbol or UniqueID (Symbol|...) list per column, with the LIST NAMEs in row 1
  1645. #in this case, the longest list is used as background or the "universe" for the FET contingencies
  1646. # For simple columnwise list input, DON'T FORGET TO PUT THE APPROPRIATE BACKGROUND LIST IN, OR RESULTS WILL BE UNRELIABLE.
  1647. # filePath <- "Z:/Evan/Emory 41 Proteomics/Emory41/GOElite" #gsub("//","/",outputfigs)
  1648. # #Folder that (may) contain the input file specified above, and which will contain the outFilename project Folder.
  1649. outFilename <- "SE2-GO"
  1650. #SUBFOLDER WITH THIS NAME WILL BE CREATED, and .PDF + .csv file using the same name will be created within this folder.
  1651. outputGOeliteInputs=FALSE
  1652. #If TRUE, GO Elite background file and module or list-specific input files will be created in the outFilename subfolder.
  1653. maxBarsPerOntology=5
  1654. #Ontologies per ontology type, used for generating the PDF report; does not limit tabled output
  1655. #GMTdatabaseFile="Z:/Evan/ROSMAP_Synaptosome_Proteome_2024/GOElite/Human_GO_AllPathways_noPFOCR_with_GO_iea_September_16_2024_symbol.gmt" # e.g. "Human_GO_AllPathways_with_GO_iea_June_01_2022_symbol.gmt"
  1656. # Current month release will be downloaded if file does not exist.
  1657. # **Specify a nonexistent file to always download the current database to this folder.**
  1658. # Database .GMT file will be saved to the specified folder with its original date-specific name.
  1659. #path/to/filename of ontology database for the appropriate species (no conversion is performed)
  1660. #BaderLab website links to their current monthly update of ontologies for Human, Mouse, and Rat, minimally
  1661. #http://download.baderlab.org/EM_Genesets/current_release/
  1662. #For more information, see documentation: http://baderlab.org/GeneSets
  1663. panelDimensions=c(3,2) #dimensions of the individual parblots within a page of the main barplot PDF output
  1664. pageDimensions=c(8.5,11) #main barplot PDF output page dimensions, in inches
  1665. color=c("darkseagreen3","lightsteelblue1","lightpink4","goldenrod","darkorange","gold")
  1666. #scale_fill_manual(values = c("#3FB8AF", "#7FC7AF", "#DAD8A7","#FF9E9D", "#FF3D7F"))
  1667. #color=c("#009392FF","#F1EAC8FF","#D0587EFF")
  1668. #color=c("#009392FF","#F1EAC8FF","#E5B9ADFF","#D98994FF","#D0587EFF")
  1669. #colors respectively for ontology Types:
  1670. #"Biological Process","Molecular Function","Cellular Component","Reactome","WikiPathways","MSig.C2"
  1671. #must be valid R colors
  1672. modulesInMemory=TRUE
  1673. #uses cleanDat, net, and kMEdat from WGCNA systems biology pipeline already run, and these variables must be in memory
  1674. #inputFile will be ignored
  1675. ANOVAgroups=FALSE
  1676. #if true, modulesInMemory ignored. Volcano pipeline code should already have been run!
  1677. #inputFile will be ignored
  1678. ############ MUST HAVE AT LEAST 2 THREADS ENABLED TO RUN ############################################################################
  1679. parallelThreads=20
  1680. removeRedundantGOterms=TRUE
  1681. #if true, the 3 GO ontology types are collapased into a minimal set of less redundant terms using the below OBO file
  1682. GO.OBOfile<-"C:/Users/ehobby/Documents/SE2_EH/GOelite/go.obo"
  1683. #only used and needed if above flag to remove redundant GO terms is TRUE.
  1684. #Download from http://current.geneontology.org/ontology/go.obo will commence into the specified folder if the specified filename does not exist.
  1685. #Does not appear to be species specific, stores all GO term relations and is periodically updated.
  1686. cocluster=TRUE
  1687. #If TRUE, output PDF of signed Zscore coclustering on GO:cellular component terms (useful for WGCNA modules)
  1688. ######################## END OF PARAMETER VARIABLES ###################################################################################
  1689. library(piano)
  1690. source("GOparallel-FET.R")
  1691. GOparallel() # parameters are set in global environment as above; if not set, the function falls back to defaults and looks for all inputs available.
  1692. # priority is given to modulesInMemory

eNeuro_paper_code_wgcna_SE2.R at commit 8eaada4, under MIT · at the source

Overview

Authors: Emma L. Hobby1, Audrey J. Weber1, Evan Liu1, Cheyenne Hurst2, Kelsey M. Greathouse1, Chris Gaiteri3, Nicholas T. Seyfried2, Jeremy H. Herskowitz1
  1. Department of Neurology, Killion Center for Neurodegeneration and Experimental Therapeutics, University of Alabama at Birmingham, Birmingham, Alabama 35294
  2. Department of Biochemistry, Emory University School of Medicine, Atlanta, Georgia 30322
  3. Department of Psychiatry, SUNY Upstate Medical University, Syracuse, New York 13210
Institutions: University of Alabama at Birmingham (United States); Emory University (United States); SUNY Upstate Medical University (United States); UAB Medicine
Journal: eNeuro, volume 13, issue 4, pages ENEURO.0468-25.2026
Dates: received 17 December 2025; accepted 6 March 2026; published online 10 April 2026; in print April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1523/eneuro.0468-25.2026 · PMID 41922169 · PMCID PMC13095402 · OpenAlex W7147271794
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: genetics / omics (modality), structural MRI / diffusion (modality), histology / microscopy (modality), human (organism), Alzheimer's / dementia (population)
Methods: Statistics, Machine learning, Preprocessing, Connectivity, Complexity, fMRI & imaging
Keywords: Alzheimer's disease, dendritic spines, human neuroscience, network analysis, proteomics, synapse
MeSH: Alzheimer Disease*, Dendritic Spines*, Prefrontal Cortex*, Aged, Aged, 80 and over, Female, Humans, Male, Proteomics (* major topic)
Journal subjects: Research Article: New Research, Disorders of the Nervous System
Topic: Alzheimer's disease research and treatments (Physiology, Medicine), according to OpenAlex
Funding: NIH (AG083305, NS095775, AG085379, AG086401, AG061800, AG061798, AG057911)
Citations: not cited yet (Europe PMC); 63 references in the paper

Abstract

Proteomic studies have generated robust assessments of protein abundance changes in Alzheimer's disease (AD); however, identifying how the protein abundance changes affect specific biological processes remains a challenge. To address these hurdles, we used a multi-network computational analysis approach that integrated dendritic spine morphometry data with mass spectrometry-based proteomics from the same individuals. The samples exhibited a range of AD neuropathology and were categorized into three groups: controls, asymptomatic AD, and AD cases. Multiplex tandem mass tag mass spectrometry proteomic data (N = 8,212 proteins) was generated on Brodmann area 46 (BA46) dorsolateral prefrontal cortex (DLPFC) human samples (N = 41, 23 males and 18 females), from which dendritic spine morphometry analysis existed. To integrate the multi-scale data types, two computational network analysis methods were performed, including weighted coexpression network analysis (WGCNA) and SpeakEasy2 (SE2). Both WGCNA and SE2 revealed that the mitochondria protein modules were decreased in AsymAD and AD cases compared with controls, whereas the DNA repair modules were increased in AsymAD and AD compared with controls. Synaptic protein modules that correlated to multiple spine morphology traits were identified in both WGCNA and SE2. Pearson’s correlation analyses identified over a dozen individual proteins linked to multiple dendritic spine density and morphology traits. Collectively, these findings demonstrate how integration of spine morphometry data with proteomics can contextualize proteins for functional validation and identify synaptic alterations in AD progression.

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.

ehobby33/eNeuro-repo

License: MIT
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 8eaada4efebb0bf0a0bd810296773912ad1bd73a, 23 February 2026
Languages: R (1)
Size: 3 files, 1 script
Software Heritage: not archived
Found in: “Code accessibility”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ggplot2 (1 file), ggpubr (1 file), igraph (1 file), limma (1 file), Plotly (1 file), reshape2 (1 file), tidyverse (1 file), WGCNA (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
3 files

Code accessibility

The code described in the paper is freely available online at https://github.com/ehobby33/eNeuro-repo.git. The code is available as Extended Data 1 (https://doi.org/10.1523/ENEURO.0468-25.2026.d1).

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

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.

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, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 8 authors, 6 keywords, 9 MeSH terms, 1 funder, 63 references.

Cite

This paper

Hobby, E. L., Weber, A. J., Liu, E., Hurst, C., Greathouse, K. M., Gaiteri, C., Seyfried, N. T., & Herskowitz, J. H. (2026). A Multi-Network Approach Identifies Proteins Related to Dendritic Spines in Alzheimer's Disease. eNeuro, 13(4), ENEURO.0468-25.2026. https://doi.org/10.1523/eneuro.0468-25.2026

BibTeX

@article{hobby2026multi,
author = {Hobby, Emma L. and Weber, Audrey J. and Liu, Evan and Hurst, Cheyenne and Greathouse, Kelsey M. and Gaiteri, Chris and Seyfried, Nicholas T. and Herskowitz, Jeremy H.},
title = {{A Multi-Network Approach Identifies Proteins Related to Dendritic Spines in Alzheimer's Disease}},
journal = {eNeuro},
year = {2026},
month = apr,
volume = {13},
number = {4},
pages = {ENEURO.0468--25.2026},
publisher = {Society for Neuroscience},
issn = {2373-2822},
doi = {10.1523/eneuro.0468-25.2026},
url = {https://doi.org/10.1523/eneuro.0468-25.2026},
pmid = {41922169},
pmcid = {PMC13095402}
}

RIS

TY - JOUR
AU - Hobby, Emma L.
AU - Weber, Audrey J.
AU - Liu, Evan
AU - Hurst, Cheyenne
AU - Greathouse, Kelsey M.
AU - Gaiteri, Chris
AU - Seyfried, Nicholas T.
AU - Herskowitz, Jeremy H.
TI - A Multi-Network Approach Identifies Proteins Related to Dendritic Spines in Alzheimer's Disease
T2 - eNeuro
J2 - eNeuro
PY - 2026
DA - 2026/04/20
VL - 13
IS - 4
SP - ENEURO.0468
EP - 25.2026
SN - 2373-2822
PB - Society for Neuroscience
DO - 10.1523/eneuro.0468-25.2026
UR - https://doi.org/10.1523/eneuro.0468-25.2026
LA - en
ER -

CSL-JSON

{
"id": "10.1523/eneuro.0468-25.2026",
"type": "article-journal",
"title": "A Multi-Network Approach Identifies Proteins Related to Dendritic Spines in Alzheimer's Disease",
"container-title": "eNeuro",
"author": [
{
"family": "Hobby",
"given": "Emma L."
},
{
"family": "Weber",
"given": "Audrey J."
},
{
"family": "Liu",
"given": "Evan"
},
{
"family": "Hurst",
"given": "Cheyenne"
},
{
"family": "Greathouse",
"given": "Kelsey M."
},
{
"family": "Gaiteri",
"given": "Chris"
},
{
"family": "Seyfried",
"given": "Nicholas T."
},
{
"family": "Herskowitz",
"given": "Jeremy H."
}
],
"container-title-short": "eNeuro",
"volume": "13",
"issue": "4",
"page": "ENEURO.0468-25.2026",
"DOI": "10.1523/eneuro.0468-25.2026",
"PMID": "41922169",
"PMCID": "PMC13095402",
"ISSN": "2373-2822",
"publisher": "Society for Neuroscience",
"URL": "https://doi.org/10.1523/eneuro.0468-25.2026",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
20
]
]
}
}

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.1016/j.celrep.2026.117235 [code]
Integration of aged brain multi-omics reveals cross-system mechanisms underlying Alzheimer's disease heterogeneity.
Journal: Cell reports
In common: reshape2, ggpubr, ggplot2, 1 other tool, Alzheimer's / dementia, genetics / omics, 7 references
[2] doi:10.1038/s41398-026-04200-5 [code]
Postmortem brain single-nucleus and bulk gene expression analyses identify shared and distinct abnormalities in bipolar disorder and major depressive disorder.
Journal: Translational psychiatry
In common: WGCNA, limma, igraph, 4 other tools, histology / microscopy, genetics / omics, 1 reference
[3] 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: WGCNA, limma, igraph, 5 other tools, genetics / omics
[4] 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: WGCNA, limma, igraph, 5 other tools, genetics / omics
[5] doi:10.1038/s41380-026-03585-5 [code]
Multiomics analysis identifies VPA-induced changes in neural progenitor cells, ventricular-like regions, and cellular microenvironment in dorsal forebrain organoids.
Journal: Molecular psychiatry
In common: WGCNA, limma, igraph, 4 other tools, genetics / omics, 1 reference
[6] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: WGCNA, limma, igraph, 5 other tools
[7] doi:10.1038/s41380-026-03629-w [code]
Maternal fasting during early gestation induces epigenetic alterations and schizophrenia-related phenotypes.
Journal: Molecular psychiatry
In common: WGCNA, limma, igraph, 4 other tools, genetics / omics, 1 reference
[8] doi:10.1016/j.isci.2026.115196 [code]
Transcriptional and cellular maturation of the chick spinal cord in the context of distinct neuromuscular circuits.
Journal: iScience
In common: WGCNA, limma, igraph, 5 other tools
[9] doi:10.1038/s41593-026-02367-0 [code]
A reproducible three-dimensional model of human brain tissue to investigate physiological and disease-associated microglia phenotypes.
Journal: Nature neuroscience
In common: WGCNA, limma, igraph, 4 other tools, Alzheimer's / dementia
[10] doi:10.1038/s44318-026-00806-z [code]
Interspecific diversity in the neuronal composition of the mammalian cortex arises from heterochrony in neurogenesis.
Journal: The EMBO journal
In common: WGCNA, igraph, Plotly, 4 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.