OSCR

Protein phosphatase 2A regulates senescence and immunogenicity in medulloblastoma models.

Code ↔ Paper

1 match 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 1 match
  1. [1] § Methods › In vitro experiments › RNA-Seq. ↔ R/pipeline_functions.R, lines 2929–3031 · score 0.51 · MSigDB, NetBID2, Pathway, regulatory, RNA, genes

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 · 3,700 lines · 195 KB · Apache-2.0 · 1 match

  1. #' @import Biobase limma tximport igraph biomaRt openxlsx msigdbr ConsensusClusterPlus kableExtra
  2. #' @importFrom GEOquery getGEO
  3. #' @importFrom RColorBrewer brewer.pal
  4. #' @importFrom plot3D scatter3D
  5. #' @importFrom plotrix draw.ellipse draw.circle
  6. #' @importFrom impute impute.knn
  7. #' @importFrom umap umap umap.defaults
  8. #' @importFrom rhdf5 H5Fopen H5Fclose
  9. #' @importFrom DESeq2 DESeqDataSetFromTximport DESeq
  10. #' @importFrom ComplexHeatmap Heatmap
  11. #' @importFrom graphics plot
  12. #' @importFrom aricode clustComp
  13. #' @importFrom GSVA gsva
  14. #' @importFrom MCMCglmm MCMCglmm
  15. #' @importFrom arm bayesglm
  16. #' @importFrom reshape melt
  17. #' @importFrom ordinal clm clmm
  18. #' @importFrom rmarkdown render pandoc_available html_document
  19. #' @importFrom Matrix rowSums
  20. #' @importFrom SummarizedExperiment assay
  21. #' @importFrom lme4 lmer
  22. #' @importFrom grDevices col2rgb colorRampPalette dev.off pdf rgb
  23. #' @importFrom graphics abline arrows axis barplot boxplot hist image layout legend lines mtext par points polygon rect segments strheight stripchart strwidth text
  24. #' @importFrom stats IQR aggregate as.dendrogram as.dist cutree density dist fisher.test gaussian glm hclust kmeans ks.test lm median model.matrix na.omit order.dendrogram p.adjust pchisq pnorm prcomp pt qnorm quantile sd splinefun
  25. #' @importFrom utils read.delim write.table
  26. ################################
  27. library(Biobase) ## basic functions for bioconductor
  28. library(GEOquery) ## for samples from GEO
  29. library(limma) ## for data normalization for micro-array
  30. library(DESeq2) ## for data normalization for RNASeq
  31. library(tximport) ## for data import from Salmon/sailfish/kallisto/rsem/stringtie output
  32. library(RColorBrewer) ## for color scale
  33. library(colorspace) ## for color scale
  34. library(plot3D) ## for 3D plot
  35. library(igraph) ## for network related functions
  36. library(plotrix) ## for draw.ellipse
  37. library(biomaRt) ## for gene id conversion
  38. library(openxlsx) ## for output into excel
  39. library(impute) ## for impute
  40. library(msigdbr) ## for msigDB gene sets
  41. library(ComplexHeatmap) ## for complex heatmap
  42. library(umap) ## for umap visualization
  43. library(rhdf5) ## for read in MICA results
  44. library(GSVA)
  45. library(MCMCglmm)
  46. library(arm)
  47. library(reshape)
  48. library(ordinal)
  49. library(rmarkdown)
  50. library(aricode)
  51. ##
  52. check_para <- function(para_name,envir){
  53. if(base::exists(para_name,envir=envir)==FALSE){message(sprintf('%s missing !',para_name));return(0)}
  54. if(is.null(base::get(para_name,envir=envir))==TRUE){message(sprintf('%s is NULL !',para_name));return(0)}
  55. return(1)
  56. }
  57. check_option <- function(para_name,option_list,envir){
  58. if(!base::get(para_name,envir=envir) %in% option_list){
  59. message(sprintf('Only accept %s set at: %s !',para_name,base::paste(option_list,collapse=';')));return(0)
  60. }
  61. return(1)
  62. }
  63. clean_charVector <- function(x){
  64. x1 <- names(x)
  65. x <- as.character(x);
  66. x[which(x=='')] <- 'NULL';
  67. x[which(is.null(x)==TRUE)] <- 'NULL'
  68. x[which(is.na(x)==TRUE)] <- 'NA'
  69. names(x) <- x1
  70. x
  71. }
  72. ##
  73. #
  74. #' Preload database files into R workspace for NetBID2
  75. #'
  76. #' \code{db.preload} is a pre-processing function for NetBID2. It preloads needed data into R workspace,
  77. #' and saves it locally under db/ directory with specified species name and analysis level.
  78. #'
  79. #' Users need to set the species name (e.g. human, mouse) and
  80. #' analysis level (transcript or gene level). TF list and SIG list are optional, if not specified, list from package data will be used as default.
  81. #'
  82. #' @param use_level character, users can choose "transcript" or "gene". Default is "gene".
  83. #' @param use_spe character, the name of an interested species (e.g. "human", "mouse", "rat"). Default is "human".
  84. #' @param update logical, if TRUE, previous loaded RData will be updated. Default is FALSE.
  85. #' @param TF_list a character vector, the list of TF (Transcription Factor) names. If NULL, the pre-defined list in the package will be used.
  86. #' Default is NULL.
  87. #' @param SIG_list a character vector, the list of SIG (Signaling Factor) names. If NULL, the pre-defined list in the package will be use.
  88. #' Default is NULL.
  89. #' @param input_attr_type character, the type of the TF_list and SIG_list.
  90. #' Details please check biomaRt, \url{https://bioconductor.org/packages/release/bioc/vignettes/biomaRt/inst/doc/biomaRt.html}.
  91. #' If TF_list and SIG_list are not specified, the list in the NetBID2 package will be used.
  92. #' This only support "external_gene_name" and "ensembl_gene_id".
  93. #' Default is "external_gene_name".
  94. #' @param main.dir character, the main directory for NetBID2.
  95. #' If NULL, will be \code{system.file(package = "NetBID2")}. Default is NULL.
  96. #' @param db.dir character, a path for saving the RData.
  97. #' Default is \code{db} directory under the \code{main.dir}, if \code{main.dir} is provided.
  98. #' @param useCache Boolean, parameter pass to \code{getBM()} indicating whether the results cache should be used. Setting to FALSE will disable reading and writing of the cache.
  99. #'
  100. #' @return Return TRUE if loading is successful, otherwise return FALSE. Two variables will be loaded into R workspace, \code{tf_sigs} and \code{db_info}.
  101. #' @examples
  102. #' db.preload(use_level='gene',use_spe='human',update=FALSE)
  103. #'
  104. #' \dontrun{
  105. #' db.preload(use_level='transcript',use_spe='human',update=FALSE)
  106. #' db.preload(use_level='gene',use_spe='mouse',update=FALSE)
  107. #' }
  108. #' @export
  109. db.preload <- function(use_level='transcript',use_spe='human',update = FALSE,
  110. TF_list=NULL,SIG_list=NULL,input_attr_type='external_gene_name',
  111. main.dir=NULL,
  112. db.dir=sprintf("%s/db/",main.dir),useCache = TRUE){
  113. all_input_para <- c('use_level','use_spe','update')
  114. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  115. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  116. check_res <- c(check_option('use_level',c('transcript','gene'),envir=environment()),
  117. check_option('update',c(TRUE,FALSE),envir=environment()))
  118. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  119. ## load annotation info, including: TF/Sig list, gene info
  120. if(is.null(main.dir)==TRUE){
  121. main.dir <- system.file(package = "NetBID2")
  122. message(sprintf('main.dir not set, will use package directory: %s',main.dir))
  123. }
  124. if(is.null(db.dir)==TRUE){
  125. db.dir <- sprintf("%s/db/",main.dir)
  126. }
  127. message(sprintf('Will use directory %s as the db.dir',db.dir))
  128. message(sprintf('Your setting for species is %s, with level at %s',use_spe,use_level))
  129. use_spe <- toupper(use_spe)
  130. output.db.dir <- sprintf('%s/%s',db.dir,use_spe)
  131. RData.file <- sprintf('%s/%s_%s.RData', output.db.dir,use_spe,use_level)
  132. if (!file.exists(RData.file) | update==TRUE) { ## not exist or need to update
  133. ## get info from use_spe
  134. ensembl <- biomaRt::useMart("ensembl")
  135. all_ds <- biomaRt::listDatasets(ensembl)
  136. w1 <- grep(sprintf("^%s GENES",use_spe),toupper(all_ds$description))
  137. if(base::length(w1)==0){
  138. tmp_use_spe <- unlist(strsplit(use_spe,' ')); tmp_use_spe <- tmp_use_spe[base::length(tmp_use_spe)]
  139. w1 <- grep(sprintf(".*%s_GENE_ENSEMBL",toupper(tmp_use_spe)),toupper(all_ds$dataset))
  140. if(base::length(w1)==1){use_spe <- toupper(strsplit(all_ds[w1,2],' ')[[1]][1]); output.db.dir <- sprintf('%s/%s',db.dir,use_spe)}
  141. }
  142. if(base::length(w1)==1){
  143. w2 <- all_ds[w1,1]
  144. mart <- biomaRt::useMart(biomart="ensembl", dataset=w2) ## get id for input spe
  145. message(sprintf('Read in ensembl annotation file for %s and output all db files in %s/%s !',use_spe,db.dir,use_spe))
  146. }
  147. if(base::length(w1)==0){
  148. message(sprintf('Check input use_spe parameter: %s, not included in the ensembl database',use_spe))
  149. return(FALSE)
  150. }
  151. if(base::length(w1)>1){
  152. w2 <- base::paste(all_ds[w1,2],collapse=';')
  153. message(sprintf('Check input use_spe parameter: %s, more than one species match in ensembl database : %s,
  154. please check and re-try',use_spe,w2))
  155. return(FALSE)
  156. }
  157. }
  158. RData.file <- sprintf('%s/%s_%s.RData', output.db.dir,use_spe,use_level)
  159. if (update == TRUE | !file.exists(RData.file)) {
  160. if(!file.exists(output.db.dir)){
  161. dir.create(output.db.dir)
  162. }
  163. ## get attributes for mart
  164. filters <- biomaRt::listFilters(mart)
  165. attributes <- biomaRt::listAttributes(mart)
  166. ensembl.attr.transcript <- c('ensembl_transcript_id','ensembl_gene_id',
  167. 'external_transcript_name','external_gene_name',
  168. 'gene_biotype','gene_biotype',
  169. 'chromosome_name','strand','start_position','end_position','band','transcript_start','transcript_end',
  170. 'description','phenotype_description','refseq_mrna')
  171. ensembl.attr.gene <- c('ensembl_gene_id','external_gene_name',
  172. 'gene_biotype',
  173. 'chromosome_name','strand','start_position','end_position','band',
  174. 'description','phenotype_description','refseq_mrna')
  175. if(use_spe=='HUMAN'){
  176. ensembl.attr.transcript <- c(ensembl.attr.transcript,'hgnc_symbol','entrezgene_id')
  177. ensembl.attr.gene <- c(ensembl.attr.gene,'hgnc_symbol','entrezgene_id')
  178. }
  179. ## do not output hgnc in non-human species
  180. if(use_spe != 'HUMAN')
  181. ensembl.attr.transcript <- base::setdiff(ensembl.attr.transcript,'hgnc_symbol')
  182. if(use_spe != 'HUMAN')
  183. ensembl.attr.gene <- base::setdiff(ensembl.attr.gene,'hgnc_symbol')
  184. ## if too much: Query ERROR: caught BioMart::Exception::Usage: Too many attributes selected for External References
  185. # judge input type if TF_list, Sig_list not equal to NULL
  186. if(is.null(TF_list)==FALSE | is.null(SIG_list)==FALSE){
  187. if(!input_attr_type %in% filters$name){
  188. message(sprintf('%s not in the filter name, please retry !',input_attr_type));return(FALSE)
  189. }
  190. if(!input_attr_type %in% ensembl.attr.transcript)
  191. ensembl.attr.transcript <- c(ensembl.attr.transcript,input_attr_type)
  192. if(!input_attr_type %in% ensembl.attr.gene)
  193. ensembl.attr.gene <- c(ensembl.attr.gene,input_attr_type)
  194. }
  195. ## get TF/SIG list and output to output.db.dir, if not defined by user, will use in db/ (human)
  196. # for TF list
  197. filter_attr <- input_attr_type
  198. if(is.null(TF_list)){ ## use TF.txt in db/
  199. if(use_spe != 'HUMAN' & use_spe != 'MOUSE'){ ## if spe not human/mouse
  200. TF_f <- sprintf('%s/%s_TF_%s.txt',db.dir,'HUMAN',filter_attr)
  201. message(sprintf('Will use %s file as the input TF_list!',TF_f))
  202. TF_list <- read.delim(TF_f,stringsAsFactors=FALSE,header=F)$V1
  203. filter_attr <- 'external_gene_name'
  204. tmp1 <- biomaRt::getBM(attributes=c('hsapiens_homolog_associated_gene_name','external_gene_name'),values=TRUE,mart=mart,filters='with_hsapiens_homolog',useCache = useCache)
  205. TF_list <- base::unique(tmp1[which(tmp1[,1] %in% TF_list),2])
  206. }else{
  207. TF_f <- sprintf('%s/%s_TF_%s.txt',db.dir,use_spe,filter_attr)
  208. message(sprintf('Will use %s file as the input TF_list!',TF_f))
  209. TF_list <- read.delim(TF_f,stringsAsFactors=FALSE,header=F)$V1
  210. }
  211. }
  212. if(use_level=='transcript'){
  213. message(sprintf('Begin read TF list information from ensembl for %s !',use_spe))
  214. TF_info <- biomaRt::getBM(attributes = ensembl.attr.transcript,values=TF_list, mart=mart, filters=filter_attr,useCache = useCache)
  215. }
  216. if(use_level=='gene'){
  217. message(sprintf('Begin read TF list information from ensembl for %s !',use_spe))
  218. TF_info <- biomaRt::getBM(attributes = ensembl.attr.gene,values=TF_list, mart=mart, filters=filter_attr,useCache = useCache)
  219. }
  220. # for SIG list
  221. if(is.null(SIG_list)){
  222. if(use_spe != 'HUMAN' & use_spe != 'MOUSE'){ ## if spe not human/mouse
  223. SIG_f <- sprintf('%s/%s_SIG_%s.txt',db.dir,'HUMAN',filter_attr)
  224. message(sprintf('Will use %s file as the input SIG_list!',SIG_f))
  225. SIG_list <- read.delim(SIG_f,stringsAsFactors=FALSE,header=F)$V1
  226. filter_attr <- 'external_gene_name'
  227. tmp1 <- biomaRt::getBM(attributes=c('hsapiens_homolog_associated_gene_name','external_gene_name'),values=TRUE,mart=mart,filters='with_hsapiens_homolog',useCache = useCache)
  228. SIG_list <- base::unique(tmp1[which(tmp1[,1] %in% SIG_list),2])
  229. }else{
  230. SIG_f <- sprintf('%s/%s_SIG_%s.txt',db.dir,use_spe,filter_attr)
  231. message(sprintf('Will use %s file as the input SIG_list!',SIG_f))
  232. SIG_list <- read.delim(SIG_f,stringsAsFactors=FALSE,header=F)$V1
  233. }
  234. }
  235. if(use_level=='transcript'){
  236. message(sprintf('Begin read SIG list information from ensembl for %s !',use_spe))
  237. SIG_info <- biomaRt::getBM(attributes = ensembl.attr.transcript,values=SIG_list, mart=mart, filters=filter_attr,useCache = useCache)
  238. }
  239. if(use_level=='gene'){
  240. message(sprintf('Begin read SIG list information from ensembl for %s !',use_spe))
  241. SIG_info <- biomaRt::getBM(attributes = ensembl.attr.gene,values=SIG_list, mart=mart, filters=filter_attr,useCache = useCache)
  242. }
  243. # check input not in the list
  244. miss_TF <- base::unique(base::setdiff(TF_list,TF_info[[filter_attr]]))
  245. miss_SIG <- base::unique(base::setdiff(SIG_list,SIG_info[[filter_attr]]))
  246. if(base::length(miss_TF)>0){message(sprintf("%d TFs could not match,please check and choose to re-try : %s",
  247. base::length(miss_TF),base::paste(sort(miss_TF),collapse=';')))}
  248. if(base::length(miss_SIG)>0){message(sprintf("%d SIGs could not match,please check and choose to re-try : %s",
  249. base::length(miss_SIG),base::paste(sort(miss_SIG),collapse=';')))}
  250. ####### output full info
  251. tf_sigs <- list();tf_sigs$tf <- list();tf_sigs$sig <- list();
  252. tf_sigs$tf$info <- TF_info; tf_sigs$sig$info <- SIG_info;
  253. for(each_id_type in base::intersect(c('ensembl_transcript_id','ensembl_gene_id',
  254. 'external_transcript_name','external_gene_name','hgnc_symbol',
  255. 'entrezgene_id','refseq_mrna'),colnames(TF_info))){
  256. tf_sigs$tf[[each_id_type]] <- base::setdiff(base::unique(TF_info[[each_id_type]]),"")
  257. tf_sigs$sig[[each_id_type]] <- base::setdiff(base::unique(SIG_info[[each_id_type]]),"")
  258. }
  259. db_info <- all_ds[w1,]
  260. save(tf_sigs,db_info=db_info,file = RData.file)
  261. }
  262. load(RData.file,.GlobalEnv)
  263. return(TRUE)
  264. }
  265. #' Get Transcription Factor (TF) and Signaling Factor (SIG) List
  266. #'
  267. #' \code{get.TF_SIG.list} is a function converts gene ID into the corresponding TF/SIG list,
  268. #' with selected gene/transcript type.
  269. #'
  270. #' @param use_genes a vector of characters, genes will be used in the network construction.
  271. #' If NULL, no filter will be performed to the TF/SIG list. Default is NULL.
  272. #' @param use_gene_type character, the attribute name inherited from the biomaRt package.
  273. #' Some options are, "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" and "refseq_mrna".
  274. #' All options can be accessed by calling \code{biomaRt::useMart} (e.g. mart <- biomaRt::useMart('ensembl',db_info[1]); biomaRt::listAttributes(mart)$name).
  275. #'
  276. #' The type must match the gene type from the input \code{use_genes}. Default is "external_gene_name".
  277. #' @param ignore_version logical, if TRUE, the version "ensembl_gene_id_version" or "ensembl_transcript_id_version" will be ignored.
  278. #' Default is FALSE.
  279. #' @param dataset character, the dataset used for ID conversion (e.g. "hsapiens_gene_ensembl").
  280. #' If NULL, use \code{db_info[1]} from \code{db.preload}. Default is NULL.
  281. #'
  282. #'
  283. #' @return Return a list containing two elements. \code{tf} is the TF list, \code{sig} is the SIG list.
  284. #'
  285. #' @examples
  286. #' db.preload(use_level='transcript',use_spe='human',update=FALSE)
  287. #' use_genes <- c("ENST00000210187","ENST00000216083","ENST00000216127",
  288. #' "ENST00000216416","ENST00000217233","ENST00000221418",
  289. #' "ENST00000504956","ENST00000507468")
  290. #' res_list <- get.TF_SIG.list(use_gene_type = 'ensembl_transcript_id',
  291. #' use_genes=use_genes,
  292. #' dataset='hsapiens_gene_ensembl')
  293. #' print(res_list)
  294. #'
  295. #' \dontrun{
  296. #' }
  297. #'
  298. #' @export
  299. get.TF_SIG.list <- function(use_genes=NULL,
  300. use_gene_type='external_gene_name',ignore_version=FALSE,
  301. dataset=NULL){
  302. #
  303. all_input_para <- c('use_genes')
  304. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  305. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  306. check_res <- c(check_option('ignore_version',c(TRUE,FALSE),envir=environment()))
  307. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  308. #
  309. if(is.null(dataset)==TRUE){
  310. check_res <- check_para('db_info',envir=environment())
  311. if(base::min(check_res)==0){message('Please use db.preload() to get db_info or set dataset, check and re-try!');return(FALSE)}
  312. dataset <- db_info[1]
  313. }
  314. if(is.null(tf_sigs)==TRUE){
  315. message('tf_sigs not loaded yet, please run db.preload() before processing !');return(FALSE);
  316. }
  317. n1 <- names(tf_sigs$tf)[-1]
  318. if(use_gene_type %in% n1){
  319. if(is.null(use_genes)==TRUE){
  320. TF_list <- base::unique(tf_sigs$tf[[use_gene_type]])
  321. SIG_list <- base::unique(tf_sigs$sig[[use_gene_type]])
  322. }else{
  323. TF_list <- base::unique(base::intersect(use_genes,tf_sigs$tf[[use_gene_type]]))
  324. SIG_list <- base::unique(base::intersect(use_genes,tf_sigs$sig[[use_gene_type]]))
  325. }
  326. }else{
  327. if(grepl('version$',use_gene_type)==TRUE & ignore_version==TRUE){
  328. use_genes_no_v <- gsub('(.*)\\..*','\\1',use_genes)
  329. transfer_tab <- data.frame(to_type=use_genes,from_type=use_genes_no_v,stringsAsFactors = FALSE)
  330. print(str(transfer_tab))
  331. TF_list <- transfer_tab[which(transfer_tab$from_type %in% tf_sigs$tf[[gsub('(.*)_version','\\1',use_gene_type)]]),'to_type']
  332. SIG_list <- transfer_tab[which(transfer_tab$from_type %in% tf_sigs$sig[[gsub('(.*)_version','\\1',use_gene_type)]]),'to_type']
  333. }else{
  334. mart <- biomaRt::useMart(biomart="ensembl", dataset=dataset) ## get mart for id conversion !!!! db_info is saved in db RData
  335. #filters <- biomaRt::listFilters(mart)
  336. attributes <- biomaRt::listAttributes(mart)
  337. if(!use_gene_type %in% attributes$name){
  338. message(sprintf('%s not in the attributes for %s, please check and re-try !',use_gene_type,dataset));return(FALSE)
  339. }
  340. transfer_tab <- get_IDtransfer(from_type=use_gene_type,to_type=n1[1],ignore_version = ignore_version)
  341. print(str(transfer_tab))
  342. TF_list <- get_name_transfertab(use_genes=tf_sigs$tf[n1[1]][[1]],transfer_tab = transfer_tab,ignore_version = ignore_version,ignore_order=TRUE,from_type=n1[1],to_type=use_gene_type)
  343. SIG_list <- get_name_transfertab(use_genes=tf_sigs$sig[n1[1]][[1]],transfer_tab = transfer_tab,ignore_version = ignore_version,ignore_order=TRUE,from_type=n1[1],to_type=use_gene_type)
  344. }
  345. TF_list <- base::unique(base::intersect(use_genes,TF_list))
  346. SIG_list <- base::unique(base::intersect(use_genes,SIG_list))
  347. }
  348. message(sprintf('%d TFs and %s SIGs are included in the expression matrix !',base::length(TF_list),base::length(SIG_list)))
  349. return(list(tf=TF_list,sig=SIG_list))
  350. }
  351. #' Creates Data Frame for ID Conversion
  352. #'
  353. #' \code{get_IDtransfer} creates a data frame for ID conversion using biomaRt. For example, to convert Ensembl ID into gene symbol.
  354. #'
  355. #' @param from_type character, the attribute name match the current ID type (the type of \code{use_genes}).
  356. #' Such as "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" or "refseq_mrna".
  357. #' The "attribute" is inherited from the biomaRt package. For details, user can call \code{biomaRt::listAttributes()} to display all available attributes in the selected dataset.
  358. #' @param to_type character, the attribute name to convert into.
  359. #' @param add_type character, the additional attribute name to add into the conversion data frame.
  360. #' @param use_genes a vector of characters, the genes for ID conversion.
  361. #' If NULL, all genes will be selected.
  362. #' @param dataset character, name of the dataset used for ID conversion. For example, "hsapiens_gene_ensembl".
  363. #' If NULL, \code{db_info[1]} will be used. \code{db_info} requires the calling of \code{db.preload} in the previous steps.
  364. #' Default is NULL.
  365. #' @param ignore_version logical, if it is set to TRUE and \code{from_type} is "ensembl_gene_id_version" or "ensembl_transcript_id_version",
  366. #' the version of the original ID will be ignored in ID mapping.
  367. #' Default is FALSE.
  368. #' @param useCache Boolean, parameter pass to \code{getBM()} indicating whether the results cache should be used. Setting to FALSE will disable reading and writing of the cache.
  369. #'
  370. #' @return
  371. #' Return a data frame for ID conversion.
  372. #'
  373. #' @examples
  374. #' use_genes <- c("ENST00000210187","ENST00000216083","ENST00000216127",
  375. #' "ENST00000216416","ENST00000217233","ENST00000221418")
  376. #' transfer_tab <- get_IDtransfer(from_type = 'ensembl_transcript_id',
  377. #' to_type='external_gene_name',
  378. #' use_genes=use_genes,
  379. #' dataset='hsapiens_gene_ensembl')
  380. #' ## get transfer table !!!
  381. #' res1 <- get_name_transfertab(use_genes,transfer_tab=transfer_tab)
  382. #' transfer_tab_withtype <- get_IDtransfer2symbol2type(from_type = 'ensembl_transcript_id',
  383. #' use_genes=use_genes,
  384. #' dataset='hsapiens_gene_ensembl')
  385. #' ## get transfer table !!!
  386. #' \dontrun{
  387. #' }
  388. #' @export
  389. get_IDtransfer <- function(from_type=NULL,to_type=NULL,add_type=NULL,use_genes=NULL,dataset=NULL,ignore_version=FALSE,useCache = TRUE){
  390. #
  391. all_input_para <- c('from_type','to_type')
  392. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  393. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  394. check_res <- c(check_option('ignore_version',c(TRUE,FALSE),envir=environment()))
  395. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  396. #
  397. if(is.null(dataset)==TRUE){
  398. check_res <- check_para('db_info',envir=environment())
  399. if(base::min(check_res)==0){message('Please use db.preload() to get db_info or set dataset, check and re-try!');return(FALSE)}
  400. dataset <- db_info[1]
  401. }
  402. mart <- biomaRt::useMart(biomart="ensembl", dataset=dataset) ## get mart for id conversion !!!! db_info is saved in db RData
  403. attributes <- biomaRt::listAttributes(mart)
  404. if(!from_type %in% attributes$name){
  405. message(sprintf('%s not in the attributes for %s, please check and re-try !',from_type,dataset));return(FALSE)
  406. }
  407. if(!to_type %in% attributes$name){
  408. message(sprintf('%s not in the attributes for %s, please check and re-try !',to_type,dataset));return(FALSE)
  409. }
  410. ori_from_type <- from_type
  411. ori_use_genes <- use_genes
  412. if(from_type %in% c('ensembl_gene_id_version','ensembl_transcript_id_version')){
  413. if(ignore_version==FALSE){
  414. message(sprintf('Attention: %s in %s will be updated with new version number, please check the output.
  415. If lots of missing, try to set ignore_version=TRUE and try again !',from_type,dataset));
  416. }else{
  417. from_type <- gsub('(.*)_version','\\1',from_type)
  418. if(is.null(use_genes)==FALSE) use_genes <- gsub('(.*)\\..*','\\1',use_genes)
  419. }
  420. }
  421. if(is.null(use_genes)==TRUE | base::length(use_genes)>100){
  422. tmp1 <- biomaRt::getBM(attributes=c(from_type,to_type,add_type),values=1,mart=mart,filters='strand',useCache = useCache)
  423. tmp2 <- biomaRt::getBM(attributes=c(from_type,to_type,add_type),values=-1,mart=mart,filters='strand',useCache = useCache)
  424. tmp1 <- base::rbind(tmp1,tmp2)
  425. if(is.null(use_genes)==FALSE){
  426. tmp1 <- tmp1[which(tmp1[,1] %in% use_genes),]
  427. }
  428. }else{
  429. tmp1 <- biomaRt::getBM(attributes=c(from_type,to_type,add_type),values=use_genes,mart=mart,filters=from_type,useCache = useCache)
  430. }
  431. if(ori_from_type %in% c('ensembl_gene_id_version','ensembl_transcript_id_version') & is.null(use_genes)==FALSE & ignore_version==TRUE){
  432. tmp2 <- data.frame(ori_from_type=ori_use_genes,from_type=use_genes,stringsAsFactors=FALSE)
  433. names(tmp2) <- c(ori_from_type,from_type)
  434. tmp1 <- base::merge(tmp2,tmp1,by.y=from_type,by.x=from_type)[c(from_type,to_type,add_type,ori_from_type)]
  435. }
  436. w1 <- apply(tmp1,1,function(x)base::length(which(is.na(x)==TRUE | x=="")))
  437. transfer_tab <- tmp1[which(w1==0),]
  438. for(i in 1:ncol(transfer_tab)){
  439. transfer_tab[,i] <- as.character(transfer_tab[,i])
  440. }
  441. return(transfer_tab)
  442. }
  443. #' Create Data Frame for ID Conversion Between Species
  444. #'
  445. #' \code{get_IDtransfer_betweenSpecies} creates a data frame to convert ID between species.
  446. #'
  447. #' @param from_spe character, name of the original species (e.g. "human", "mouse", "rat") that \code{use_genes} belongs to. Default is "human".
  448. #' @param to_spe character, name of the target species (e.g. "human", "mouse", "rat"). Default is "mouse".
  449. #' @param from_type character, the attribute name match the current ID type (the type of \code{use_genes}).
  450. #' Such as "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" or "refseq_mrna".
  451. #' The "attribute" is inherited from the \code{biomaRt} package. For details, user can call \code{biomaRt::listAttributes()} function to display all available attributes in the selected dataset.
  452. #' @param to_type character, the attribute name match the target ID type.
  453. #' @param use_genes a vector of characters, the genes for ID conversion. Must be the genes with ID type of \code{from_type}, and from species \code{from_spe}.
  454. #' If NULL, all the possible genes will be shown in the conversion table. Default is NULL.
  455. #' @param useCache Boolean, parameter pass to \code{getBM()} indicating whether the results cache should be used. Setting to FALSE will disable reading and writing of the cache.
  456. #'
  457. #' @return Return a data frame for ID conversion, from one species to another.
  458. #'
  459. #' @examples
  460. #' use_genes <- c("ENST00000210187","ENST00000216083","ENST00000216127",
  461. #' "ENST00000216416","ENST00000217233","ENST00000221418")
  462. #' transfer_tab <- get_IDtransfer_betweenSpecies(from_spe='human',
  463. #' to_spe='mouse',
  464. #' from_type = 'ensembl_transcript_id',
  465. #' to_type='external_gene_name',
  466. #' use_genes=use_genes)
  467. #' ## get transfer table !!!
  468. #' transfer_tab <- get_IDtransfer_betweenSpecies(from_spe='human',
  469. #' to_spe='mouse',
  470. #' from_type = 'ensembl_transcript_id',
  471. #' to_type='ensembl_transcript_id_version',
  472. #' use_genes=use_genes)
  473. #' ## get transfer table !!!
  474. #' \dontrun{
  475. #' transfer_tab <- get_IDtransfer_betweenSpecies(from_spe='human',
  476. #' to_spe='mouse',
  477. #' from_type='refseq_mrna',
  478. #' to_type='refseq_mrna')
  479. #' }
  480. #' @export
  481. get_IDtransfer_betweenSpecies <- function(from_spe='human',to_spe='mouse',
  482. from_type=NULL,to_type=NULL,
  483. use_genes=NULL,useCache = TRUE){
  484. #
  485. all_input_para <- c('from_spe','to_spe','from_type','to_type')
  486. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  487. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  488. #
  489. from_spe <- toupper(from_spe)
  490. to_spe <- toupper(to_spe)
  491. ensembl <- biomaRt::useMart("ensembl")
  492. all_ds <- biomaRt::listDatasets(ensembl)
  493. w1 <- grep(sprintf("^%s GENES",from_spe),toupper(all_ds$description))
  494. if(base::length(w1)==1){
  495. from_spe_ds <- all_ds[w1,1]
  496. mart1 <- biomaRt::useMart(biomart="ensembl", dataset=from_spe_ds) ## get id for input spe
  497. }
  498. if(base::length(w1)==0){
  499. message(sprintf('Check input from_spe parameter: %s, not included in the ensembl database',from_spe))
  500. return(FALSE)
  501. }
  502. if(base::length(w1)>1){
  503. w2 <- base::paste(all_ds[w1,2],collapse=';')
  504. message(sprintf('Check input from_spe parameter: %s, more than one species match in ensembl database : %s,
  505. please check and re-try',from_spe,w2))
  506. return(FALSE)
  507. }
  508. w1 <- grep(sprintf("^%s GENES",to_spe),toupper(all_ds$description))
  509. if(base::length(w1)==1){
  510. to_spe_ds <- all_ds[w1,1]
  511. mart2 <- biomaRt::useMart(biomart="ensembl", dataset=to_spe_ds) ## get id for input spe
  512. }
  513. if(base::length(w1)==0){
  514. message(sprintf('Check input to_spe parameter: %s, not included in the ensembl database',to_spe))
  515. return(FALSE)
  516. }
  517. if(base::length(w1)>1){
  518. w2 <- base::paste(all_ds[w1,2],collapse=';')
  519. message(sprintf('Check input to_spe parameter: %s, more than one species match in ensembl database : %s,
  520. please check and re-try',to_spe,w2))
  521. return(FALSE)
  522. }
  523. #### mart1 mart2
  524. attributes <- biomaRt::listAttributes(mart1)
  525. if(!from_type %in% attributes$name){
  526. message(sprintf('%s not in the attributes for %s, please check and re-try !',from_type,from_spe));return(FALSE)
  527. }
  528. attributes <- biomaRt::listAttributes(mart2)
  529. if(!to_type %in% attributes$name){
  530. message(sprintf('%s not in the attributes for %s, please check and re-try !',to_type,to_spe));return(FALSE)
  531. }
  532. ## get homolog between from_spe to to_spe
  533. cn1 <- gsub('(.*)_gene_ensembl','\\1',from_spe_ds)
  534. cn2 <- attributes$name ## attribute names in mart2
  535. cn3 <- cn2[grep(sprintf('%s_homolog_associated_gene_name',cn1),cn2)]
  536. if(base::length(cn3)!=1){
  537. message('No homolog info found in Biomart, sorry !');return(FALSE)
  538. }
  539. tmp1 <- get_IDtransfer(from_type=from_type,to_type='external_gene_name',use_genes=use_genes,dataset=from_spe_ds)
  540. tmp2 <- biomaRt::getBM(attributes=c(cn3,'external_gene_name'),values=TRUE,
  541. mart=mart2,filters=sprintf('with_%s_homolog',cn1),useCache = useCache)
  542. colnames(tmp1) <- sprintf('%s_%s',colnames(tmp1),from_spe)
  543. colnames(tmp2) <- sprintf('%s_%s',colnames(tmp2),to_spe)
  544. tmp3 <- base::merge(tmp1,tmp2,by.x=sprintf('external_gene_name_%s',from_spe),by.y=sprintf('%s_%s',cn3,to_spe))
  545. transfer_tab <- tmp3[,c(2,3,1)]
  546. if(to_type != 'external_gene_name'){
  547. tmp4 <- get_IDtransfer(from_type='external_gene_name',to_type=to_type,use_genes=tmp3[,3],dataset=to_spe_ds)
  548. colnames(tmp4) <- sprintf('%s_%s',colnames(tmp4),to_spe)
  549. tmp5 <- base::merge(tmp3,tmp4)
  550. transfer_tab <- tmp5[,c(3,4,2,1)]
  551. }
  552. return(transfer_tab)
  553. }
  554. #' Create Data Frame for ID Conversion With Biotype Information
  555. #'
  556. #' \code{get_IDtransfer2symbol2type} creates a data frame to convert original ID into gene symbol and gene biotype (gene level),
  557. #' or into transcript symbol and transcript biotype (transcript level).
  558. #'
  559. #' @param from_type character, the attribute name matches the current ID type (the type of use_genes).
  560. #' Such as "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" or "refseq_mrna".
  561. #' The "attribute" is inherited from the biomaRt package.
  562. #' For details, user can call \code{biomaRt::listAttributes()} function to display all available attributes in the selected dataset.
  563. #' @param use_genes a vector of characters, the genes for ID conversion.
  564. #' If NULL, all genes will be selected.
  565. #' @param dataset character, name of the dataset used for ID conversion.
  566. #' For example, "hsapiens_gene_ensembl".
  567. #' If NULL, \code{db_info[1]} will be used. \code{db_info} requires the calling of \code{db.preload} in the previous steps.
  568. #' Default is NULL.
  569. #' @param use_level character, users can chose between "transcript" and "gene". Default is "gene".
  570. #' @param ignore_version logical, if it is set to TRUE and \code{from_type} is "ensembl_gene_id_version" or "ensembl_transcript_id_version",
  571. #' the version of the original ID will be ignored in ID mapping.
  572. #'
  573. #' @return
  574. #' Return a data frame for ID conversion, from ID to gene symbol and gene biotype.
  575. #'
  576. #' @examples
  577. #' use_genes <- c("ENST00000210187","ENST00000216083","ENST00000216127",
  578. #' "ENST00000216416","ENST00000217233","ENST00000221418")
  579. #' transfer_tab <- get_IDtransfer(from_type = 'ensembl_transcript_id',
  580. #' to_type='external_gene_name',use_genes=use_genes,
  581. #' dataset='hsapiens_gene_ensembl')
  582. #' ## get transfer table !!!
  583. #' res1 <- get_name_transfertab(use_genes,transfer_tab=transfer_tab)
  584. #' transfer_tab_withtype <- get_IDtransfer2symbol2type(from_type = 'ensembl_transcript_id',
  585. #' use_genes=use_genes,
  586. #' dataset='hsapiens_gene_ensembl',
  587. #' use_level='transcript')
  588. #' ## get transfer table !!!
  589. #' \dontrun{
  590. #' }
  591. #' @export
  592. get_IDtransfer2symbol2type <- function(from_type=NULL,use_genes=NULL,dataset=NULL,use_level='gene',ignore_version=FALSE){
  593. #
  594. all_input_para <- c('from_type')
  595. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  596. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  597. check_res <- c(check_option('ignore_version',c(TRUE,FALSE),envir=environment()),
  598. check_option('use_level',c('gene','transcript'),envir=environment()))
  599. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  600. #
  601. if(is.null(dataset)==TRUE){
  602. check_res <- check_para('db_info',envir=environment())
  603. if(base::min(check_res)==0){message('Please use db.preload() to get db_info or set dataset, check and re-try!');return(FALSE)}
  604. dataset <- db_info[1]
  605. }
  606. message(sprintf('Your setting is at %s level',use_level))
  607. mart <- biomaRt::useMart(biomart="ensembl", dataset=dataset) ## get mart for id conversion !!!! db_info is saved in db RData
  608. attributes <- biomaRt::listAttributes(mart)
  609. if(!from_type %in% attributes$name){
  610. message(sprintf('%s not in the attributes for %s, please check and re-try !',from_type,dataset));return(FALSE)
  611. }
  612. if(use_level=='gene') tmp1 <- get_IDtransfer(from_type=from_type,to_type='external_gene_name',add_type='gene_biotype',use_genes=use_genes,dataset=dataset,ignore_version=ignore_version)
  613. if(use_level=='transcript') tmp1 <- get_IDtransfer(from_type=from_type,to_type='external_transcript_name',add_type='transcript_biotype',use_genes=use_genes,dataset=dataset,ignore_version=ignore_version)
  614. transfer_tab <- tmp1
  615. return(transfer_tab)
  616. }
  617. #' Convert Original Gene ID into Target Gene ID
  618. #'
  619. #' \code{get_name_transfertab} converts the original gene IDs into target gene IDs, with conversion table provided.
  620. #'
  621. #' @param use_genes a vector of characters, the genes for ID conversion.
  622. #' @param transfer_tab data.frame, the conversion table. Users can create it by calling \code{get_IDtransfer}.
  623. #' @param from_type character, the attribute name match the current ID type (the type of use_genes).
  624. #' Such as "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" or "refseq_mrna".
  625. #' The "attribute" is inherited from the biomaRt package. For details, user can call \code{biomaRt::listAttributes()} to see all available attributes in the selected dataset.
  626. #' If NULL, will use the first column of \code{transfer_tab}.
  627. #' @param to_type character, the attribute name to convert into. If NULL, will use the second column of \code{transfer_tab}.
  628. #' @param ignore_version logical, if TRUE and \code{from_type} is "ensembl_gene_id_version" or "ensembl_transcript_id_version", the version will be ignored. Default is FALSE.
  629. #' @param ignore_order logical, whether need to ignore the output order to match the input list of \code{use_genes}. Default is FALSE.
  630. #' @return Return a vector of converted gene IDs.
  631. #'
  632. #' @examples
  633. #' use_genes <- c("ENST00000210187","ENST00000216083","ENST00000216127",
  634. #' "ENST00000216416","ENST00000217233","ENST00000221418")
  635. #' transfer_tab <- get_IDtransfer(from_type = 'ensembl_transcript_id',
  636. #' to_type='external_gene_name',use_genes=use_genes,
  637. #' dataset='hsapiens_gene_ensembl')
  638. #' ## get transfer table !!!
  639. #' res1 <- get_name_transfertab(use_genes=use_genes,transfer_tab=transfer_tab)
  640. #' transfer_tab_withtype <- get_IDtransfer2symbol2type(from_type = 'ensembl_transcript_id',
  641. #' use_genes=use_genes,
  642. #' dataset='hsapiens_gene_ensembl')
  643. #' ## get transfer table !!!
  644. #' \dontrun{
  645. #' }
  646. #' @export
  647. get_name_transfertab <- function(use_genes=NULL,transfer_tab=NULL,from_type=NULL,to_type=NULL,ignore_version=FALSE,ignore_order=FALSE){
  648. #
  649. all_input_para <- c('use_genes','transfer_tab')
  650. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  651. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  652. check_res <- c(check_option('ignore_version',c(TRUE,FALSE),envir=environment()),
  653. check_option('ignore_order',c(TRUE,FALSE),envir=environment()))
  654. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  655. #
  656. if(is.null(from_type)==TRUE){from_type=colnames(transfer_tab)[1];}
  657. if(is.null(to_type)==TRUE){to_type=colnames(transfer_tab)[2];}
  658. if(ignore_version==TRUE){
  659. w1 <- which(colnames(transfer_tab)==from_type)
  660. transfer_tab[,w1] <- gsub('(.*)\\..*','\\1',transfer_tab[,w1])
  661. from_type <- gsub('(.*)_version','\\1',from_type)
  662. colnames(transfer_tab)[w1] <- from_type
  663. use_genes <- gsub('(.*)\\..*','\\1',use_genes)
  664. }
  665. transfer_tab <- base::unique(transfer_tab[,c(from_type,to_type)])
  666. x <- use_genes;
  667. t1 <- base::unique(transfer_tab[which(transfer_tab[,from_type] %in% x),])
  668. c1 <- base::unique(t1[,from_type])
  669. if(base::length(c1)<nrow(t1) & ignore_order==FALSE){
  670. message('Gene ID in from type contain multiple items!');return(FALSE)
  671. }
  672. if(ignore_order==TRUE){
  673. x1 <- t1[,to_type]
  674. }else{
  675. rownames(t1) <- t1[,from_type]
  676. x1 <- t1[x,to_type]
  677. w1 <- which(is.na(x1)==TRUE)
  678. x1[w1] <- x[w1]
  679. }
  680. return(x1)
  681. }
  682. #' Manipulation of Working Directories for NetBID2 Network Construction Step
  683. #'
  684. #' \code{NetBID.network.dir.create} is used to help users create an organized working directory for the network construction step in NetBID2 analysis.
  685. #' However, it is not essential for the analysis.
  686. #' It creates a hierarchcial working directory and returns a list contains this directory information.
  687. #'
  688. #' This function needs users to define the main working directory and the project's name.
  689. #' It creates a main working directory with a subdirectory of the project.
  690. #' It also automatically creates three subfolders (QC, DATA and SJAR) within the project folder. QC/,
  691. #' storing Quality Control related plots; DATA/, saving data in RData format;
  692. #' SJAR/, storing files needed for running SJAracne command.
  693. #' This function also returns a list object (example, \code{network.par} in the demo) with directory information wrapped inside.
  694. #' This list is an essential for
  695. #' network construction step, all the important intermediate data generated later will be wrapped inside.
  696. #' @param project_main_dir character, name or absolute path of the main working directory.
  697. #' @param project_name character, name of the project folder.
  698. #'
  699. #' @return \code{NetBID.network.dir.create} returns a list object, containing main.dir (path of the main working directory),
  700. #' project.name (project name), out.dir (path of the project folder, which contains three subfolders), out.dir.QC,
  701. #' out.dir.DATA and out.dir.SJAR.
  702. #' @examples
  703. #'
  704. #' \dontrun{
  705. #' # Creating a main working directory under the current working directory by folder name
  706. #' network.par <- NetBID.network.dir.create("MyMainDir","MyProject")
  707. #' # Or creating a main working directory under the current working directory by relative path
  708. #' network.par <- NetBID.network.dir.create("./MyMainDir","MyProject")
  709. #' # Or creating a main working directory to a specific path by absolute path
  710. #' network.par <- NetBID.network.dir.create("~/Desktop/MyMainDir","MyProject")
  711. #' }
  712. #' @export
  713. NetBID.network.dir.create <- function(project_main_dir=NULL,project_name=NULL){
  714. #
  715. if(base::exists('network.par')==TRUE){
  716. stop('network.par is occupied in the current session,please manually run: rm(network.par) and re-try, otherwise will not change !');
  717. }
  718. #
  719. all_input_para <- c('project_main_dir','project_name')
  720. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  721. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  722. #
  723. network.par <- list()
  724. network.par$main.dir <- project_main_dir
  725. network.par$project.name <- project_name
  726. network.par$out.dir <- sprintf('%s/%s',network.par$main.dir,network.par$project.name)
  727. # create output directory
  728. if (!dir.exists(project_main_dir)) {
  729. dir.create(project_main_dir, recursive = TRUE)
  730. }
  731. if (!dir.exists(network.par$out.dir)) {
  732. dir.create(network.par$out.dir, recursive = TRUE)
  733. }
  734. network.par$out.dir.QC <- paste0(network.par$out.dir, '/QC/')
  735. if (!dir.exists(network.par$out.dir.QC)) {
  736. dir.create(network.par$out.dir.QC, recursive = TRUE) ## directory for QC
  737. }
  738. network.par$out.dir.DATA <- paste0(network.par$out.dir, '/DATA/')
  739. if (!dir.exists(network.par$out.dir.DATA)) {
  740. dir.create(network.par$out.dir.DATA, recursive = TRUE) ## directory for DATA
  741. }
  742. network.par$out.dir.SJAR <- paste0(network.par$out.dir, '/SJAR/')
  743. if (!dir.exists(network.par$out.dir.SJAR)) {
  744. dir.create(network.par$out.dir.SJAR, recursive = TRUE) ## directory for SJARAcne
  745. }
  746. message(sprintf('Project space created, please check %s',network.par$out.dir))
  747. return(network.par)
  748. }
  749. #' Manipulation of Working Directories for NetBID2 Driver Estimation Step
  750. #'
  751. #' \code{NetBID.analysis.dir.create} is used to help users create an organized working directory
  752. #' for the driver estimation step in NetBID2 analysis.
  753. #' However, it is not essential for the analysis.
  754. #' It creates a hierarchcial working directory and returns a list contains this directory information.
  755. #'
  756. #' This function requires user to define the main working directory and the project’s name.
  757. #' It creates a main working directory with a subdirectory of the project.
  758. #' It also automatically creates three subfolders (QC, DATA and PLOT) within the project folder.
  759. #' QC/, storing Quality Control related plots; DATA/, saving data in RData format; PLOT/, storing output plots.
  760. #' This function also returns a list object (e.g. \code{analysis.par} in the demo) with directory information wrapped inside.
  761. #' This list is an essential for driver construction step, all the important intermediate data generated later will be wrapped inside.
  762. #'
  763. #' @param project_main_dir character, name or absolute path of the main working directory for driver analysis.
  764. #' @param project_name character, name of the project folder.
  765. #' @param network_dir character, name or absolute path of the main working directory for network construction.
  766. #' @param network_project_name character, the project name of network construction. Or use the project name of SJARACNe.
  767. #' This parameter is optional. If one didn't run NetBID2 network construction part in the pipeline, he could set it to NULL.
  768. #' If one like to follow the NetBID2 pipeline, he should set it to the path of the TF network file and the SIG network file.
  769. #' @param tf.network.file character, the path of the TF network file (e.g. "XXX/consensus_network_ncol_.txt").
  770. #' Default is the path of network_project_name.
  771. #' @param sig.network.file character, the path of the SIG network file (e.g. "XXX/consensus_network_ncol_.txt").
  772. #' Default is the path of network_project_name.
  773. #'
  774. #' @return Returns a list object, containing main.dir (path of the main working directory), project.name (project name),
  775. #' out.dir (path of the project folder, which contains three subfolders), out.dir.QC, out.dir.DATA and out.dir.PLOT.
  776. #'
  777. #' @examples
  778. #'
  779. #' \dontrun{
  780. #' network.dir <- sprintf('%s/demo1/network/',system.file(package = "NetBID2")) # use demo
  781. #' network.project.name <- 'project_2019-02-14' #
  782. #' project_main_dir <- 'demo1/'
  783. #' project_name <- 'driver_test'
  784. #' analysis.par <- NetBID.analysis.dir.create(project_main_dir=project_main_dir,
  785. #' project_name=project_name,
  786. #' network_dir=network.dir,
  787. #' network_project_name=network.project.name)
  788. #' }
  789. #' @export
  790. NetBID.analysis.dir.create <- function(project_main_dir=NULL,project_name=NULL,
  791. network_dir=NULL,
  792. network_project_name=NULL,
  793. tf.network.file=NULL,
  794. sig.network.file=NULL){
  795. #
  796. if(base::exists('analysis.par')==TRUE){
  797. stop('analysis.par is occupied in the current session,please manually run: rm(analysis.par) and re-try, otherwise will not change !');
  798. }
  799. #
  800. all_input_para <- c('project_main_dir','project_name')
  801. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  802. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  803. if((is.null(network_dir)==TRUE | is.null(network_project_name)==TRUE) & (is.null(tf.network.file)==TRUE | is.null(sig.network.file)==TRUE)){
  804. message('Either network_dir,network_project_name or tf.network.file,sig.network.file is required, please check and re-try !')
  805. return(FALSE);
  806. }
  807. #
  808. analysis.par <- list()
  809. analysis.par$main.dir <- project_main_dir
  810. analysis.par$project.name <- project_name
  811. analysis.par$out.dir <- sprintf('%s/%s/',analysis.par$main.dir,analysis.par$project.name)
  812. analysis.par$tf.network.file <- ''
  813. analysis.par$sig.network.file <- ''
  814. if(is.null(tf.network.file)==FALSE){
  815. analysis.par$tf.network.file <- tf.network.file
  816. }else{
  817. tf_net1 <- sprintf('%s/SJAR/%s/output_tf_sjaracne_%s_out_.final/consensus_network_ncol_.txt',
  818. network_dir,network_project_name,network_project_name) ## old version of sjaracne
  819. tf_net2 <- sprintf('%s/SJAR/SJARACNE_%s_TF/consensus_network_ncol_.txt',
  820. network_dir,network_project_name) ## new version of sjaracne
  821. if(file.exists(tf_net2)) analysis.par$tf.network.file <- tf_net2 else analysis.par$tf.network.file <- tf_net1
  822. }
  823. if(is.null(tf.network.file)==FALSE){
  824. analysis.par$sig.network.file <- sig.network.file
  825. }else{
  826. sig_net1 <- sprintf('%s/SJAR/%s/output_sig_sjaracne_%s_out_.final/consensus_network_ncol_.txt',
  827. network_dir,network_project_name,network_project_name) ## old version of sjaracne
  828. sig_net2 <- sprintf('%s/SJAR/SJARACNE_%s_SIG/consensus_network_ncol_.txt',
  829. network_dir,network_project_name) ## new version of sjaracne
  830. if(file.exists(sig_net2)) analysis.par$sig.network.file <- sig_net2 else analysis.par$sig.network.file <- sig_net1
  831. }
  832. if(file.exists(analysis.par$tf.network.file)){
  833. message(sprintf('TF network file found in %s',analysis.par$tf.network.file))
  834. }else{
  835. message(sprintf('TF network file not found in %s, please check and re-try !',analysis.par$tf.network.file))
  836. return(FALSE)
  837. }
  838. if(file.exists(analysis.par$sig.network.file)){
  839. message(sprintf('SIG network file found in %s',analysis.par$sig.network.file))
  840. }else{
  841. message(sprintf('SIG network file not found in %s, please check and re-try ',analysis.par$sig.network.file))
  842. return(FALSE)
  843. }
  844. # create output directory
  845. if (!dir.exists(analysis.par$out.dir)) {
  846. dir.create(analysis.par$out.dir, recursive = TRUE)
  847. }
  848. analysis.par$out.dir.QC <- paste0(analysis.par$out.dir, '/QC/')
  849. if (!dir.exists(analysis.par$out.dir.QC)) {
  850. dir.create(analysis.par$out.dir.QC, recursive = TRUE) ## directory for QC
  851. }
  852. analysis.par$out.dir.DATA <- paste0(analysis.par$out.dir, '/DATA/')
  853. if (!dir.exists(analysis.par$out.dir.DATA)) {
  854. dir.create(analysis.par$out.dir.DATA, recursive = TRUE) ## directory for DATA
  855. }
  856. analysis.par$out.dir.PLOT <- paste0(analysis.par$out.dir, '/PLOT/')
  857. if (!dir.exists(analysis.par$out.dir.PLOT)) {
  858. dir.create(analysis.par$out.dir.PLOT, recursive = TRUE) ## directory for Result Plots
  859. }
  860. #
  861. message(sprintf('Analysis space created, please check %s',analysis.par$out.dir))
  862. return(analysis.par)
  863. }
  864. #' Save Data Produced by Corresponding NetBID2 Pipeline Step.
  865. #'
  866. #' \code{NetBID.saveRData} is a function to save complicated list object generated by certain steps of NetBID2's pipeline
  867. #' (e.g. load gene expression file from GEO, 'exp-load').
  868. #' This function is not essential, but it is highly suggested for easier pipeline step checkout and reference.
  869. #'
  870. #' There are two important steps in the NetBID2 pipeline, network construction and driver analysis.
  871. #' User can save these two complicated list objects, network.par and analysis.par.
  872. #' Assigning the \code{step} name to save the RData for easier reference.
  873. #' Calling \code{NetBID.loadRData} to load the corresponding step RData, users can avoid repeating the former steps.
  874. #'
  875. #' @param network.par list, stores all related datasets from network construction pipeline step.
  876. #' @param analysis.par list, stores all related datasets from driver analysis pipeline step.
  877. #' @param step character, name of the pipeline step decided by user for easier reference.
  878. #'
  879. #' @examples
  880. #' \dontrun{
  881. #' analysis.par <- list()
  882. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  883. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  884. #' NetBID.saveRData(analysis.par=analysis.par,step='ms-tab_test')
  885. #' }
  886. #' @export
  887. NetBID.saveRData <- function(network.par=NULL,analysis.par=NULL,step='exp-load'){
  888. #
  889. all_input_para <- c('step')
  890. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  891. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  892. #
  893. if(is.null(network.par)==FALSE & is.null(analysis.par)==FALSE){
  894. message('Can not save network.par and analysis.par at once, please only use one !');return(FALSE)
  895. }
  896. if(is.null(network.par)==FALSE){
  897. save(network.par,file=sprintf('%s/network.par.Step.%s.RData',network.par$out.dir.DATA,step))
  898. message(sprintf('Successful save to %s',sprintf('%s/network.par.Step.%s.RData',network.par$out.dir.DATA,step)))
  899. }
  900. if(is.null(analysis.par)==FALSE){
  901. save(analysis.par,file=sprintf('%s/analysis.par.Step.%s.RData',analysis.par$out.dir.DATA,step))
  902. message(sprintf('Successful save to %s',sprintf('%s/analysis.par.Step.%s.RData',analysis.par$out.dir.DATA,step)))
  903. }
  904. }
  905. #' Reload Saved RData Created by \code{NetBID.saveRData}.
  906. #'
  907. #' \code{NetBID.loadRData} is a function loads RData saved by \code{NetBID.saveRData} function.
  908. #' It prevents user from repeating former pipeline steps.
  909. #'
  910. #' @param network.par list, stores all related datasets from network construction step.
  911. #' @param analysis.par list, stores all related datasets from driver analysis step.
  912. #' @param step character, name of the pipeline step. It should be previously assigned by user when calling \code{NetBID.saveRData} function.
  913. #'
  914. #' @examples
  915. #' analysis.par <- list()
  916. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  917. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  918. #'
  919. #' @export
  920. NetBID.loadRData <- function(network.par=NULL,analysis.par=NULL,step='exp-load'){
  921. #
  922. all_input_para <- c('step')
  923. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  924. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  925. #
  926. if(is.null(network.par)==FALSE & is.null(analysis.par)==FALSE){
  927. message('Can not load network.par and analysis.par at once, please only use one !');return(FALSE)
  928. }
  929. if(is.null(network.par)==FALSE){
  930. load(file=sprintf('%s/network.par.Step.%s.RData',network.par$out.dir.DATA,step),.GlobalEnv)
  931. message(sprintf('Successful load from %s',sprintf('%s/network.par.Step.%s.RData',network.par$out.dir.DATA,step)))
  932. }
  933. if(is.null(analysis.par)==FALSE){
  934. load(file=sprintf('%s/analysis.par.Step.%s.RData',analysis.par$out.dir.DATA,step),.GlobalEnv)
  935. message(sprintf('Successful load from %s',sprintf('%s/analysis.par.Step.%s.RData',analysis.par$out.dir.DATA,step)))
  936. }
  937. }
  938. #' Download Gene Expression Series From GEO Database with Platform Specified
  939. #'
  940. #' \code{load.exp.GEO} downloads user assigned Gene Expression Series (GSE file) along with its Platform from GEO dataset.
  941. #' It returns an ExpressionSet class object and saves it as RData. If the GSE RData already exists, it will be loaded directly.
  942. #' It also allows users to update the Gene Expression Series RData saved before.
  943. #'
  944. #' @param out.dir character, the file path used to save the GSE RData. If the data already exsits, it will be loaded from this path.
  945. #' @param GSE character, the GEO Series Accession ID.
  946. #' @param GPL character, the GEO Platform Accession ID.
  947. #' @param getGPL logical, if TRUE, the corresponding GPL file will be downloaded. Default is TRUE.
  948. #' @param update logical, if TRUE, the previous stored Gene ExpressionSet RData will be updated. Default is FALSE
  949. #'
  950. #' @return Return an ExpressionSet class object.
  951. #' @examples
  952. #'
  953. #' \dontrun{
  954. #' # Download the GSE116028 which performed on GPL6480 platform
  955. #' # from GEO and save it to the current directory
  956. #' # Assign this ExpressionSet object to net_eset
  957. #' net_eset <- load.exp.GEO(out.dir='./',
  958. #' GSE='GSE116028',
  959. #' GPL='GPL6480',
  960. #' getGPL=TRUE,
  961. #' update=FALSE)
  962. #' }
  963. #' @export
  964. load.exp.GEO <- function(out.dir = NULL,GSE = NULL,GPL = NULL,getGPL=TRUE,update = FALSE){
  965. #
  966. all_input_para <- c('out.dir','GSE','GPL')
  967. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  968. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  969. check_res <- c(check_option('getGPL',c(TRUE,FALSE),envir=environment()),
  970. check_option('update',c(TRUE,FALSE),envir=environment()))
  971. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  972. #
  973. if(!grepl('^GSE',GSE)){
  974. message('Only support GSE ID')
  975. return(FALSE)
  976. }
  977. expRData_dir <- sprintf('%s/%s_%s.RData', out.dir, GSE,GPL)
  978. if (file.exists(expRData_dir) & update == FALSE) {
  979. message(sprintf('RData exist in %s and update==TRUE, will directly load from RData .',expRData_dir))
  980. load(expRData_dir)
  981. } else{
  982. eset <- GEOquery::getGEO(GSE, GSEMatrix = TRUE, getGPL = getGPL)
  983. if (base::length(eset) > 1)
  984. idx <- grep(GPL, attr(eset, "names"))
  985. else
  986. idx <- 1
  987. eset <- eset[[idx]]
  988. if(GPL!=annotation(eset)) {GPL <- annotation(eset); message(sprintf('Real GPL:%s',GPL))}
  989. expRData_dir <- sprintf('%s/%s_%s.RData', out.dir, GSE,GPL)
  990. save(eset, file = expRData_dir)
  991. message(sprintf('RData for the eset is saved in %s .',expRData_dir))
  992. }
  993. return(eset)
  994. }
  995. #' Load Gene Expression Set from Salmon Output (demo version)
  996. #'
  997. #' \code{load.exp.RNASeq.demoSalmon} is a function to read in Salmon results and convert it to eSet/DESeqDataSet class object.
  998. #'
  999. #' This function helps users to read in results created by Salmon.
  1000. #' Due to the complicated manipulations (e.g. reference sequence) in processing Salmon, this demo function may not be suitable for all scenarios.
  1001. #'
  1002. #' @param salmon_dir character, the directory to save the results created by Salmon.
  1003. #' @param tx2gene data.frame or NULL, this parameter will be passed to \code{tximport}. For details, please check \code{tximport}.
  1004. #' If NULL, will read in one of the transcript names from Salmon's results. Note, it works when using e.g. "gencode.v29.transcripts.fa" from GENCODE as reference.
  1005. #' @param use_phenotype_info data.frame, the data frame contains phenotype information. It must have the columns \code{use_sample_col} and \code{use_design_col}.
  1006. #' @param use_sample_col character, the column name, indicating which column in \code{use_phenotype_info} should be used as the sample name.
  1007. #' @param use_design_col character, the column name, indicating which column in \code{use_phenotype_info} should be used as the design feature for samples.
  1008. #' @param return_type character, the class of the return object.
  1009. #' "txi" is the output of tximport. It is a list containing three matrices, abundance, counts and length.
  1010. #' "counts" is the matrix of raw count.
  1011. #' "tpm" is the raw tpm.
  1012. #' "fpm", "cpm" is the fragments/counts per million mapped fragments (fpm/cpm).
  1013. #' "raw-dds" is the DESeqDataSet class object, which is the original one without processing.
  1014. #' "dds" is the DESeqDataSet class object, which is processed by DESeq.
  1015. #' "eset" is the ExpressionSet class object, which is processed by DESeq and vst.
  1016. #' Default is "tpm".
  1017. #' @param merge_level character, users can choose between "gene" and "transcript".
  1018. #' "gene", the original salmon results will be mapped to the transcriptome and the expression matrix will be merged to the gene level.
  1019. #' This only works when using e.g. "gencode.vXX.transcripts.fa" from GENCODE as the reference.
  1020. #' @export
  1021. load.exp.RNASeq.demoSalmon <- function(salmon_dir = NULL,tx2gene=NULL,
  1022. use_phenotype_info = NULL,
  1023. use_sample_col=NULL,
  1024. use_design_col=NULL,
  1025. return_type='tpm',
  1026. merge_level='gene') {
  1027. #
  1028. all_input_para <- c('salmon_dir','use_phenotype_info','use_sample_col','use_design_col','return_type','merge_level')
  1029. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1030. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1031. check_res <- c(check_option('return_type',c('txi','counts','tpm','fpm','cpm','raw-dds','dds','eset'),envir=environment()),
  1032. check_option('merge_level',c('gene','transcript'),envir=environment()))
  1033. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1034. #
  1035. files <- file.path(salmon_dir, list.files(salmon_dir), "quant.sf")
  1036. if(!grepl('_salmon',use_phenotype_info[1,use_sample_col])) sample_name <- gsub('(.*)_salmon', '\\1', list.files(salmon_dir)) ## if no _salmon is also ok
  1037. names(files) <- sample_name
  1038. w1 <- base::length(files)
  1039. message(sprintf('%d %s/quant.sf found !',w1,salmon_dir))
  1040. if(is.null(tx2gene)){
  1041. gene_info <- read.delim(file = files[1], stringsAsFactors = FALSE)[, 1]
  1042. gen1 <- sapply(gene_info, function(x)unlist(strsplit(x, '\\|')))
  1043. gen1 <- t(gen1)
  1044. if(merge_level=='gene'){
  1045. tx2gene <- data.frame('transcript' = gene_info,'gene' = gen1[,2],stringsAsFactors = FALSE)
  1046. }else{
  1047. tx2gene <- data.frame('transcript' = gene_info,'gene' = gen1[,1],stringsAsFactors = FALSE)
  1048. }
  1049. }
  1050. eset <- load.exp.RNASeq.demo(files,type='salmon',
  1051. tx2gene=tx2gene,
  1052. use_phenotype_info=use_phenotype_info,
  1053. use_sample_col=use_sample_col,
  1054. use_design_col=use_design_col,
  1055. return_type=return_type,
  1056. merge_level=merge_level)
  1057. return(eset)
  1058. }
  1059. #' Load Gene Expression Set from RNA-Seq Results (demo version)
  1060. #'
  1061. #' \code{load.exp.RNASeq.demo} is a function to read in RNA-Seq results and convert it to \code{eSet/DESeqDataSet} class object.
  1062. #'
  1063. #' This function helps users to read in RNA-Seq results from various sources.
  1064. #' Due to the complicated manipulations (e.g. reference sequence) in processing RNA-Seq, this demo function may not be suitable for all scenarios.
  1065. #'
  1066. #' @param files a vector of characters, the filenames for the transcript-level abundances. It will be passed to \code{tximport}.
  1067. #' For details, please check \code{tximport}.
  1068. #' @param type character, the type of software used to generate the abundances. It will be passed to \code{tximport}.
  1069. #' For details, please check \code{tximport}.
  1070. #' @param tx2gene data.frame or NULL, this parameter will be passed to \code{tximport}. For details, please check \code{tximport}.
  1071. #' @param use_phenotype_info data.frame, the data frame contains phenotype information. It must have the columns \code{use_sample_col} and \code{use_design_col}.
  1072. #' @param use_sample_col character, the column name, indicating which column in \code{use_phenotype_info} should be used as the sample name.
  1073. #' @param use_design_col character, the column name, indicating which column in \code{use_phenotype_info} should be used as the design feature for samples.
  1074. #' @param return_type character, the class of the return object.
  1075. #' "txi" is the output of \code{tximport}. It is a list containing three matrices, abundance, counts and length.
  1076. #' "counts" is the matrix of raw count.
  1077. #' "tpm" is the raw tpm.
  1078. #' "fpm", "cpm" is the fragments/counts per million mapped fragments.
  1079. #' "raw-dds" is the DESeqDataSet class object, which is the original one without processing.
  1080. #' "dds" is the DESeqDataSet class object, which is processed by \code{DESeq}.
  1081. #' "eset" is the ExpressionSet class object, which is processed by \code{DESeq} and \code{vst}.
  1082. #' Default is "tpm".
  1083. #' @param merge_level character, users can choose between "gene" and "transcript".
  1084. #' "gene", the original salmon results will be mapped to the transcriptome and the expression matrix will be merged to the gene level.
  1085. #' This only works when using e.g. "gencode.vXX.transcripts.fa" from GENCODE as the reference.
  1086. #' @export
  1087. load.exp.RNASeq.demo <- function(files,type='salmon',
  1088. tx2gene=NULL,
  1089. use_phenotype_info = NULL,
  1090. use_sample_col=NULL,
  1091. use_design_col=NULL,
  1092. return_type='tpm',
  1093. merge_level='gene') {
  1094. #
  1095. all_input_para <- c('files','type','tx2gene','use_phenotype_info','use_sample_col','use_design_col','return_type','merge_level')
  1096. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1097. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1098. check_res <- c(check_option('return_type',c('txi','counts','tpm','fpm','cpm','raw-dds','dds','eset'),envir=environment()),
  1099. check_option('merge_level',c('gene','transcript'),envir=environment()))
  1100. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1101. #
  1102. n1 <- colnames(use_phenotype_info)
  1103. if(!use_sample_col %in% n1){
  1104. message(sprintf('%s not in the colnames of use_phenotype_info,
  1105. please check and re-try !',use_sample_col));return(FALSE)
  1106. }
  1107. if(!use_design_col %in% n1){
  1108. message(sprintf('%s not in the colnames of use_phenotype_info,
  1109. please check and re-try !',use_design_col));return(FALSE)
  1110. }
  1111. # get intersected samples
  1112. rownames(use_phenotype_info) <- use_phenotype_info[,use_sample_col]
  1113. w1 <- base::intersect(names(files),rownames(use_phenotype_info))
  1114. files <- files[w1]; use_phenotype_info <- use_phenotype_info[w1,]
  1115. if(base::length(w1)==0){
  1116. message(sprintf('No sample could match the %s in the use_phenotype_info, please check and re-try !',use_sample_col))
  1117. return(FALSE)
  1118. }
  1119. message(sprintf('%d samples could further processed !',base::length(w1)))
  1120. # import into txi
  1121. txi <- tximport::tximport(files, type = type, tx2gene = tx2gene) ## key step one, tximport
  1122. if(return_type=='counts'){
  1123. return(txi$counts)
  1124. }
  1125. if(return_type=='tpm'){
  1126. return(txi$abundance)
  1127. }
  1128. if(return_type=='txi'){
  1129. return(txi)
  1130. }
  1131. use_phenotype_info <- use_phenotype_info[colnames(txi$abundance), ]
  1132. tmp_phe <- base::cbind(group=use_phenotype_info[,use_design_col],use_phenotype_info,stringsAsFactors=FALSE)
  1133. # import into deseq2
  1134. dds <- DESeq2::DESeqDataSetFromTximport(txi, colData = tmp_phe, design = ~ group) ## key step two, DESeqDataSetFromTximport
  1135. if(return_type=='raw-dds'){
  1136. return(dds)
  1137. }
  1138. if(return_type=='fpm' | return_type=='cpm'){
  1139. return(DESeq2::fpm(dds))
  1140. }
  1141. dds <- DESeq2::DESeq(dds)
  1142. if(return_type=='dds'){
  1143. return(dds)
  1144. }else{
  1145. vsd <- DESeq2::vst(dds)
  1146. mat <- SummarizedExperiment::assay(vsd)
  1147. eset <- generate.eset(exp_mat=mat, phenotype_info = use_phenotype_info, feature_info = NULL, annotation_info='Salmon')
  1148. if(return_type=='eset') return(eset)
  1149. if(return_type=='both') return(list(eset=eset,dds=dds))
  1150. }
  1151. }
  1152. #' Generate ExpressionSet Object
  1153. #'
  1154. #' \code{generate.eset} generates ExpressionSet class object to contain and describe the high-throughput assays.
  1155. #' Users need to define its slots, which are expression matrix (required),
  1156. #' phenotype information and feature information (optional).
  1157. #' It is very useful when only expression matrix is available.
  1158. #'
  1159. #' @param exp_mat matrix, the expression data matrix. Each row represents a gene/transcript/probe, each column represents a sample.
  1160. #' @param phenotype_info data.frame, the phenotype information for all the samples in \code{exp_mat}.
  1161. #' In the phenotype data frame, each row represents a sample, each column represents a phenotype feature.
  1162. #' The row names must match the column names of \code{exp_mat}. If NULL, it will generate a single-column data frame.
  1163. #' Default is NULL.
  1164. #' @param feature_info data.frame, the feature information for all the genes/transcripts/probes in \code{exp_mat}.
  1165. #' In the feature data frame, each row represents a gene/transcript/probe and each column represents an annotation of the feature.
  1166. #' The row names must match the row names of \code{exp_mat}. If NULL, it will generate a single-column data frame.
  1167. #' Default is NULL.
  1168. #' @param annotation_info character, the annotation set by users for easier reference. Default is "".
  1169. #'
  1170. #' @return Return an ExressionSet object.
  1171. #'
  1172. #' @examples
  1173. #' mat1 <- matrix(rnorm(10000),nrow=1000,ncol=10)
  1174. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  1175. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  1176. #' eset <- generate.eset(exp_mat=mat1)
  1177. #' @export
  1178. generate.eset <- function(exp_mat=NULL, phenotype_info=NULL, feature_info=NULL, annotation_info="") {
  1179. #
  1180. all_input_para <- c('exp_mat')
  1181. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1182. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1183. #
  1184. if(is.null(dim(exp_mat))==TRUE){
  1185. exp_mat <- t(as.matrix(exp_mat));rownames(exp_mat) <- 'g1'
  1186. }
  1187. if (is.null(phenotype_info)) {
  1188. phenotype_info <- data.frame(group = colnames(exp_mat), stringsAsFactors = FALSE)
  1189. rownames(phenotype_info) <- colnames(exp_mat)
  1190. }
  1191. if (is.null(feature_info)) {
  1192. feature_info <- data.frame(gene = rownames(exp_mat), stringsAsFactors = FALSE)
  1193. rownames(feature_info) <- rownames(exp_mat)
  1194. }
  1195. if((class(phenotype_info)=='character' | is.null(dim(phenotype_info))==TRUE) & is.null(names(phenotype_info))==TRUE){
  1196. phenotype_info <- data.frame(group = phenotype_info, stringsAsFactors = FALSE)
  1197. rownames(phenotype_info) <- colnames(exp_mat)
  1198. }
  1199. if((class(feature_info)=='character' | is.null(dim(feature_info))==TRUE) & is.null(names(feature_info))==TRUE){
  1200. feature_info <- data.frame(gene = feature_info, stringsAsFactors = FALSE)
  1201. rownames(feature_info) <- rownames(exp_mat)
  1202. }
  1203. #
  1204. eset <-
  1205. new(
  1206. "ExpressionSet",
  1207. phenoData = new("AnnotatedDataFrame", phenotype_info),
  1208. featureData = new("AnnotatedDataFrame", feature_info),
  1209. annotation = annotation_info,
  1210. exprs = as.matrix(exp_mat)
  1211. )
  1212. return(eset)
  1213. }
  1214. #' Merge Two ExpressionSet Class Objects into One
  1215. #'
  1216. #' \code{merge_eset} merges two ExpressionSet class objects and returns one ExpresssionSet object.
  1217. #' If genes in the two ExpressionSet objects are identical, the expression matrix will be combined directly.
  1218. #' Otherwise, Z-transformation is strongly suggested to be performed before combination (set std=TRUE).
  1219. #'
  1220. #' @param eset1 ExpressionSet class, the first ExpressionSet.
  1221. #' @param eset2 ExpressionSet class, the second ExpressionSet.
  1222. #' @param group1 character, name of the first ExpressionSet.
  1223. #' @param group2 character, name of the second ExpressionSet.
  1224. #' @param use_col a vector of characters, the column names in the phenotype information to be kept.
  1225. #' If NULL, shared column names of \code{eset1} and \code{eset2} will be used. Default is NULL.
  1226. #' @param group_col_name character, name of the column which contains the names defined in \code{group1} and \code{group2}.
  1227. #' This column is designed to show which original ExpressionSet each sample comes from before combination.
  1228. #' Default name of this column is "original_group".
  1229. #' @param remove_batch logical, if TRUE, remove the batch effects from these two expression datasets. Default is FALSE.
  1230. #' @param std logical, whether to perform std to the original expression matrix. Default is FALSE.
  1231. #'
  1232. #' @return Return an ExressionSet class object.
  1233. #' @examples
  1234. #' mat1 <- matrix(rnorm(10000),nrow=1000,ncol=10)
  1235. #' colnames(mat1) <- paste0('Sample1_',1:ncol(mat1))
  1236. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  1237. #' eset1 <- generate.eset(exp_mat=mat1)
  1238. #' mat2 <- matrix(rnorm(10000),nrow=1000,ncol=10)
  1239. #' colnames(mat2) <- paste0('Sample2_',1:ncol(mat1))
  1240. #' rownames(mat2) <- paste0('Gene',1:nrow(mat1))
  1241. #' eset2 <- generate.eset(exp_mat=mat2)
  1242. #' new_eset <- merge_eset(eset1,eset2)
  1243. #' @export
  1244. merge_eset <- function(eset1,eset2,
  1245. group1=NULL,group2=NULL,
  1246. group_col_name='original_group',
  1247. use_col = NULL,
  1248. remove_batch = FALSE,std=FALSE) {
  1249. #
  1250. all_input_para <- c('eset1','eset2','group_col_name')
  1251. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1252. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1253. check_res <- c(check_option('remove_batch',c(TRUE,FALSE),envir=environment()),
  1254. check_option('std',c(TRUE,FALSE),envir=environment()))
  1255. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1256. #
  1257. mat1 <- Biobase::exprs(eset1)
  1258. mat2 <- Biobase::exprs(eset2)
  1259. w1 <- base::intersect(rownames(mat1), rownames(mat2))
  1260. if(base::length(w1)==0){
  1261. message('No overlap genes between two eSet, please check and re-try!');return(FALSE);
  1262. }
  1263. if((base::length(w1)<nrow(mat1) | base::length(w1)<nrow(mat2)) & std==FALSE){
  1264. message('Original two esets contain different gene list, strongly suggest to do z transformation (set std=TRUE) across all samples before merge!');
  1265. }
  1266. if(std==TRUE){
  1267. ## z-transformation
  1268. mat1 <- apply(mat1,2,do.std) # std to samples
  1269. mat2 <- apply(mat2,2,do.std)
  1270. }
  1271. rmat <- base::cbind(as.data.frame(mat1)[w1, ], as.data.frame(mat2)[w1,])
  1272. rmat <- as.matrix(rmat)
  1273. #choose1 <- apply(rmat <= quantile(rmat, probs = 0.05), 1, sum) <= ncol(rmat) * 0.90 ## low expressed genes
  1274. #rmat <- rmat[choose1, ]
  1275. phe1 <- Biobase::pData(eset1)
  1276. phe2 <- Biobase::pData(eset2)
  1277. phe1 <- as.data.frame(apply(phe1,2,clean_charVector),stringsAsFactors=F)
  1278. phe2 <- as.data.frame(apply(phe2,2,clean_charVector),stringsAsFactors=F)
  1279. if(base::length(use_col)==0){
  1280. use_col <- base::intersect(colnames(phe1),colnames(phe2))
  1281. }
  1282. rphe <- list();
  1283. if(base::length(use_col)>1)
  1284. rphe <- base::rbind(phe1[colnames(mat1), use_col], phe2[colnames(mat2), use_col])
  1285. if(base::length(use_col)==1){
  1286. rphe <- c(phe1[colnames(mat1), use_col], phe2[colnames(mat2), use_col])
  1287. rphe <- data.frame(rphe,stringsAsFactors=FALSE); colnames(rphe) <- use_col;
  1288. rownames(rphe) <- colnames(rmat)
  1289. }
  1290. if(base::length(use_col)==0){message('Warning: no intersected phenotype column!');}
  1291. if(is.null(group1)==TRUE) group1 <- 'group1'
  1292. if(is.null(group2)==TRUE) group2 <- 'group2'
  1293. rphe[[group_col_name]]<- c(rep(group1, ncol(mat1)), rep(group2, ncol(mat2)))
  1294. if (remove_batch == TRUE) {
  1295. rmat <- limma::removeBatchEffect(rmat,batch=rphe[[group_col_name]])
  1296. }
  1297. if(class(rphe)=='list'){rphe <- as.data.frame(rphe,stringsAsFactors=FALSE); rownames(rphe) <- colnames(rmat)}
  1298. reset <- generate.eset(rmat,phenotype_info = rphe, annotation_info = 'combine')
  1299. return(reset)
  1300. }
  1301. #' Reassign featureData slot of ExpressionSet and Update feature information
  1302. #'
  1303. #' \code{update_eset.feature} reassigns the featureData slot of ExpressionSet object based on user's demand. It is mainly used for gene ID conversion.
  1304. #'
  1305. #' User can pass a conversion table to \code{use_eset} for the ID conversion. A conversion table can be obtained from the original featureDta slot
  1306. #' (if one called the \code{load.exp.GEO} function and set getGPL==TRUE) or by running the \code{get_IDtransfer} function.
  1307. #' The mapping between original ID and target ID can be summerised into 4 categories.
  1308. #' 1) One-to-one, simply replaces the original ID with target ID;
  1309. #' 2) Many-to-one, the expression value for the target ID will be merged from its original ID;
  1310. #' 3) One-to-many, the expression value for the original ID will be distributed to the matched target IDs;
  1311. #' 4) Many-to-many, apply part 3) first, then part 2).
  1312. #'
  1313. #' @param use_eset ExpressionSet class object.
  1314. #' @param use_feature_info data.frame, a data frame contains feature information, it can be obtained by calling \code{fData} function.
  1315. #' @param from_feature character, orginal ID. Must be one of the column names in \code{use_feature_info} and correctly characterize the \code{use_eset}'s row names.
  1316. #' @param to_feature character, target ID. Must be one of the column names in \code{use_feature_info}.
  1317. #' @param merge_method character, the agglomeration method to be used for merging gene expression value.
  1318. #' This should be one of, "median", "mean", "max" or "min". Default is "median".
  1319. #' @param distribute_method character, the agglomeration method to be used for distributing the gene expression value.
  1320. #' This should be one of, "mean" or "equal". Default is "equal".
  1321. #'
  1322. #' @return Return an ExressionSet object with updated feature information.
  1323. #' @examples
  1324. #' mat1 <- matrix(rnorm(10000),nrow=1000,ncol=10)
  1325. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  1326. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  1327. #' eset <- generate.eset(exp_mat=mat1)
  1328. #' test_transfer_table <- data.frame(
  1329. #' 'Gene'=c('Gene1','Gene1','Gene2','Gene3','Gene4'),
  1330. #' 'Transcript'=c('T11','T12','T2','T3','T3'))
  1331. #' new_eset <- update_eset.feature(use_eset=eset,
  1332. #' use_feature_info=test_transfer_table,
  1333. #' from_feature='Gene',
  1334. #' to_feature='Transcript',
  1335. #' merge_method='median',
  1336. #' distribute_method='equal'
  1337. #' )
  1338. #' print(Biobase::exprs(eset)[test_transfer_table$Gene,])
  1339. #' print(Biobase::exprs(new_eset))
  1340. #'
  1341. #' @export update_eset.feature
  1342. update_eset.feature <- function(use_eset=NULL,use_feature_info=NULL,from_feature=NULL,to_feature=NULL,
  1343. merge_method='median',distribute_method='equal'){
  1344. #
  1345. all_input_para <- c('use_eset','from_feature','to_feature')
  1346. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1347. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1348. check_res <- c(check_option('merge_method',c("median","mean","max","min"),envir=environment()),
  1349. check_option('distribute_method',c('mean','equal'),envir=environment()))
  1350. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1351. #
  1352. if(is.null(use_feature_info)) use_feature_info <- Biobase::fData(use_eset)
  1353. n1 <- colnames(use_feature_info)
  1354. if(!from_feature %in% n1){
  1355. message(sprintf('%s not in in the colnames of use_feature_info, please re-try!',from_feature));return(use_eset)
  1356. }
  1357. if(!to_feature %in% n1){
  1358. message(sprintf('%s not in in the colnames of use_feature_info, please re-try!',to_feature));return(use_eset)
  1359. }
  1360. mat <- Biobase::exprs(use_eset)
  1361. use_feature_info <- base::unique(use_feature_info);
  1362. w1 <- which(use_feature_info[,1]!="" & use_feature_info[,2]!="" & is.na(use_feature_info[,1])==FALSE & is.na(use_feature_info[,2])==FALSE)
  1363. use_feature_info <- use_feature_info[w1,]
  1364. g1 <- rownames(mat) ## rownames for the expmat
  1365. f1 <- clean_charVector(use_feature_info[,from_feature]) ## from feature info
  1366. t1 <- clean_charVector(use_feature_info[,to_feature]) ## to feature info
  1367. w1 <- which(f1 %in% g1); f1 <- f1[w1]; t1 <- t1[w1]; ## only consider features in the rownames of expmat
  1368. if(base::length(w1)==0){
  1369. message(sprintf('Rownames of the expression matrix was not included in the %s column, please check and re-try !',from_feature))
  1370. return(use_eset)
  1371. }
  1372. message(sprintf('%d transfer pairs related with %d rows from original expression matrix will be keeped !',base::length(w1),base::length(g1)))
  1373. fc1 <- base::table(f1); tc1 <- base::table(t1); fw1 <- which(fc1>1); tw1 <- which(tc1>1); ## check duplicate records
  1374. if(base::length(fw1)>0){
  1375. message(sprintf('Original feature %s has %d items with duplicate records, will distribute the original values equal to all related items !
  1376. if do not want this, please check and retry !',from_feature,base::length(fw1)))
  1377. #return(use_eset)
  1378. w2 <- which(f1 %in% names(fw1)) ## need to distribute
  1379. w0 <- base::setdiff(1:base::length(f1),w2) ## do not need to distribute
  1380. if(distribute_method=='equal'){
  1381. v1 <- mat[f1[w2],]; ## distribute equal
  1382. }
  1383. if(distribute_method=='mean'){
  1384. v1 <- mat[f1[w2],]; ## distribute mean
  1385. tt <- as.numeric(base::table(f1[w2])[f1[w2]])
  1386. v1 <- v1/tt;
  1387. }
  1388. rownames(v1) <- paste0(f1[w2],'-',t1[w2]);
  1389. f1[w2] <- paste0(f1[w2],'-',t1[w2]); # update transfer table
  1390. mat <- base::rbind(v1,mat[f1[w0],]) # update mat table
  1391. fc1 <- base::table(f1); tc1 <- base::table(t1); fw1 <- which(fc1>1); tw1 <- which(tc1>1); ## update f1, t1 and related values
  1392. }
  1393. if(base::length(tw1)>0){
  1394. w2 <- which(t1 %in% names(tw1)) ## need to merge
  1395. w0 <- base::setdiff(1:base::length(t1),w2) ## do not need to merge
  1396. mat_new_0 <- mat[f1[w0],]; rownames(mat_new_0) <- t1[w0] ## mat do not need to merge
  1397. if(merge_method=='mean') tmp1 <- stats::aggregate(mat[f1[w2],,drop=FALSE],list(t1[w2]),function(x){base::mean(x,na.rm=TRUE)})
  1398. if(merge_method=='median') tmp1 <- stats::aggregate(mat[f1[w2],,drop=FALSE],list(t1[w2]),function(x){stats::median(x,na.rm=TRUE)})
  1399. if(merge_method=='max') tmp1 <- stats::aggregate(mat[f1[w2],,drop=FALSE],list(t1[w2]),function(x){base::max(x,na.rm=TRUE)})
  1400. if(merge_method=='min') tmp1 <- stats::aggregate(mat[f1[w2],,drop=FALSE],list(t1[w2]),function(x){base::min(x,na.rm=TRUE)})
  1401. mat_new_1 <- tmp1[,-1]; rownames(mat_new_1) <- tmp1[,1] ## mat merged
  1402. mat_new <- base::rbind(mat_new_0,mat_new_1)
  1403. }else{
  1404. mat_new <- mat[f1,]
  1405. rownames(mat_new) <- t1
  1406. }
  1407. new_eset <- generate.eset(exp_mat=mat_new, phenotype_info=Biobase::pData(use_eset), feature_info=NULL, annotation_info=annotation(use_eset))
  1408. return(new_eset)
  1409. }
  1410. #' Reassign the phenoData slot of ExpressionSet and Update phenotype information
  1411. #'
  1412. #' \code{update_eset.phenotype} reassigns the phenoData slot of ExpressionSet based on user's demand.
  1413. #' It is mainly used to modify sample names and extract interested phenotype information for further sample clustering.
  1414. #'
  1415. #' @param use_eset ExpressionSet class object.
  1416. #' @param use_phenotype_info data.frame, a dataframe contains phenotype information, can be obtained by calling \code{pData} function.
  1417. #' @param use_sample_col character, must be one of the column names in \code{use_phenotype_info}.
  1418. #' @param use_col character, the columns will be kept from \code{use_phenotype_info}.
  1419. #' 'auto', only extracting 'cluster-meaningful' sample features (e.g. it is meaningless to use 'gender' as clustering feature, if all samples are female).
  1420. #' 'GEO-auto' means it will extract the following selected columns,
  1421. #' "geo_accession", "title", "source_name_ch1", and columns ended with ":ch1". Default is "auto".
  1422. #' @return Return an ExressionSet object with updated phenotype information.
  1423. #' @examples
  1424. #' \dontrun{
  1425. #' net_eset <- load.exp.GEO(out.dir='./test',
  1426. #' GSE='GSE116028',
  1427. #' GPL='GPL6480',
  1428. #' getGPL=TRUE,
  1429. #' update=FALSE)
  1430. #' net_eset <- update_eset.phenotype(use_eset=net_eset,
  1431. #' use_phenotype_info=Biobase::pData(net_eset),
  1432. #' use_sample_col='geo_accession',
  1433. #' use_col='GEO-auto')
  1434. #' }
  1435. #' @export update_eset.phenotype
  1436. update_eset.phenotype <- function(use_eset=NULL,use_phenotype_info=NULL,use_sample_col=NULL,use_col='auto'){
  1437. #
  1438. all_input_para <- c('use_eset')
  1439. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1440. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1441. #
  1442. if(is.null(use_eset)){
  1443. message('use_eset required, please re-try !');
  1444. return(use_eset)
  1445. }
  1446. if(is.null(use_phenotype_info)) use_phenotype_info <- Biobase::pData(use_eset)
  1447. if(is.null(use_sample_col)==FALSE){
  1448. if(!use_sample_col %in% colnames(use_phenotype_info)){
  1449. stop(sprintf('%s not in the colnames of use_phenotype_info, please re-try!',use_sample_col));#return(use_eset)
  1450. }
  1451. }
  1452. if(is.null(use_col)) use_col <- colnames(use_phenotype_info)
  1453. mat <- Biobase::exprs(use_eset)
  1454. s1 <- colnames(mat) ## all samples
  1455. if(is.null(use_sample_col)==TRUE){
  1456. p1 <- rownames(use_phenotype_info)
  1457. }else{
  1458. p1 <- use_phenotype_info[,use_sample_col] ## sample in the phenotype info
  1459. }
  1460. w1 <- which(p1 %in% s1); p1 <- p1[w1]; ## only consider samples in the colnames of expmat
  1461. if(base::length(w1)==0){
  1462. if(is.null(use_sample_col)==TRUE){
  1463. message('Colnames of the expression matrix was not included in the rownames of use_phenotype_info, please check and re-try !')
  1464. }else{
  1465. message(sprintf('Colnames of the expression matrix was not included in the %s column, please check and re-try !',use_sample_col))
  1466. }
  1467. return(use_eset)
  1468. }
  1469. message(sprintf('%d out of %d samples from the expression matrix will be keeped !',base::length(w1),base::length(s1)))
  1470. mat_new <- mat[,p1]
  1471. use_phenotype_info <- use_phenotype_info[w1,]
  1472. n1 <- colnames(use_phenotype_info)
  1473. if(use_col[1] == 'GEO-auto'){
  1474. w1 <- c('geo_accession','title','source_name_ch1',n1[grep(':ch1',n1)])
  1475. p1 <- use_phenotype_info[,w1]
  1476. colnames(p1)[4:ncol(p1)] <- gsub('(.*):ch1','\\1',colnames(p1)[4:ncol(p1)])
  1477. colnames(p1)[3] <- gsub('(.*)_ch1','\\1',colnames(p1)[3])
  1478. if(is.null(use_sample_col)==FALSE) rownames(p1) <- use_phenotype_info[,use_sample_col]
  1479. if(base::length(w1)>1) p1 <- as.data.frame(apply(p1,2,clean_charVector),stringsAsFactors=FALSE)
  1480. if(base::length(w1)==1) p1 <- as.data.frame(clean_charVector(p1),stringsAsFactors=FALSE)
  1481. new_phenotype_info <- p1;
  1482. }else{
  1483. if(use_col[1] == 'auto'){
  1484. u1 <- apply(use_phenotype_info,2,function(x)base::length(base::unique(x)))
  1485. w1 <- which(u1>=2 & u1<=nrow(use_phenotype_info)-1)
  1486. if(base::length(w1)==0){
  1487. message('No column could match the auto criteria, please check and re-try!');return(FALSE)
  1488. }
  1489. p1 <- use_phenotype_info[,w1]
  1490. if(base::length(w1)>1) p1 <- as.data.frame(apply(p1,2,clean_charVector),stringsAsFactors=FALSE)
  1491. if(base::length(w1)==1) p1 <- as.data.frame(clean_charVector(p1),stringsAsFactors=FALSE)
  1492. new_phenotype_info <- use_phenotype_info[,w1];names(new_phenotype_info) <- names(use_phenotype_info)[w1];
  1493. }else{
  1494. if(base::length(base::setdiff(use_col,n1))>0){
  1495. message(sprintf('%s not in use_phenotype_info, please re-try!',base::paste(base::setdiff(use_col,n1),collapse=';')));return(FALSE)
  1496. }
  1497. p1 <- use_phenotype_info[,use_col]
  1498. if(base::length(use_col)>1) p1 <- as.data.frame(apply(p1,2,clean_charVector),stringsAsFactors=FALSE)
  1499. if(base::length(use_col)==1) p1 <- as.data.frame(clean_charVector(p1),stringsAsFactors=FALSE)
  1500. new_phenotype_info <- p1; names(new_phenotype_info) <- use_col;
  1501. }
  1502. }
  1503. rownames(new_phenotype_info) <- rownames(use_phenotype_info)
  1504. #print(new_phenotype_info)
  1505. message(sprintf('%d out of %d sample features will be keeped !',ncol(new_phenotype_info),ncol(use_phenotype_info)))
  1506. new_eset <- generate.eset(exp_mat=mat_new, phenotype_info=new_phenotype_info, feature_info=Biobase::fData(use_eset), annotation_info=annotation(use_eset))
  1507. return(new_eset)
  1508. }
  1509. #' IQR (interquartile range) filter to extract genes from expression matrix
  1510. #'
  1511. #' \code{IQR.filter} is a function to extract genes from the expression matrix by setting threshold to their IQR value.
  1512. #' IQR (interquartile range) is a measure of statistical dispersion. It is calculated for each gene across all the samples.
  1513. #' By setting threshold value, genes with certain statistical dispersion across samples will be filtered out.
  1514. #' This step is mainly used to perform sample cluster and to prepare the input for SJAracne.
  1515. #'
  1516. #' @param exp_mat matrix, the gene expression matrix. Each row represents a gene/transcript/probe, each column represents a sample.
  1517. #' @param use_genes a vector of characters, the gene list needed to be filtered. Default is the row names of \code{exp_mat}.
  1518. #' @param thre numeric, the threshold for IQR of the genes in \code{use_genes}. Default is 0.5.
  1519. #' @param loose_gene a vector of characters, the gene list that only need to pass the \code{loose_thre}.
  1520. #' This parameter is designed for the input of possible drivers used in SJAracne. Default is NULL.
  1521. #' @param loose_thre numeric, the threshold for IQR of the genes in \code{loose_gene}. Default is 0.1.
  1522. #' @return Return a vector with logical values indicate which genes should be kept.
  1523. #' @examples
  1524. #' mat1 <- matrix(rnorm(15000),nrow=1500,ncol=10)
  1525. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  1526. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  1527. #' choose1 <- IQR.filter(mat1,thre=0.5,
  1528. #' loose_gene=paste0('Gene',1:100))
  1529. #' @export
  1530. IQR.filter <- function(exp_mat,use_genes=rownames(exp_mat),thre = 0.5,loose_gene=NULL,loose_thre=0.1) {
  1531. #
  1532. all_input_para <- c('exp_mat','use_genes','thre')
  1533. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1534. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1535. #
  1536. use_genes <- base::intersect(use_genes,rownames(exp_mat))
  1537. use_genes <- base::setdiff(use_genes,"")
  1538. exp_mat <- exp_mat[use_genes,]
  1539. iqr <- apply(exp_mat, 1, stats::IQR) ## calculate IQR for each gene
  1540. choose0 <- use_genes[iqr > quantile(iqr, loose_thre)] ## for loose_gene
  1541. choose1 <- use_genes[iqr > quantile(iqr, thre)] ## for all genes
  1542. choose2 <- base::unique(c(base::intersect(loose_gene, choose0), choose1)) ## union set
  1543. use_vec <- rep(FALSE,length.out=base::length(use_genes));names(use_vec) <- use_genes
  1544. use_vec[choose2] <- TRUE
  1545. print(base::table(use_vec))
  1546. return(use_vec)
  1547. }
  1548. #' Normalization of RNA-Seq Reads Count
  1549. #'
  1550. #' \code{RNASeqCount.normalize.scale} is a simple version to normalize the RNASeq reads count data.
  1551. #'
  1552. #' Users can also load \code{load.exp.RNASeq.demo}, and follow the \code{DESeq2} pipeline for RNASeq data processing.
  1553. #' Warning, \code{load.exp.RNASeq.demo} and \code{load.exp.RNASeq.demoSalmon} in NetBID2 may not cover all the possible scenarios.
  1554. #'
  1555. #' @param mat matrix, matrix of RNA-Seq reads data. Each row is a gene/transcript, each column is a sample.
  1556. #' @param total integer, total RNA-Seq reads count. If NULL, will use the mean of each column's summation. Default is NULL.
  1557. #' @param pseudoCount integer, the integer added to avoid "-Inf" showing up during log transformation. Default is 1.
  1558. #'
  1559. #' @return Return a numeric matrix, containing the normalized RNA-Seq reads count.
  1560. #'
  1561. #' @examples
  1562. #' mat1 <- matrix(rnbinom(10000, mu = 10, size = 1),nrow=1000,ncol=10)
  1563. #' colnames(mat1) <- paste0('Sample1',1:ncol(mat1))
  1564. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  1565. #' norm_mat1 <- RNASeqCount.normalize.scale(mat1)
  1566. #' @export
  1567. RNASeqCount.normalize.scale <- function(mat,
  1568. total = NULL,
  1569. pseudoCount = 1) {
  1570. #
  1571. all_input_para <- c('mat','pseudoCount')
  1572. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1573. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1574. #
  1575. d <- mat
  1576. if (!is.data.frame(d))
  1577. d <- data.frame(d)
  1578. if (!all(d > 0))
  1579. d <- d + pseudoCount
  1580. s <- apply(d, 2, sum)
  1581. m <-
  1582. ifelse(is.null(total), as.integer(base::mean(s)), as.integer(total)) ## total or mean sum
  1583. options(digits = 2 + nchar(m))
  1584. fac <- m / s
  1585. for (i in 1:base::length(s)) {
  1586. d[, i] <- d[, i] * fac[i]
  1587. #d[, i] <- round(d[, i] * fac[i], 0)
  1588. }
  1589. if (!all(d > 0))
  1590. d <- d + pseudoCount
  1591. d
  1592. }
  1593. ## inner function for dist2
  1594. dist2.mod <- function (x, fun = function(a, b) base::mean(abs(a - b), na.rm = TRUE),
  1595. diagonal = 0)
  1596. {
  1597. if (!(is.numeric(diagonal) && (base::length(diagonal) == 1)))
  1598. stop("'diagonal' must be a numeric scalar.")
  1599. if (missing(fun)) {
  1600. res = apply(x, 2, function(w) base::colMeans(abs(x - w), na.rm = TRUE))
  1601. }
  1602. else {
  1603. res = matrix(diagonal, ncol = ncol(x), nrow = ncol(x))
  1604. if (ncol(x) >= 2) {
  1605. for (j in 2:ncol(x)) for (i in 1:(j - 1)) res[i,
  1606. j] = res[j, i] = fun(x[, i], x[, j])
  1607. }
  1608. }
  1609. colnames(res) = rownames(res) = colnames(x)
  1610. return(res)
  1611. }
  1612. ########################### activity-related functions
  1613. ## functions for activity score calculation, mean, absmean, maxmean, weighted mean ?
  1614. es <- function(z, es.method = "mean") {
  1615. if (es.method == "maxmean") {
  1616. n <- base::length(z)
  1617. m1 <- ifelse(sum(z > 0) > 0, sum(z[z > 0]) / n, 0)
  1618. m2 <- ifelse(sum(z < 0) > 0, sum(z[z < 0]) / n, 0)
  1619. if (m1 > -m2)
  1620. es <- m1
  1621. else
  1622. es <- m2
  1623. }
  1624. else if (es.method == 'absmean') {
  1625. es <- base::mean(abs(z),na.rm=TRUE)
  1626. }
  1627. else if (es.method == 'mean') {
  1628. es <- base::mean(z,na.rm=TRUE)
  1629. }
  1630. else if (es.method == 'median') {
  1631. es <- stats::median(z,na.rm=TRUE)
  1632. }
  1633. else if (es.method == 'max') {
  1634. es <- base::max(z,na.rm=TRUE)
  1635. }
  1636. else if (es.method == 'min') {
  1637. es <- base::min(z,na.rm=TRUE)
  1638. }
  1639. return(es)
  1640. }
  1641. do.std <- function(x) {
  1642. x <- x[!is.na(x)]
  1643. (x - base::mean(x,na.rm=TRUE)) / sd(x,na.rm=TRUE)
  1644. }
  1645. #' Calculate Activity Value for Each Driver
  1646. #'
  1647. #' \code{cal.Activity} calculates the activity value for each driver.
  1648. #' This function requires two inputs, the driver-to-target list object \code{target_list} and the expression matrix.
  1649. #'
  1650. #' @param target_list list, the driver-to-target list object. Either igraph_obj or target_list is necessary for this function.
  1651. #' The names of the list elements are drivers.
  1652. #' Each element is a data frame, usually contains at least three columns.
  1653. #' "target", target gene names;
  1654. #' "MI", mutual information;
  1655. #' "spearman", spearman correlation coefficient.
  1656. #' "MI" and "spearman" is necessary if es.method="weightedmean".
  1657. #' Users can call \code{get.SJAracne.network} to get this list from the network file generated by SJAracne (the second element of the return list)
  1658. #' or prepare the list object by hand but should match the data format described above.
  1659. #' @param igraph_obj igraph object, optional. Either igraph_obj or target_list is necessary for this function.
  1660. #' Users can call \code{get.SJAracne.network} to get this object from the network file generated by SJAracne (the third element of the return list),
  1661. #' or prepare the igraph network object by hand (Directed network and the edge attributes should include "weight" and "sign" if es.method="weightedmean").
  1662. #' @param cal_mat numeric matrix, the expression matrix of genes/transcripts.
  1663. #' @param es.method character, method applied to calculate the activity value. User can choose from "mean", "weightedmean", "maxmean" and "absmean".
  1664. #' Default is "weightedmean".
  1665. #' @param std logical, if TRUE, the expression matrix will be normalized by column. Default is TRUE.
  1666. #' @param memory_constrain logical, if TRUE, the calculation strategy will not use Matrix Cross Products, which is memory consuming.
  1667. #' Default is FALSE.
  1668. #' @return Return a matrix of activity values. Rows are drivers, columns are samples.
  1669. #' @examples
  1670. #' analysis.par <- list()
  1671. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  1672. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  1673. #' ac_mat <- cal.Activity(target_list=analysis.par$merge.network$target_list,
  1674. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),
  1675. #' es.method='weightedmean')
  1676. #' ac_mat <- cal.Activity(igraph_obj=analysis.par$merge.network$igraph_obj,
  1677. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),
  1678. #' es.method='maxmean')
  1679. #' @export
  1680. cal.Activity <- function(target_list=NULL, igraph_obj = NULL, cal_mat=NULL, es.method = 'weightedmean',std=TRUE,memory_constrain=FALSE) {
  1681. #
  1682. all_input_para <- c('cal_mat','es.method','std','memory_constrain')
  1683. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1684. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1685. check_res <- c(check_option('memory_constrain',c(TRUE,FALSE),envir=environment()),
  1686. check_option('std',c(TRUE,FALSE),envir=environment()),
  1687. check_option('es.method',c('mean','weightedmean','maxmean','absmean'),envir=environment()))
  1688. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1689. #
  1690. if(is.null(target_list)==TRUE & is.null(igraph_obj)==TRUE){
  1691. message('Either target_list or igraph_obj is required, please check and re-try!');return(FALSE);
  1692. }
  1693. if(is.null(target_list)==FALSE & memory_constrain==TRUE){
  1694. ac.mat <- cal.Activity.old(target_list=target_list, cal_mat=cal_mat, es.method = es.method,std=std)
  1695. return(ac.mat)
  1696. }
  1697. if(is.null(target_list)==TRUE & memory_constrain==TRUE){
  1698. message('Only accepts target_list input when memory_constrain=TRUE, please check and re-try!');return(FALSE);
  1699. }
  1700. if(nrow(cal_mat)==0){
  1701. message('No genes in the cal_mat, please check and re-try!');return(FALSE);
  1702. }
  1703. if(std==TRUE) cal_mat <- apply(cal_mat, 2, do.std)
  1704. if(is.null(igraph_obj)==FALSE){
  1705. gr <- igraph_obj
  1706. mat1 <- get_igraph2matrix(gr,es.method=es.method)
  1707. mat2 <- get_igraph2matrix(gr,es.method='mean')
  1708. all_source <- get_gr2driver(gr)
  1709. }else{
  1710. if(is.null(target_list)==FALSE){
  1711. mat1 <- get_target_list2matrix(target_list,es.method=es.method)
  1712. mat2 <- get_target_list2matrix(target_list,es.method='mean')
  1713. all_source <- names(target_list)
  1714. }
  1715. }
  1716. ##
  1717. mat1_source <- mat1[all_source,,drop=FALSE]
  1718. w1 <- base::intersect(rownames(cal_mat),colnames(mat1_source))
  1719. if(base::length(w1)==0){
  1720. message('No intersected genes found for the cal_mat and target in the network, please check and re-try!');
  1721. return(FALSE)
  1722. }
  1723. use_mat1_source <- mat1_source[,w1,drop=FALSE] ## network info
  1724. use_mat2_source <- mat2[all_source,w1,drop=FALSE] ## network binary info
  1725. ## weighted mean + mean
  1726. if(es.method %in% c('weightedmean','mean')){
  1727. use_cal_mat <- cal_mat[w1,,drop=FALSE] ## expression info
  1728. out_mat <- use_mat1_source %*% use_cal_mat
  1729. out_mat <- out_mat/Matrix::rowSums(use_mat2_source) ## get mean
  1730. }
  1731. ## absmean
  1732. if(es.method == 'absmean'){
  1733. use_cal_mat <- cal_mat[w1,,drop=FALSE] ## expression info
  1734. out_mat <- use_mat1_source %*% abs(use_cal_mat)
  1735. out_mat <- out_mat/Matrix::rowSums(use_mat2_source) ## get mean
  1736. }
  1737. ## maxmean
  1738. if(es.method == 'maxmean'){
  1739. use_cal_mat <- cal_mat[w1,,drop=FALSE] ## expression info
  1740. use_cal_mat_pos <- use_cal_mat;use_cal_mat_pos[which(use_cal_mat_pos<0)] <- 0;
  1741. use_cal_mat_neg <- use_cal_mat;use_cal_mat_neg[which(use_cal_mat_neg>0)] <- 0;
  1742. out_mat_pos <- use_mat1_source %*% use_cal_mat_pos
  1743. out_mat_pos <- out_mat_pos/Matrix::rowSums(use_mat2_source) ## get mean
  1744. out_mat_neg <- use_mat1_source %*% use_cal_mat_neg
  1745. out_mat_neg <- out_mat_neg/Matrix::rowSums(use_mat2_source) ## get mean
  1746. out_mat_sign <- sign(abs(out_mat_pos)-abs(out_mat_neg))
  1747. out_mat_sign_pos <- out_mat_sign; out_mat_sign_pos[out_mat_sign_pos!=1] <-0;
  1748. out_mat_sign_neg <- out_mat_sign; out_mat_sign_neg[out_mat_sign_neg!= -1] <-0;
  1749. out_mat <- out_mat_pos*out_mat_sign_pos-out_mat_neg*out_mat_sign_neg
  1750. }
  1751. ## median, min, max , not supported
  1752. # output
  1753. ac.mat <- as.matrix(out_mat)
  1754. w1 <- which(is.na(ac.mat[,1])==FALSE)
  1755. if(base::length(w1)==0){
  1756. message('Fail in calculating activity, please check the ID type in cal_mat and target_list and try again !')
  1757. }
  1758. ac.mat <- ac.mat[w1,,drop=FALSE]
  1759. return(ac.mat)
  1760. }
  1761. ## inner functions
  1762. cal.Activity.old <- function(target_list=NULL, cal_mat=NULL, es.method = 'weightedmean',std=TRUE) {
  1763. ## mean, absmean, maxmean, weightedmean
  1764. use_genes <- row.names(cal_mat)
  1765. if(base::length(use_genes)==0){
  1766. message('No genes in the cal_mat, please check and re-try!');return(FALSE);
  1767. }
  1768. all_target <- target_list
  1769. #all_target <- all_target[base::intersect(use_genes, names(all_target))] ## if the driver is not included in cal_mat but its target genes are included, will also calculate activity
  1770. ac.mat <-
  1771. matrix(NA, ncol = ncol(cal_mat), nrow = base::length(all_target)) ## generate activity matrix, each col for sample, each row for source target
  1772. #z-normalize each sample
  1773. if(std==TRUE) cal_mat <- apply(cal_mat, 2, do.std)
  1774. for (i in 1:base::length(all_target)) {
  1775. x <- names(all_target)[i]
  1776. x1 <- all_target[[x]]
  1777. x2 <- base::unique(base::intersect(rownames(x1), use_genes)) ## filter target by cal genes
  1778. x1 <- x1[x2, ] ## target info
  1779. target_num <- base::length(x2)
  1780. if (target_num == 0)
  1781. next
  1782. if (target_num == 1){
  1783. if (es.method != 'weightedmean') ac.mat[i, ] <- cal_mat[x2,] # 20230228
  1784. if (es.method == 'weightedmean') ac.mat[i, ] <- cal_mat[x2,]*x1$MI * sign(x1$spearman) # 20230228
  1785. next
  1786. }
  1787. if (es.method != 'weightedmean')
  1788. ac.mat[i, ] <- apply(cal_mat[x2,,drop=FALSE], 2, es, es.method)
  1789. if (es.method == 'weightedmean') {
  1790. weight <- x1$MI * sign(x1$spearman)
  1791. ac.mat[i, ] <- apply(cal_mat[x2,,drop=FALSE] * weight, 2, es, 'mean')
  1792. }
  1793. }
  1794. rownames(ac.mat) <- names(all_target)
  1795. colnames(ac.mat) <- colnames(cal_mat)
  1796. w1 <- which(is.na(ac.mat[,1])==FALSE)
  1797. if(base::length(w1)==0){
  1798. message('Fail in calculating activity, please check the ID type in cal_mat and target_list and try again !')
  1799. }
  1800. ac.mat <- ac.mat[w1,]
  1801. return(ac.mat)
  1802. }
  1803. get_target_list2matrix <- function(target_list=NULL,es.method = 'weightedmean') {
  1804. all_source <- names(target_list)
  1805. all_target <- base::unique(unlist(lapply(target_list,function(x)rownames(x))))
  1806. mat1 <- matrix(0,nrow=base::length(all_source),ncol=base::length(all_target))
  1807. rownames(mat1) <- all_source;
  1808. colnames(mat1) <- all_target;
  1809. for(i in all_source){
  1810. if(es.method!='weightedmean') mat1[i,rownames(target_list[[i]])] <- 1;
  1811. if(es.method=='weightedmean') mat1[i,rownames(target_list[[i]])] <- target_list[[i]]$MI*sign(target_list[[i]]$spearman);
  1812. }
  1813. return(mat1)
  1814. }
  1815. get_igraph2matrix <- function(gr=NULL,es.method = 'weightedmean'){
  1816. if(es.method=='weightedmean'){
  1817. if('weight' %in% igraph::edge_attr_names(gr) & 'sign' %in% igraph::edge_attr_names(gr)){
  1818. mat1 <- igraph::as_adjacency_matrix(gr,type='both',attr = 'weight')
  1819. mat2 <- igraph::as_adjacency_matrix(gr,type='both',attr = 'sign')
  1820. mat1 <- mat1*mat2
  1821. }else{
  1822. message('weight, sign attributes are not included in the input igraph object, weightedmean can not be used !')
  1823. return(FALSE)
  1824. }
  1825. }else{
  1826. mat1 <- igraph::as_adjacency_matrix(gr,type='both')
  1827. }
  1828. return(mat1)
  1829. }
  1830. get_gr2driver <- function(gr,mode='out'){
  1831. d1 <- igraph::degree(gr,mode=mode)
  1832. names(d1[which(d1>0)])
  1833. }
  1834. #' Clean Activity-based profile
  1835. #'
  1836. #' \code{processDriverProfile} is a helper function to pre-process Activity-based profile.
  1837. #'
  1838. #' @param Driver_profile a numeric vector, contain statistics for drivers
  1839. #' (e.g driver's target size, driver's Z-statistics)
  1840. #' @param Driver_name a character vector, contain name for drivers.
  1841. #' The length of `Driver_profile` and `Driver_name` must be equal and the order of item must match.
  1842. #' @param choose_strategy character, strategy of selection if duplicate driver name (e.g TP53_TF, TP53_SIG).
  1843. #' Choose from "min","max","absmin","absmax". Default is "min".
  1844. #' @param return_type character, strategy of return type.
  1845. #' Choose from "driver_name","gene_name", "driver_statistics", "gene_statistics".
  1846. #' If choose "*_name", only the name vector is returned.
  1847. #' If choose "*_statistics", the statistics vector is returned with character name.
  1848. #' Default is "driver_name".
  1849. #' @examples
  1850. #' analysis.par <- list()
  1851. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  1852. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  1853. #' ms_tab <- analysis.par$final_ms_tab
  1854. #' Driver_profile <- ms_tab$P.Value.G4.Vs.WNT_DA
  1855. #' Driver_name <- ms_tab$gene_label
  1856. #' res1 <- processDriverProfile(Driver_profile=Driver_profile,
  1857. #' Driver_name=Driver_name,
  1858. #' choose_strategy='min')
  1859. #' res2 <- processDriverProfile(Driver_profile=Driver_profile,
  1860. #' Driver_name=Driver_name,
  1861. #' return_type = 'gene_name',
  1862. #' choose_strategy='min')
  1863. #' Driver_profile <- ms_tab$Z.G4.Vs.WNT_DA
  1864. #' res3 <- processDriverProfile(Driver_profile=Driver_profile,
  1865. #' Driver_name=Driver_name,
  1866. #' choose_strategy='absmax',
  1867. #' return_type = 'driver_statistics')
  1868. #' res4 <- processDriverProfile(Driver_profile=Driver_profile,
  1869. #' Driver_name=Driver_name,
  1870. #' choose_strategy='absmax',
  1871. #' return_type = 'gene_statistics')
  1872. #' driver_size <- ms_tab$Size
  1873. #' res5 <- processDriverProfile(Driver_profile=driver_size,
  1874. #' Driver_name=Driver_name,
  1875. #' choose_strategy='max')
  1876. #' @export
  1877. processDriverProfile <- function(Driver_profile,Driver_name,
  1878. choose_strategy='min',
  1879. return_type='driver_name'){
  1880. all_input_para <- c('Driver_profile','Driver_name','choose_strategy')
  1881. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1882. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1883. check_res <- c(check_option('choose_strategy',c('min','max','absmin','absmax'),envir=environment()),
  1884. check_option('return_type',c('driver_name','gene_name','driver_statistics','gene_statistics'),envir=environment()))
  1885. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1886. ##
  1887. l1 <- length(Driver_profile)
  1888. l2 <- length(Driver_name)
  1889. if(l1!=l2 | l1==0 | l2==0){
  1890. message('Driver_profile and Driver_name need same length.
  1891. Please check Driver_profile, Driver_name, and re-try!');return(FALSE)
  1892. }
  1893. gene_name <- gsub('(.*)_(TF|SIG)',"\\1",Driver_name)
  1894. uni_gene_name <- unique(gene_name)
  1895. tmp1 <- lapply(uni_gene_name,function(x){
  1896. w1 <- which(gene_name == x)
  1897. x1 <- Driver_profile[w1]
  1898. x2 <- rank(x1)
  1899. x3 <- rank(abs(x1))
  1900. if(choose_strategy=='min'){w2 <- w1[which.min(x2)]}
  1901. if(choose_strategy=='max'){w2 <- w1[which.max(x2)]}
  1902. if(choose_strategy=='absmin'){w2 <- w1[which.min(x3)]}
  1903. if(choose_strategy=='absmax'){w2 <- w1[which.max(x3)]}
  1904. w2
  1905. })
  1906. remain_item <- unlist(tmp1)
  1907. if(return_type=='driver_name') new_vec <- Driver_name[remain_item]
  1908. if(return_type=='gene_name') new_vec <- gene_name[remain_item]
  1909. if(return_type=='driver_statistics'){new_vec <- Driver_profile[remain_item]; names(new_vec) <- Driver_name[remain_item]}
  1910. if(return_type=='gene_statistics'){new_vec <- Driver_profile[remain_item]; names(new_vec) <- gene_name[remain_item]}
  1911. return(new_vec)
  1912. }
  1913. #' Calculate Activity Value for Gene Sets
  1914. #'
  1915. #' \code{cal.Activity.GS} calculates activity value for each gene set, and return a numeric matrix with rows of gene sets and columns of samples.
  1916. #'
  1917. #' @param use_gs2gene list, contains elements of gene sets. Element name is gene set name, each element contains a vector of genes belong to that gene set.
  1918. #' Default is using \code{all_gs2gene[c('H','CP:BIOCARTA','CP:REACTOME','CP:KEGG_MEDICU')]}, which is loaded from \code{gs.preload}.
  1919. #' @param cal_mat numeric matrix, gene/transcript expression matrix.
  1920. #' If want to input activity matrix, need to use `processDriverProfile()` to pre-process the dataset.
  1921. #' Detailed could see demo.
  1922. #' @param es.method character, method to calculate the activity value. Users can choose from "mean", "absmean", "maxmean", "gsva", "ssgsea", "zscore" and "plage".
  1923. #' The details for using the last four options, users can check \code{gsva}. Default is "mean".
  1924. #' @param std logical, if TRUE, the expression matrix will be normalized by column. Default is TRUE.
  1925. #' @return Return an activity matrix with rows of gene sets and columns of samples.
  1926. #'
  1927. #' @examples
  1928. #' analysis.par <- list()
  1929. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  1930. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  1931. #' gs.preload(use_spe='Homo sapiens',update=FALSE)
  1932. #' use_gs2gene <- merge_gs(all_gs2gene=all_gs2gene,
  1933. #' use_gs=c('H','CP:BIOCARTA','CP:REACTOME','CP:KEGG_MEDICU','C5'))
  1934. #' exp_mat_gene <- Biobase::exprs(analysis.par$cal.eset)
  1935. #' ## each row is a gene symbol, if not, must convert ID first
  1936. #' ac_gs <- cal.Activity.GS(use_gs2gene = use_gs2gene,
  1937. #' cal_mat = exp_mat_gene)
  1938. #' ## if want to input activity-matrix
  1939. #' ac_mat <- cal.Activity(target_list=analysis.par$merge.network$target_list,
  1940. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),
  1941. #' es.method='weightedmean')
  1942. #' # pre-process the activity matrix by selecting the one
  1943. #' # with larger target size for duplicate drivers
  1944. #' Driver_name <- rownames(ac_mat)
  1945. #' ms_tab <- analysis.par$final_ms_tab
  1946. #' driver_size <- ms_tab[Driver_name,]$Size
  1947. #' use_driver <- processDriverProfile(Driver_profile=driver_size,
  1948. #' Driver_name=Driver_name,
  1949. #' choose_strategy='max',
  1950. #' return_type='driver_name')
  1951. #' use_driver_gene_name <-
  1952. #' processDriverProfile(Driver_profile=driver_size,
  1953. #' Driver_name=Driver_name,
  1954. #' choose_strategy='max',
  1955. #' return_type='gene_name')
  1956. #' ac_mat_gene <- ac_mat[use_driver,]
  1957. #' rownames(ac_mat_gene) <- use_driver_gene_name
  1958. #' driver_ac_gs <- cal.Activity.GS(use_gs2gene = use_gs2gene,
  1959. #' cal_mat = ac_mat_gene)
  1960. #' @export
  1961. cal.Activity.GS <- function(use_gs2gene=all_gs2gene[c('H','CP:BIOCARTA','CP:REACTOME','CP:KEGG_MEDICU')], cal_mat=NULL, es.method = 'mean',std=TRUE) {
  1962. #
  1963. all_input_para <- c('use_gs2gene','cal_mat','es.method','std')
  1964. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  1965. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1966. check_res <- c(check_option('std',c(TRUE,FALSE),envir=environment()),
  1967. check_option('es.method',c("mean","absmean","maxmean","gsva","ssgsea","zscore","plage"),envir=environment()))
  1968. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  1969. #
  1970. while(class(use_gs2gene[[1]])=='list'){
  1971. nn <- unlist(lapply(use_gs2gene,names))
  1972. use_gs2gene <- unlist(use_gs2gene,recursive = FALSE)
  1973. names(use_gs2gene)<-nn
  1974. }
  1975. use_genes <- row.names(cal_mat)
  1976. if(es.method %in% c('gsva','ssgsea','zscore','plage')){
  1977. ac.mat <- GSVA::gsva(generate.eset(cal_mat),use_gs2gene,method=es.method)
  1978. ac.mat <- Biobase::exprs(ac.mat)
  1979. return(ac.mat)
  1980. }
  1981. ac.mat <-
  1982. matrix(NA, ncol = ncol(cal_mat), nrow = base::length(use_gs2gene)) ## generate activity matrix, each col for sample, each row for source target
  1983. #z-normalize each sample
  1984. if(std==TRUE) cal_mat <- apply(cal_mat, 2, do.std)
  1985. for (i in 1:base::length(use_gs2gene)) {
  1986. x <- names(use_gs2gene)[i]
  1987. x1 <- use_gs2gene[[x]]
  1988. x2 <- base::unique(base::intersect(x1, use_genes)) ## filter target by cal genes
  1989. target_num <- base::length(x2)
  1990. if (target_num == 0)
  1991. next
  1992. if (target_num == 1){
  1993. ac.mat[i, ] <- cal_mat[x2,]
  1994. next
  1995. }
  1996. ac.mat[i, ] <- apply(cal_mat[x2,,drop=FALSE], 2, es, es.method)
  1997. }
  1998. rownames(ac.mat) <- names(use_gs2gene)
  1999. colnames(ac.mat) <- colnames(cal_mat)
  2000. w1 <- apply(ac.mat,1,function(x)base::length(which(is.na(x)==TRUE)))
  2001. ac.mat <- ac.mat[which(w1==0),,drop=FALSE]
  2002. return(ac.mat)
  2003. }
  2004. #' Differential Expression Analysis and Differential Activity Analysis Between 2 Sample Groups Using Bayesian Inference
  2005. #'
  2006. #' \code{getDE.BID.2G} is a function performs differential gene expression analysis and differential driver activity analysis between
  2007. #' control group (parameter G0) and experimental group (parameter G1).
  2008. #'
  2009. #' @param eset ExpressionSet class object, contains gene expression data or driver activity data.
  2010. #' @param output_id_column character, the column names of Biobase::fData(eset).
  2011. #' This option is useful when the \code{eset} expression matrix is at transcript-level, and user is expecting to see the gene-level comparison statistics.
  2012. #' If NULL, rownames of the Biobase::fData(eset) will be used.
  2013. #' Default is NULL.
  2014. #' @param G1 a vector of characters, the sample names of experimental group.
  2015. #' @param G0 a vecotr of characters, the sample names of control group.
  2016. #' @param G1_name character, the name of experimental group (e.g. "Male"). Default is "G1".
  2017. #' @param G0_name character, the name of control group (e.g. "Female"). Default is "G0".
  2018. #' @param logTransformed logical, if TRUE, log tranformation of the expression value will be performed.
  2019. #' @param method character, users can choose between "MLE" and "Bayesian".
  2020. #' "MLE", the maximum likelihood estimation, will call generalized linear model(glm/glmer) to perform data regression.
  2021. #' "Bayesian", will call Bayesian generalized linear model (bayesglm) or multivariate generalized linear mixed model (MCMCglmm) to perform data regression.
  2022. #' Default is "Bayesian".
  2023. #' @param family character or family function or the result of a call to a family function.
  2024. #' This parameter is used to define the model's error distribution. See \code{?family} for details.
  2025. #' Currently, options are gaussian, poisson, binomial(for two-group sample classes)/category(for multi-group sample classes)/ordinal(for multi-group sample classes with class_ordered=TRUE).
  2026. #' If set with gaussian or poission, the response variable in the regression model will be the expression level, and the independent variable will be the sample's phenotype.
  2027. #' If set with binomial, the response variable in the regression model will be the sample phenotype, and the independent variable will be the expression level.
  2028. #' For binomial, category and ordinal input, the family will be automatically reset, based on the sample's class level and the setting of \code{class_ordered}.
  2029. #' Default is gaussian.
  2030. #' @param pooling character, users can choose from "full","no" and "partial".
  2031. #' "full", use probes as independent observations.
  2032. #' "no", use probes as independent variables in the regression model.
  2033. #' "partial", use probes as random effect in the regression model.
  2034. #' Default is "full".
  2035. #' @param verbose logical, if TRUE, sample names of both groups will be printed. Default is TRUE.
  2036. #'
  2037. #' @return
  2038. #' Return a data frame. Rows are genes/drivers, columns are "ID", "logFC", "AveExpr", "t", "P.Value", "adj.P.Val", "Z-statistics", "Ave.G1" and "Ave.G0".
  2039. #' Names of the columns may vary from different group names. Sorted by P.Value.
  2040. #'
  2041. #' @examples
  2042. #' mat <- matrix(c(0.50099,-1.2108,-1.0524,
  2043. #' 0.34881,-0.13441,0.87112,
  2044. #' 1.84579,-2.0356,-2.6025,
  2045. #' 1.62954,1.88281,1.29604),nrow=2,byrow=TRUE)
  2046. #' rownames(mat) <- c('A1','A2')
  2047. #' colnames(mat) <- c('Case-rep1','Case-rep2','Case-rep3',
  2048. #' 'Control-rep1','Control-rep2','Control-rep3')
  2049. #' tmp_eset <- generate.eset(mat,feature_info=data.frame(row.names=rownames(mat),
  2050. #' probe=rownames(mat),gene=rep('GeneX',2),
  2051. #' stringsAsFactors = FALSE))
  2052. #' res1 <- getDE.BID.2G(tmp_eset,output_id_column='probe',
  2053. #' G1=c('Case-rep1','Case-rep2','Case-rep3'),
  2054. #' G0=c('Control-rep1','Control-rep2','Control-rep3'))
  2055. #' res2 <- getDE.BID.2G(tmp_eset,output_id_column='gene',
  2056. #' G1=c('Case-rep1','Case-rep2','Case-rep3'),
  2057. #' G0=c('Control-rep1','Control-rep2','Control-rep3'))
  2058. #' res3 <- getDE.BID.2G(tmp_eset,output_id_column='gene',
  2059. #' G1=c('Case-rep1','Case-rep2','Case-rep3'),
  2060. #' G0=c('Control-rep1','Control-rep2','Control-rep3'),
  2061. #' pooling='partial')
  2062. #' \dontrun{
  2063. #' analysis.par <- list()
  2064. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  2065. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  2066. #' phe_info <- Biobase::pData(analysis.par$cal.eset)
  2067. #' each_subtype <- 'G4'
  2068. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2069. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2070. #' DE_gene_BID <- getDE.BID.2G(eset=analysis.par$cal.eset,
  2071. #' G1=G1,G0=G0,
  2072. #' G1_name=each_subtype,
  2073. #' G0_name='other')
  2074. #' DA_driver_BID <- getDE.BID.2G(eset=analysis.par$merge.ac.eset,
  2075. #' G1=G1,G0=G0,
  2076. #' G1_name=each_subtype,
  2077. #' G0_name='other')
  2078. #' }
  2079. #' @export
  2080. getDE.BID.2G <-function(eset,output_id_column=NULL,G1=NULL, G0=NULL,G1_name=NULL,G0_name=NULL,method='Bayesian',family=gaussian,pooling='full',logTransformed=TRUE,verbose=TRUE){
  2081. #
  2082. all_input_para <- c('eset','G1','G0','method','family','pooling','logTransformed','verbose')
  2083. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2084. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2085. check_res <- c(check_option('logTransformed',c(TRUE,FALSE),envir=environment()),
  2086. check_option('verbose',c(TRUE,FALSE),envir=environment()),
  2087. check_option('method',c('Bayesian','MLE'),envir=environment()),
  2088. check_option('pooling',c('full','no','partial'),envir=environment()))
  2089. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2090. #
  2091. exp_mat <- Biobase::exprs(eset)
  2092. G1 <- base::intersect(G1,colnames(exp_mat))
  2093. G0 <- base::intersect(G0,colnames(exp_mat))
  2094. if(verbose==TRUE){
  2095. print(sprintf('G1:%s', base::paste(G1, collapse = ';')))
  2096. print(sprintf('G0:%s', base::paste(G0, collapse = ';')))
  2097. }
  2098. if(base::length(G1)==0 | base::length(G0)==0){
  2099. message('Too few samples, please check the sample name of G1, G0 and samples in eset !');return(FALSE);
  2100. }
  2101. #
  2102. rn <- rownames(exp_mat)
  2103. exp_mat <- exp_mat[,c(G1,G0)]
  2104. if(is.null(dim(exp_mat))==TRUE){exp_mat <- t(as.matrix(exp_mat));rownames(exp_mat)<-rn}
  2105. if(is.null(output_id_column)==TRUE) use_id <- rownames(Biobase::fData(eset)) else use_id <- Biobase::fData(eset)[,output_id_column]
  2106. comp <- c(rep(1,length.out=base::length(G1)),rep(0,length.out=base::length(G0)))
  2107. all_id <- base::unique(use_id)
  2108. de <- lapply(all_id,function(x){
  2109. w1 <- which(use_id==x)
  2110. x1 <- exp_mat[w1,,drop=FALSE]
  2111. bid(mat=x1,use_obs_class=comp,class_order=c(0,1),family=family,method=method,
  2112. nitt=13000,burnin=3000,thin=1,pooling=pooling,class_ordered=FALSE,
  2113. logTransformed=logTransformed,std=FALSE,average.method=c('geometric'),verbose=FALSE)
  2114. })
  2115. de <- as.data.frame(do.call(base::rbind,de))
  2116. de$adj.P.Val<-p.adjust(de$P.Value,'fdr')
  2117. de$logFC<-sign(de$FC)*log2(abs(de$FC))
  2118. de$ID <- all_id
  2119. de<-de[,c('ID','logFC','AveExpr','t','P.Value','adj.P.Val','Z-statistics')]
  2120. rownames(de) <- de$ID
  2121. tT <- de;
  2122. tmp1 <- stats::aggregate(exp_mat,list(use_id),mean)
  2123. new_mat <- tmp1[,-1];rownames(new_mat) <- tmp1[,1]
  2124. tT <- tT[rownames(new_mat),,drop=FALSE]
  2125. exp_G1 <- base::rowMeans(new_mat[,G1,drop=FALSE]);
  2126. exp_G0 <- base::rowMeans(new_mat[,G0,drop=FALSE]);
  2127. tT <- base::cbind(tT,'Ave.G0'=exp_G0,'Ave.G1'=exp_G1)
  2128. if(is.null(G0_name)==FALSE) colnames(tT) <- gsub('Ave.G0',paste0('Ave.',G0_name),colnames(tT))
  2129. if(is.null(G1_name)==FALSE) colnames(tT) <- gsub('Ave.G1',paste0('Ave.',G1_name),colnames(tT))
  2130. tT <- tT[order(tT$P.Value),]
  2131. return(tT)
  2132. }
  2133. #' Combine Multiple Comparison Results from Differential Expression (DE) or Differential Activity (DA) Analysis
  2134. #'
  2135. #' \code{combineDE} combines multiple comparisons of DE or DA analysis.
  2136. #' Can combine DE with DE, DA with DA and also DE with DA if proper transfer table prepared.
  2137. #'
  2138. #' For example, there are 4 subgroups in the phenotype, G1, G2, G3 and G4. One DE analysis was performed on G1 vs. G2, and another DE was performed on G1 vs. G3.
  2139. #' If user is interested in the DE analysis between G1 vs. (G2 and G3), he can call this function to combine the two comparison results above toghether.
  2140. #' The combined P values will be taken care by \code{combinePvalVector}.
  2141. #'
  2142. #' @param DE_list list, each element in the list is one DE/DA comparison need to be combined.
  2143. #' @param DE_name a vector of characters, the DE/DA comparison names.
  2144. #' If not NULL, it must match the names of DE_list in correct order.
  2145. #' If NULL, names of the DE_list will be used.
  2146. #' Default is NULL.
  2147. #' @param transfer_tab data.frame, the ID conversion table. Users can call \code{get_IDtransfer} to get this table.
  2148. #' The purpose is to correctly mapping ID for \code{DE_list}. The column names must match \code{DE_name}.
  2149. #' If NULL, ID column of each DE comparison will be considered as the same type.
  2150. #' Default is NULL.
  2151. #' @param main_id character, a name of the element in \code{DE_list}. The ID column of that comparison will be used as the ID of the final combination.
  2152. #' If NULL, the first element name from \code{DE_list} will be used. Default is NULL.
  2153. #' @param method character, users can choose between "Stouffer" and "Fisher". Default is "Stouffer".
  2154. #' @param twosided logical, if TRUE, a two-tailed test will be performed.
  2155. #' If FALSE, a one-tailed test will be performed, and P value falls within the range of 0 to 0.5. Default is TRUE.
  2156. #' @param signed logical, if TRUE, give a sign to the P value, which indicating the direction of testing.
  2157. #' Default is TRUE.
  2158. #' @return Return a list contains the combined DE/DA analysis. Each single comparison result before combination is wrapped inside
  2159. #' (may have with some IDs filtered out, due to the combination). A data frame named "combine" inside the list is the combined analysis.
  2160. #' Rows are genes/drivers, columns are combined statistics (e.g. "logFC", "AveExpr", "t", "P.Value" etc.).
  2161. #'
  2162. #' @examples
  2163. #' analysis.par <- list()
  2164. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  2165. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  2166. #' phe_info <- Biobase::pData(analysis.par$cal.eset)
  2167. #' each_subtype <- 'G4'
  2168. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2169. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2170. #' DE_gene_limma <- getDE.limma.2G(eset=analysis.par$cal.eset,
  2171. #' G1=G1,G0=G0,
  2172. #' G1_name=each_subtype,
  2173. #' G0_name='other')
  2174. #' DA_driver_limma <- getDE.limma.2G(eset=analysis.par$merge.ac.eset,
  2175. #' G1=G1,G0=G0,
  2176. #' G1_name=each_subtype,
  2177. #' G0_name='other')
  2178. #' DE_list <- list(DE=DE_gene_limma,DA=DA_driver_limma)
  2179. #' g1 <- gsub('(.*)_.*','\\1',DE_list$DA$ID)
  2180. #' transfer_tab <- data.frame(DE=g1,DA=DE_list$DA$ID,stringsAsFactors = FALSE)
  2181. #' res1 <- combineDE(DE_list,transfer_tab=transfer_tab,main_id='DA')
  2182. #'
  2183. #' \dontrun{
  2184. #' each_subtype <- 'G4'
  2185. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2186. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2187. #' DE_gene_limma_G4 <- getDE.limma.2G(eset=analysis.par$cal.eset,
  2188. #' G1=G1,G0=G0,
  2189. #' G1_name=each_subtype,
  2190. #' G0_name='other')
  2191. #' each_subtype <- 'SHH'
  2192. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2193. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2194. #' DE_gene_limma_SHH <- getDE.limma.2G(eset=analysis.par$cal.eset,
  2195. #' G1=G1,G0=G0,
  2196. #' G1_name=each_subtype,
  2197. #' G0_name='other')
  2198. #' DE_list <- list(G4=DE_gene_limma_G4,SHH=DE_gene_limma_SHH)
  2199. #' res2 <- combineDE(DE_list,transfer_tab=NULL)
  2200. #' }
  2201. #' @export
  2202. combineDE<-function(DE_list,DE_name=NULL,transfer_tab=NULL,main_id=NULL,method='Stouffer',twosided=TRUE,signed=TRUE){
  2203. #
  2204. all_input_para <- c('DE_list','method','twosided','signed')
  2205. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2206. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2207. check_res <- c(check_option('twosided',c(TRUE,FALSE),envir=environment()),
  2208. check_option('signed',c(TRUE,FALSE),envir=environment()),
  2209. check_option('method',c('Stouffer','Fisher'),envir=environment()))
  2210. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2211. #
  2212. nDE<-base::length(DE_list)
  2213. if(nDE<2) stop('At least two DE outputs are required for combineDE analysis!\n')
  2214. if(is.null(DE_name)==TRUE){
  2215. DE_name <- names(DE_list)
  2216. }
  2217. if(is.null(transfer_tab)==TRUE){
  2218. transfer_tab <- lapply(DE_list,function(x)x$ID)
  2219. names(transfer_tab) <- DE_name
  2220. w1 <- names(which(base::table(unlist(transfer_tab))==nDE))
  2221. if(base::length(w1)==0){message('No intersected IDs found between DE list, please check and re-try!');return(FALSE);}
  2222. transfer_tab <- do.call(base::cbind,lapply(transfer_tab,function(x)w1))
  2223. transfer_tab <- as.data.frame(transfer_tab,stringsAsFactors=FALSE)
  2224. }
  2225. w1 <- lapply(DE_name,function(x){
  2226. which(transfer_tab[[x]] %in% DE_list[[x]]$ID)
  2227. })
  2228. w11 <- which(base::table(unlist(w1))==nDE)
  2229. if(base::length(w11)==0){
  2230. message('No intersected IDs found between DE list, please check and re-try!');return(FALSE);
  2231. }
  2232. w2 <- as.numeric(names(w11))
  2233. transfer_tab <- transfer_tab[w2,]
  2234. combine_info <- lapply(DE_name,function(x1){
  2235. DE_list[[x1]][transfer_tab[[x1]],]
  2236. })
  2237. names(combine_info) <- DE_name
  2238. dd <- do.call(base::cbind,lapply(combine_info,function(x1){
  2239. x1$P.Value*sign(x1$logFC)
  2240. }))
  2241. res1 <- t(apply(dd,1,function(x){
  2242. combinePvalVector(x,method=method,signed=signed,twosided=twosided)
  2243. }))
  2244. res1 <- as.data.frame(res1)
  2245. if(is.null(main_id)==TRUE) main_id <- DE_name[1]
  2246. res1$adj.P.Val <- p.adjust(res1$P.Value,'fdr')
  2247. res1$logFC <- base::rowMeans(do.call(base::cbind,lapply(combine_info,function(x)x$logFC)))
  2248. res1$AveExpr <- base::rowMeans(do.call(base::cbind,lapply(combine_info,function(x)x$AveExpr)))
  2249. res1 <- base::cbind(ID=transfer_tab[,main_id],transfer_tab,res1,stringsAsFactors=FALSE)
  2250. rownames(res1) <- res1$ID
  2251. combine_info$combine <- res1
  2252. return(combine_info)
  2253. }
  2254. # inner function: class_label can be obtained by get_class
  2255. get_class2design <- function(class_label){
  2256. design <- model.matrix(~0+class_label);colnames(design) <- base::unique(class_label);
  2257. rownames(design) <- names(class_label)
  2258. return(design)
  2259. #design.mat <-as.data.frame(matrix(0, nrow = base::length(class_label), ncol = base::length(base::unique(class_label))))
  2260. #rownames(design.mat) <- names(class_label) ## sample
  2261. #colnames(design.mat) <- base::unique(class_label)
  2262. #for(i in 1:base::length(class_label)){design.mat[names(class_label)[i],class_label[i]]<-1;}
  2263. #return(design.mat)
  2264. }
  2265. #' Differential Expression Analysis and Differential Activity Analysis Between 2 Sample Groups Using Limma
  2266. #'
  2267. #' \code{getDE.limma.2G} is a function performs differential gene expression analysis and differential driver activity analysis
  2268. #' between control group (parameter G0) and experimental group (parameter G1), using limma related functions.
  2269. #'
  2270. #' @param eset ExpressionSet class object, contains gene expression data or driver activity data.
  2271. #' @param G1 a vector of characters, the sample names of experimental group.
  2272. #' @param G0 a vecotr of characters, the sample names of control group.
  2273. #' @param G1_name character, the name of experimental group (e.g. "Male"). Default is "G1".
  2274. #' @param G0_name character, the name of control group (e.g. "Female"). Default is "G0".
  2275. #' @param verbose logical, if TRUE, sample names of both groups will be printed. Default is TRUE.
  2276. #' @param random_effect a vector of characters, vector or factor specifying a blocking variable.
  2277. #' Default is NULL, no random effect will be considered.
  2278. #'
  2279. #' @return
  2280. #' Return a data frame. Rows are genes/drivers, columns are "ID", "logFC", "AveExpr", "t", "P.Value", "adj.P.Val", "B", "Z-statistics", "Ave.G1" and "Ave.G0".
  2281. #' Names of the columns may vary from different group names. Sorted by P-values.
  2282. #'
  2283. #'
  2284. #' @examples
  2285. #' analysis.par <- list()
  2286. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  2287. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  2288. #' phe_info <- Biobase::pData(analysis.par$cal.eset)
  2289. #' each_subtype <- 'G4'
  2290. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2291. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2292. #' DE_gene_limma <- getDE.limma.2G(eset=analysis.par$cal.eset,
  2293. #' G1=G1,G0=G0,
  2294. #' G1_name=each_subtype,
  2295. #' G0_name='other')
  2296. #' DA_driver_limma <- getDE.limma.2G(eset=analysis.par$merge.ac.eset,
  2297. #' G1=G1,G0=G0,
  2298. #' G1_name=each_subtype,
  2299. #' G0_name='other')
  2300. #' @export
  2301. getDE.limma.2G <- function(eset=NULL, G1=NULL, G0=NULL,G1_name=NULL,G0_name=NULL,verbose=TRUE,random_effect=NULL) {
  2302. #
  2303. all_input_para <- c('eset','G1','G0','verbose')
  2304. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2305. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2306. check_res <- c(check_option('verbose',c(TRUE,FALSE),envir=environment()))
  2307. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2308. #
  2309. exp_mat <- Biobase::exprs(eset)
  2310. G1 <- base::intersect(G1,colnames(exp_mat))
  2311. G0 <- base::intersect(G0,colnames(exp_mat))
  2312. if(verbose==TRUE){
  2313. print(sprintf('G1:%s', base::paste(G1, collapse = ';')))
  2314. print(sprintf('G0:%s', base::paste(G0, collapse = ';')))
  2315. }
  2316. if(base::length(G1)==0 | base::length(G0)==0){
  2317. message('Too few samples, please check the sample name of G1, G0 and samples in eset !');return(FALSE);
  2318. }
  2319. #
  2320. all_samples <- colnames(Biobase::exprs(eset))
  2321. use_samples <- c(G0, G1)
  2322. phe <- as.data.frame(Biobase::pData(eset)[use_samples, ,drop=FALSE]);
  2323. rownames(phe) <- use_samples
  2324. new_eset <- generate.eset(exp_mat=Biobase::exprs(eset)[, use_samples,drop=F],phenotype_info=phe, feature_info=Biobase::fData(eset))
  2325. new_mat <- Biobase::exprs(new_eset)
  2326. ##
  2327. design.mat <-as.data.frame(matrix(NA, nrow = base::length(use_samples), ncol = 1))
  2328. rownames(design.mat) <-use_samples
  2329. colnames(design.mat) <- 'group'
  2330. design.mat[base::intersect(G0, use_samples), 'group'] <- 'G0'
  2331. design.mat[base::intersect(G1, use_samples), 'group'] <- 'G1'
  2332. # design <- model.matrix( ~ group + 0, design.mat)
  2333. group <- factor(design.mat$group)
  2334. design <- model.matrix(~0+group);
  2335. colnames(design) <- levels(group); rownames(design) <- colnames(new_mat)
  2336. if(is.null(random_effect)==TRUE){
  2337. fit <- limma::lmFit(new_mat,design)
  2338. }else{
  2339. random_effect <- random_effect[colnames(Biobase::exprs(new_eset))]
  2340. corfit <- limma::duplicateCorrelation(new_eset,design,block=random_effect)
  2341. fit <- limma::lmFit(new_mat,design,block=random_effect,correlation=corfit$consensus)
  2342. }
  2343. contrasts <- limma::makeContrasts(G1-G0,levels=design)
  2344. fit2 <- limma::contrasts.fit(fit,contrasts=contrasts)
  2345. fit2 <- limma::eBayes(fit2,trend=TRUE)
  2346. #summary(decideTests(fit2, method="global"))
  2347. ##
  2348. tT <- limma::topTable(fit2, adjust.method = "fdr", number = Inf,coef=1)
  2349. if(nrow(tT)==1){
  2350. rownames(tT) <- rownames(new_mat)
  2351. }
  2352. tT <- base::cbind(ID=rownames(tT),tT,stringsAsFactors=FALSE)
  2353. tT <- tT[rownames(new_mat),,drop=FALSE]
  2354. exp_G1 <- base::rowMeans(new_mat[,G1,drop=FALSE]);
  2355. exp_G0 <- base::rowMeans(new_mat[,G0,drop=FALSE]);
  2356. w1 <- which(tT$P.Value<=0);
  2357. if(base::length(w1)>0) tT$P.Value[w1] <- .Machine$double.xmin;
  2358. #z_val <- sapply(tT$P.Value*sign(tT$logFC),function(x)combinePvalVector(x,twosided = TRUE)[1])
  2359. z_val <- sapply(tT$P.Value*sign(tT$logFC),function(x)ifelse(x ==0, 0, combinePvalVector(x,twosided = TRUE)[1])) ## remove zero
  2360. if(is.null(random_effect)==TRUE){
  2361. tT <- base::cbind(tT,'Z-statistics'=z_val,'Ave.G0'=exp_G0,'Ave.G1'=exp_G1)
  2362. }else{
  2363. tT <- base::cbind(tT,'Z-statistics'=z_val,'Ave.G0'=exp_G0,'Ave.G1'=exp_G1,
  2364. 'Ave.G0_RemoveRandomEffect'=fit@.Data[[1]][rownames(tT),'G0'],
  2365. 'Ave.G1_RemoveRandomEffect'=fit@.Data[[1]][rownames(tT),'G1'])
  2366. }
  2367. if(is.null(G0_name)==FALSE) colnames(tT) <- gsub('Ave.G0',paste0('Ave.',G0_name),colnames(tT))
  2368. if(is.null(G1_name)==FALSE) colnames(tT) <- gsub('Ave.G1',paste0('Ave.',G1_name),colnames(tT))
  2369. tT <- tT[order(tT$P.Value, decreasing = FALSE), ]
  2370. return(tT)
  2371. }
  2372. #' Combine P Values Using Fisher's Method or Stouffer's Method
  2373. #'
  2374. #' \code{combinePvalVector} is a function to combine multiple comparison's P values using Fisher's method or Stouffer's method.
  2375. #'
  2376. #' @param pvals a vector of numerics, the P values from multiple comparison need to be combined.
  2377. #' @param method character, users can choose between "Stouffer" and "Fisher". Default is "Stouffer".
  2378. #' @param signed logical, if TRUE, will give a sign to the P value to indicate the direction of testing.
  2379. #' Default is TRUE.
  2380. #' @param twosided logical, if TRUE, P value is calculated in a one-tailed test.
  2381. #' If FALSE, P value is calculated in a two-tailed test, and it falls within the range 0 to 0.5.
  2382. #' Default is TRUE.
  2383. #' @return Return a vector contains the "Z-statistics" and "P.Value".
  2384. #' @examples
  2385. #' combinePvalVector(c(0.1,1e-3,1e-5))
  2386. #' combinePvalVector(c(0.1,1e-3,-1e-5))
  2387. #' @export
  2388. combinePvalVector <-
  2389. function(pvals,
  2390. method = 'Stouffer',
  2391. signed = TRUE,
  2392. twosided = TRUE) {
  2393. #
  2394. all_input_para <- c('pvals','method','signed','twosided')
  2395. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2396. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2397. check_res <- c(check_option('signed',c(TRUE,FALSE),envir=environment()),
  2398. check_option('twosided',c(TRUE,FALSE),envir=environment()),
  2399. check_option('method',c('Stouffer','Fisher'),envir=environment()))
  2400. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2401. #
  2402. #remove NA pvalues
  2403. pvals <- pvals[!is.na(pvals) & !is.null(pvals)]
  2404. pvals[which(abs(pvals)<=0)] <- .Machine$double.xmin
  2405. if (sum(is.na(pvals)) >= 1) {
  2406. stat <- NA
  2407. pval <- NA
  2408. } else{
  2409. if (twosided & (sum(pvals > 1 | pvals < -1) >= 1))
  2410. stop('pvalues must between 0 and 1!\n')
  2411. if (!twosided & (sum(pvals > 0.5 | pvals < -0.5) >= 1))
  2412. stop('One-sided pvalues must between 0 and 0.5!\n')
  2413. if (!signed) {
  2414. pvals <- abs(pvals)
  2415. }
  2416. signs <- sign(pvals)
  2417. signs[signs == 0] <- 1
  2418. if (grepl('Fisher', method, ignore.case = TRUE)) {
  2419. if (twosided & signed) {
  2420. neg.pvals <- pos.pvals <- abs(pvals) / 2
  2421. pos.pvals[signs < 0] <- 1 - pos.pvals[signs < 0]
  2422. neg.pvals[signs > 0] <- 1 - neg.pvals[signs > 0]
  2423. } else{
  2424. neg.pvals <- pos.pvals <- abs(pvals)
  2425. }
  2426. pvals <-
  2427. c(1, -1) * c(
  2428. pchisq(
  2429. -2 * sum(log(as.numeric(pos.pvals))),
  2430. df = 2 * base::length(pvals),
  2431. lower.tail = FALSE
  2432. ) / 2,
  2433. pchisq(
  2434. -2 * sum(log(as.numeric(neg.pvals))),
  2435. df = 2 * base::length(pvals),
  2436. lower.tail = FALSE
  2437. ) / 2
  2438. )
  2439. pval <- base::min(abs(pvals))[1]
  2440. #if two pvals are equal, pick up the first one
  2441. stat <-
  2442. sign(pvals[abs(pvals) == pval])[1] * qnorm(pval, lower.tail = F)[1]
  2443. pval <- 2 * pval
  2444. }
  2445. else if (grepl('Stou', method, ignore.case = TRUE)) {
  2446. if (twosided) {
  2447. zs <- signs * qnorm(abs(pvals) / 2, lower.tail = FALSE)
  2448. stat <- sum(zs) / sqrt(base::length(zs))
  2449. pval <- 2 * pnorm(abs(stat), lower.tail = FALSE)
  2450. }
  2451. else{
  2452. zs <- signs * qnorm(abs(pvals), lower.tail = FALSE)
  2453. stat <- sum(zs) / sqrt(base::length(zs))
  2454. pval <- pnorm(abs(stat), lower.tail = FALSE)
  2455. }
  2456. }
  2457. else{
  2458. stop('Only \"Fisher\" or \"Stouffer\" method is supported!!!\n')
  2459. }
  2460. }
  2461. return(c(`Z-statistics` = stat, `P.Value` = pval))
  2462. }
  2463. #' Merge Activity Values from TF (transcription factors) ExpressionSet Object and Sig (signaling factors) ExpressionSet Object
  2464. #'
  2465. #' \code{merge_TF_SIG.AC} combines the activity value from TF (transcription factors) and Sig (signaling factors) ExpressionSet objects together,
  2466. #' and adds "_TF" or "_SIG" suffix to drivers for easier distinction.
  2467. #'
  2468. #'
  2469. #' @param TF_AC ExpressionSet object, containing the activity values for all TFs.
  2470. #' @param SIG_AC ExpressionSet object, containing the activity values for all SIGs.
  2471. #'
  2472. #' @return Return an ExpressionSet object.
  2473. #' @examples
  2474. #' if(exists('analysis.par')==TRUE) rm(analysis.par)
  2475. #' network.dir <- sprintf('%s/demo1/network/',system.file(package = "NetBID2")) # use demo
  2476. #' network.project.name <- 'project_2019-02-14' # demo project name
  2477. #' project_main_dir <- 'test/'
  2478. #' project_name <- 'test_driver'
  2479. #' analysis.par <- NetBID.analysis.dir.create(project_main_dir=project_main_dir,
  2480. #' project_name=project_name,
  2481. #' network_dir=network.dir,
  2482. #' network_project_name=network.project.name)
  2483. #' analysis.par$tf.network <- get.SJAracne.network(network_file=analysis.par$tf.network.file)
  2484. #' analysis.par$sig.network <- get.SJAracne.network(network_file=analysis.par$sig.network.file)
  2485. #' ## get eset (here for demo, use network.par$net.eset)
  2486. #' network.par <- list()
  2487. #' network.par$out.dir.DATA <- system.file('demo1','network/DATA/',package = "NetBID2")
  2488. #' NetBID.loadRData(network.par=network.par,step='exp-QC')
  2489. #' analysis.par$cal.eset <- network.par$net.eset
  2490. #' ac_mat_TF <- cal.Activity(target_list=analysis.par$tf.network$target_list,
  2491. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),
  2492. #' es.method='weightedmean')
  2493. #' ac_mat_SIG <- cal.Activity(target_list=analysis.par$tf.network$target_list,
  2494. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),
  2495. #' es.method='weightedmean')
  2496. #' analysis.par$ac.tf.eset <- generate.eset(exp_mat=ac_mat_TF,
  2497. #' phenotype_info=Biobase::pData(analysis.par$cal.eset))
  2498. #' analysis.par$ac.sig.eset <- generate.eset(exp_mat=ac_mat_SIG,
  2499. #' phenotype_info=Biobase::pData(analysis.par$cal.eset))
  2500. #' analysis.par$merge.ac.eset <- merge_TF_SIG.AC(TF_AC=analysis.par$ac.tf.eset,
  2501. #' SIG_AC=analysis.par$ac.sig.eset)
  2502. #' @export
  2503. merge_TF_SIG.AC <- function(TF_AC=NULL,SIG_AC=NULL){
  2504. #
  2505. all_input_para <- c('TF_AC','SIG_AC')
  2506. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2507. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2508. #
  2509. mat_TF <- Biobase::exprs(TF_AC)
  2510. mat_SIG <- Biobase::exprs(SIG_AC)
  2511. funcType <- c(rep('TF',nrow(mat_TF)),rep('SIG',nrow(mat_SIG)))
  2512. rn <- c(rownames(mat_TF),rownames(mat_SIG))
  2513. rn_label <- base::paste(rn,funcType,sep='_')
  2514. mat_combine <- base::rbind(mat_TF,mat_SIG[,colnames(mat_TF)])
  2515. rownames(mat_combine) <- rn_label
  2516. eset_combine <- generate.eset(exp_mat=mat_combine,phenotype_info=Biobase::pData(TF_AC)[colnames(mat_combine),],
  2517. feature_info=NULL,annotation_info='activity in dataset')
  2518. return(eset_combine)
  2519. }
  2520. #' Merge TF (transcription factor) Network and Sig (signaling factor) Network
  2521. #'
  2522. #' \code{merge_TF_SIG.network} takes TF network and Sig network and combine them together.
  2523. #' The merged list object contains three elements, a data.frame contains all the combined network information \code{network_dat},
  2524. #' a driver-to-target list object \code{target_list}, and an igraph object of the network \code{igraph_obj}.
  2525. #'
  2526. #' @param TF_network list, the TF network created by \code{get.SJAracne.network} function.
  2527. #' @param SIG_network list, the SIG network created by \code{get.SJAracne.network} function.
  2528. #' @return
  2529. #' Return the a list containing three elements, \code{network_dat}, \code{target_list} and \code{igraph_obj}.
  2530. #' @examples
  2531. #' if(exists('analysis.par')==TRUE) rm(analysis.par)
  2532. #' network.dir <- sprintf('%s/demo1/network/',system.file(package = "NetBID2")) # use demo
  2533. #' network.project.name <- 'project_2019-02-14' # demo project name
  2534. #' project_main_dir <- 'test/'
  2535. #' project_name <- 'test_driver'
  2536. #' analysis.par <- NetBID.analysis.dir.create(project_main_dir=project_main_dir,
  2537. #' project_name=project_name,
  2538. #' network_dir=network.dir,
  2539. #' network_project_name=network.project.name)
  2540. #' analysis.par$tf.network <- get.SJAracne.network(network_file=analysis.par$tf.network.file)
  2541. #' analysis.par$sig.network <- get.SJAracne.network(network_file=analysis.par$sig.network.file)
  2542. #' analysis.par$merge.network <- merge_TF_SIG.network(TF_network=analysis.par$tf.network,
  2543. #' SIG_network=analysis.par$sig.network)
  2544. #' @export
  2545. merge_TF_SIG.network <- function(TF_network=NULL,SIG_network=NULL){
  2546. #
  2547. all_input_para <- c('TF_network','SIG_network')
  2548. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2549. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2550. #
  2551. s_TF <- names(TF_network$target_list)
  2552. s_SIG <- names(SIG_network$target_list)
  2553. funcType <- c(rep('TF',base::length(s_TF)),rep('SIG',base::length(s_SIG)))
  2554. rn <- c(s_TF,s_SIG)
  2555. rn_label <- base::paste(rn,funcType,sep='_')
  2556. target_list_combine <- c(TF_network$target_list,SIG_network$target_list)
  2557. names(target_list_combine) <- rn_label
  2558. n_TF <- TF_network$network_dat
  2559. if(nrow(n_TF)>0) n_TF$source <- base::paste(n_TF$source,'TF',sep='_')
  2560. n_SIG <- SIG_network$network_dat
  2561. if(nrow(n_SIG)>0) n_SIG$source <- base::paste(n_SIG$source,'SIG',sep='_')
  2562. net_dat <- base::rbind(n_TF,n_SIG)
  2563. igraph_obj <- graph_from_data_frame(net_dat[,c('source','target')],directed=TRUE)
  2564. if('MI' %in% colnames(net_dat)) igraph_obj <- set_edge_attr(igraph_obj,'weight',index=E(igraph_obj),value=net_dat[,'MI'])
  2565. if('spearman' %in% colnames(net_dat)) igraph_obj <- set_edge_attr(igraph_obj,'sign',index=E(igraph_obj),value=sign(net_dat[,'spearman']))
  2566. return(list(network_dat=net_dat,target_list=target_list_combine,igraph_obj=igraph_obj))
  2567. }
  2568. #' Generate the Master Table for Drivers
  2569. #'
  2570. #' \code{generate.masterTable} generates a master table to show the mega information of all tested drivers.
  2571. #'
  2572. #' The master table gathers TF (transcription factor) information, Sig (signaling factor) information, all the DE (differential expression analysis)
  2573. #' and DA (differential activity analysis) from multiple comparisons. It also shows each driver's target gene size and other additional information
  2574. #' (e.g. gene biotype, chromosome name, position etc.).
  2575. #'
  2576. #' @param use_comp a vector of characters, the name of multiple comparisons. It will be used to name the columns of master table.
  2577. #' @param DE list, a list of DE comparisons, each comparison is a data.frame. The element name in the list must contain the name in \code{use_comp}.
  2578. #' @param DA list, a list of DA comparisons, each comparison is a data.frame. The element name in the list must contain the name in \code{use_comp}.
  2579. #' @param target_list list, a driver-to-target list. The names of the list elements are drivers. Each element is a data frame, usually contains three columns.
  2580. #' "target", target gene names; "MI", mutual information; "spearman", spearman correlation coefficient.
  2581. #' It is highly suggested to follow the NetBID2 pipeline, and the \code{TF_network} could be generated by \code{get_net2target_list} and \code{get.SJAracne.network}.
  2582. #' @param main_id_type character, the type of driver's ID. It comes from the attribute name in biomaRt package.
  2583. #' Such as "ensembl_gene_id", "ensembl_gene_id_version", "ensembl_transcript_id", "ensembl_transcript_id_version" or "refseq_mrna".
  2584. #' For details, user can call \code{biomaRt::listAttributes()} to display all available attributes in the selected dataset.
  2585. #' @param transfer_tab data.frame, the data frame for ID conversion. This can be obtained by calling \code{get_IDtransfer}.
  2586. #' If NULL and \code{main_id_type} is not in the column names of \code{tf_sigs}, it will use the conversion table within the function.
  2587. #' Default is NULL.
  2588. #' @param tf_sigs list, contains all the detailed information of TF and Sig. Users can call \code{db.preload} for access.
  2589. #' @param z_col character, name of the column in \code{DE} and \code{DA} contains the Z statistics. Default is "Z-statistics".
  2590. #' @param display_col character, name of the column in \code{DE} and \code{DA} need to be kept in the master table. Default is c("logFC","P.Value").
  2591. #' @param column_order_strategy character, users can choose between "type" and "comp". Default is "type".
  2592. #' If set as type, the columns will be ordered by column type; If set as comp, the columns will be ordered by comparison.
  2593. #' @return Return a data frame contains the mega information of all tested drivers.
  2594. #' The column "originalID" and "originalID_label" is the same ID as from the original dataset.
  2595. #' @examples
  2596. #' analysis.par <- list()
  2597. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  2598. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  2599. #' #analysis.par$final_ms_tab ## this is master table generated before
  2600. #' ac_mat <- cal.Activity(target_list=analysis.par$merge.network$target_list,
  2601. #' cal_mat=Biobase::exprs(analysis.par$cal.eset),es.method='weightedmean')
  2602. #' analysis.par$ac.merge.eset <- generate.eset(exp_mat=ac_mat,
  2603. #' phenotype_info=Biobase::pData(analysis.par$cal.eset))
  2604. #' phe_info <- Biobase::pData(analysis.par$cal.eset)
  2605. #' all_subgroup <- base::unique(phe_info$subgroup) ##
  2606. #' for(each_subtype in all_subgroup){
  2607. #' comp_name <- sprintf('%s.Vs.others',each_subtype) ## each comparison must give a name !!!
  2608. #' G0 <- rownames(phe_info)[which(phe_info$`subgroup`!=each_subtype)] # get sample list for G0
  2609. #' G1 <- rownames(phe_info)[which(phe_info$`subgroup`==each_subtype)] # get sample list for G1
  2610. #' DE_gene_limma <- getDE.limma.2G(eset=analysis.par$cal.eset,G1=G1,G0=G0,
  2611. #' G1_name=each_subtype,G0_name='other')
  2612. #' analysis.par$DE[[comp_name]] <- DE_gene_limma
  2613. #' DA_driver_limma <- getDE.limma.2G(eset=analysis.par$ac.merge.eset,G1=G1,G0=G0,
  2614. #' G1_name=each_subtype,G0_name='other')
  2615. #' analysis.par$DA[[comp_name]] <- DA_driver_limma
  2616. #' }
  2617. #' all_comp <- names(analysis.par$DE) ## get all comparison name for output
  2618. #' db.preload(use_level='gene',use_spe='human',update=FALSE);
  2619. #' test_ms_tab <- generate.masterTable(use_comp=all_comp,
  2620. #' DE=analysis.par$DE,
  2621. #' DA=analysis.par$DA,
  2622. #' target_list=analysis.par$merge.network$target_list,
  2623. #' tf_sigs=tf_sigs,
  2624. #' z_col='Z-statistics',
  2625. #' display_col=c('logFC','P.Value'),
  2626. #' main_id_type='external_gene_name')
  2627. #' @export
  2628. generate.masterTable <- function(use_comp=NULL,DE=NULL,DA=NULL,
  2629. target_list=NULL,main_id_type=NULL,transfer_tab=NULL,
  2630. tf_sigs=NULL,
  2631. z_col='Z-statistics',display_col=c('logFC','P.Value'),
  2632. column_order_strategy='type'){
  2633. #
  2634. all_input_para <- c('use_comp','DE','DA','target_list','main_id_type','tf_sigs','z_col','display_col','column_order_strategy')
  2635. check_res <- sapply(all_input_para,function(x)check_para(x,envir=base::environment()))
  2636. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2637. check_res <- c(check_option('column_order_strategy',c('type','comp'),envir=environment()))
  2638. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2639. #
  2640. if(base::length(base::setdiff(use_comp,names(DE)))>0){message(sprintf('%s not included in DE, please check and re-try!',base::setdiff(use_comp,names(DE))));return(FALSE)}
  2641. if(base::length(base::setdiff(use_comp,names(DA)))>0){message(sprintf('%s not included in DA, please check and re-try!',base::setdiff(use_comp,names(DA))));return(FALSE)}
  2642. # get original ID
  2643. ori_rn <- rownames(DA[[1]]) ## with label
  2644. w1 <- grep('(.*)_TF',ori_rn); w2 <- grep('(.*)_SIG',ori_rn)
  2645. funcType <- rep(NA,length.out=base::length(ori_rn));rn <- funcType
  2646. funcType[w1] <- 'TF'; funcType[w2] <- 'SIG';
  2647. rn[w1] <- gsub('(.*)_TF',"\\1",ori_rn[w1]);rn[w2] <- gsub('(.*)_SIG',"\\1",ori_rn[w2]);
  2648. rn_label <- ori_rn
  2649. use_size <- unlist(lapply(target_list[rownames(DA[[1]])],nrow))
  2650. # id issue
  2651. current_id <- names(tf_sigs$tf)[-1]
  2652. use_info <- base::unique(base::rbind(tf_sigs$tf$info,tf_sigs$sig$info))
  2653. if(main_id_type %in% current_id){
  2654. use_info <- use_info[which(use_info[,main_id_type] %in% rn),]
  2655. }else{
  2656. if(is.null(transfer_tab)==TRUE){
  2657. transfer_tab <- get_IDtransfer(from_type=main_id_type,to_type=current_id[1],use_genes=rn,ignore_version = TRUE)
  2658. use_info <- base::merge(use_info,transfer_tab,by.x=current_id[1],by.y=current_id[1])
  2659. }else{
  2660. transfer_tab <- transfer_tab[which(transfer_tab[,main_id_type] %in% rn),]
  2661. uid <- base::intersect(colnames(transfer_tab),colnames(use_info))
  2662. if(base::length(uid)==0){message('No ID type in the transfer_tab could match ID type in tf_sigs, please check and re-try!');return(FALSE);}
  2663. uid <- uid[1]
  2664. use_info <- base::merge(use_info,transfer_tab,by.x=uid,by.y=uid)
  2665. }
  2666. use_info <- use_info[which(use_info[,main_id_type] %in% rn),]
  2667. }
  2668. use_info <- base::unique(use_info)
  2669. if(nrow(use_info)==0){message('ID issue error, please check main_id_type setting!');return(FALSE);}
  2670. # merge info
  2671. tmp1 <- stats::aggregate(use_info,list(use_info[,main_id_type]),function(x){
  2672. x1 <- x[which(x!="")]
  2673. x1 <- x1[which(is.na(x1)==FALSE)]
  2674. base::paste(sort(base::unique(x1)),collapse=';')
  2675. })
  2676. tmp1 <- tmp1[,-1]; rownames(tmp1) <- tmp1[,main_id_type]
  2677. geneSymbol <- tmp1[rn,'external_gene_name'] ## this column for function enrichment
  2678. if('external_transcript_name' %in% colnames(tmp1)){ ## this column for display
  2679. gene_label <- base::paste(tmp1[rn,'external_transcript_name'],funcType,sep = '_')
  2680. }else{
  2681. gene_label <-base::paste(tmp1[rn,'external_gene_name'],funcType,sep = '_')
  2682. }
  2683. #
  2684. #label_info <- data.frame('gene_label'=gene_label,'geneSymbol'=geneSymbol,
  2685. # 'originalID'=rn,'originalID_label'=rn_label,'funcType'=funcType,'Size'=use_size,stringsAsFactors=FALSE)
  2686. label_info <- data.frame('originalID_label'=rn_label,'originalID'=rn,'gene_label'=gene_label,'geneSymbol'=geneSymbol,
  2687. 'funcType'=funcType,'Size'=use_size,stringsAsFactors=FALSE)
  2688. w1 <- which(is.na(geneSymbol)==TRUE)
  2689. label_info[w1,'geneSymbol'] <- label_info[w1,'originalID']
  2690. label_info[w1,'gene_label'] <- label_info[w1,'originalID_label']
  2691. add_info <- tmp1[rn,]
  2692. #
  2693. combine_info <- lapply(use_comp,function(x){
  2694. DA[[x]] <- DA[[x]][rn_label,,drop=F]
  2695. DE[[x]] <- as.data.frame(DE[[x]])[rn,]
  2696. avg_col <- colnames(DA[[x]])[grep('^Ave',colnames(DA[[x]]))]
  2697. uc <- c(z_col,avg_col,base::setdiff(display_col,c(z_col,avg_col))); uc <- base::intersect(uc,colnames(DA[[x]]))
  2698. DA_info <- DA[[x]][rn_label,uc,drop=F]
  2699. avg_col <- colnames(DE[[x]])[grep('^Ave',colnames(DE[[x]]))]
  2700. uc <- c(z_col,avg_col,base::setdiff(display_col,c(z_col,avg_col))); uc <- base::intersect(uc,colnames(DE[[x]]))
  2701. DE_info <- as.data.frame(DE[[x]])[rn,uc,drop=F]
  2702. colnames(DA_info) <- paste0(colnames(DA_info),'.',x,'_DA')
  2703. colnames(DE_info) <- paste0(colnames(DE_info),'.',x,'_DE')
  2704. colnames(DA_info)[1] <- paste0('Z.',x,'_DA')
  2705. colnames(DE_info)[1] <- paste0('Z.',x,'_DE')
  2706. out <- base::cbind(DA_info,DE_info,stringsAsFactors=FALSE)
  2707. rownames(out) <- rn_label
  2708. out
  2709. })
  2710. combine_info_DA <- do.call(base::cbind,lapply(combine_info,function(x)x[rn_label,grep('_DA$',colnames(x)),drop=T]))
  2711. combine_info_DE <- do.call(base::cbind,lapply(combine_info,function(x)x[rn_label,grep('_DE$',colnames(x)),drop=T]))
  2712. # re-organize the columns for combine info
  2713. if(column_order_strategy=='type' & length(use_comp)>1){
  2714. col_ord <- c('Z','AveExpr',display_col)
  2715. tmp1 <- lapply(col_ord,function(x){
  2716. x1 <- grep(sprintf('^%s\\.',x),colnames(combine_info_DA))
  2717. if(length(x1)>0) combine_info_DA[,x1] else return(NULL)
  2718. })
  2719. combine_info_DA <- do.call(base::cbind,tmp1)
  2720. tmp1 <- lapply(col_ord,function(x){
  2721. combine_info_DE[,grep(sprintf('^%s\\.',x),colnames(combine_info_DE))]
  2722. })
  2723. combine_info_DE <- do.call(base::cbind,tmp1)
  2724. }
  2725. # put them together
  2726. ms_tab <- base::cbind(label_info,combine_info_DA,combine_info_DE,add_info)
  2727. rownames(ms_tab) <- ms_tab$originalID_label
  2728. return(ms_tab)
  2729. }
  2730. #' Save the Master Table into Excel File
  2731. #'
  2732. #' \code{out2excel} is a function can save data frame as Excel File. This is mainly for the output of master table generated by \code{generate.masterTable}.
  2733. #'
  2734. #' @param all_ms_tab list or data.frame, if data.frame, it is generated by \code{generate.masterTable}.
  2735. #' If list, each list element is data.frame/master table.
  2736. #' The name of the list element will be the sheet name in the excel file.
  2737. #' @param out.xlsx character, path and file name of the output Excel file.
  2738. #' @param mark_gene list, list of marker genes. The name of the list element is the marked group name. Each element is a vector of marker genes.
  2739. #' This is optional, just to add additional information to the file.
  2740. #' @param mark_col character, the color to mark the marker genes. If NULL, will use \code{get.class.color} to get the colors.
  2741. #' @param mark_strategy character, users can choose between "color" and "add_column".
  2742. #' "Color" means the mark_gene will be marked by filling its background color;
  2743. #' "add_column" means the mark_gene will be displayed in a separate column with TRUE/FALSE, indicating whether the gene belongs to a mark group or not.
  2744. #' @param workbook_name character, name of the workbook for the output Excel. Default is "ms_tab".
  2745. #' @param only_z_sheet logical, if TRUE, will create a separate sheet only contains Z-statistics related columns from DA/DE analysis.
  2746. #' Default is FALSE.
  2747. #' @param z_column character, name of the columns contain Z-statistics. If NULL, find column names start with "Z.".
  2748. #' Default is NULL.
  2749. #' @param sig_thre numeric, threshold for the Z-statistics. Z values passed the threshold will be colored. The color scale is defined by \code{z2col}.
  2750. #' Default is 1.64.
  2751. #' @return Return a logical value. If TRUE, the Excel file has been generated successfully.
  2752. #' @examples
  2753. #' \dontrun{
  2754. #' analysis.par <- list()
  2755. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  2756. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  2757. #' ms_tab <- analysis.par$final_ms_tab ## this is master table generated before
  2758. #' mark_gene <- list(WNT=c('WIF1','TNC','GAD1','DKK2','EMX2'),
  2759. #' SHH=c('PDLIM3','EYA1','HHIP','ATOH1','SFRP1'),
  2760. #' Group3=c('IMPG2','GABRA5','EGFL11','NRL','MAB21L2','NPR3','MYC'),
  2761. #' Group4=c('KCNA1','EOMES','KHDRBS2','RBM24','UNC5D'))
  2762. #' mark_col <- get.class.color(names(mark_gene),
  2763. #' pre_define=c('WNT'='blue','SHH'='red',
  2764. #' 'Group3'='yellow','Group4'='green'))
  2765. #' outfile <- 'test_out.xlsx'
  2766. #' out2excel(ms_tab,out.xlsx = outfile,mark_gene,mark_col)
  2767. #' }
  2768. #' @export
  2769. out2excel <- function(all_ms_tab,out.xlsx,
  2770. mark_gene=NULL,
  2771. mark_col=NULL,
  2772. mark_strategy='color',
  2773. workbook_name='ms_tab',
  2774. only_z_sheet=FALSE,
  2775. z_column=NULL,sig_thre=1.64){
  2776. #
  2777. all_input_para <- c('all_ms_tab','out.xlsx','mark_strategy','workbook_name','only_z_sheet','sig_thre')
  2778. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2779. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2780. check_res <- c(check_option('only_z_sheet',c(TRUE,FALSE),envir=environment()),
  2781. check_option('mark_strategy',c('color','add_column'),envir=environment()))
  2782. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2783. #
  2784. wb <- openxlsx::createWorkbook(workbook_name)
  2785. if(!'list' %in% class(all_ms_tab)){
  2786. all_ms_tab <- list('Sheet1'=as.data.frame(all_ms_tab))
  2787. }
  2788. if(only_z_sheet==TRUE){
  2789. nn <- names(all_ms_tab)
  2790. all_ms_tab <- lapply(all_ms_tab,function(x){
  2791. w1 <- grep('.*_[DE|DA]',colnames(x))
  2792. w2 <- base::setdiff(colnames(x)[w1],colnames(x)[w1][grep('^Z.*',colnames(x)[w1])])
  2793. w3 <- base::setdiff(colnames(x),w2)
  2794. list(x[,w3],x)
  2795. })
  2796. all_ms_tab <- unlist(all_ms_tab,recursive = FALSE)
  2797. nn1 <- lapply(nn,function(x){
  2798. if(x!='Sheet1'){
  2799. c(sprintf('Only_Z_%s',x),sprintf('Full_info_%s',x))
  2800. }else{
  2801. c('Only_Z','Full_info')
  2802. }
  2803. })
  2804. nn1 <- unlist(nn1)
  2805. names(all_ms_tab) <- nn1
  2806. }
  2807. if(is.null(mark_gene)==FALSE){
  2808. if(!'list' %in% class(mark_gene)){
  2809. mark_gene <- list('mark_gene'=mark_gene)
  2810. }
  2811. if(is.null(names(mark_gene))==TRUE){
  2812. message('Must give name to the mark_gene list');return(FALSE)
  2813. }
  2814. if(mark_strategy=='color' & is.null(mark_col)==TRUE){
  2815. mark_col <- get.class.color(names(mark_gene))
  2816. }
  2817. if(mark_strategy=='add_column'){
  2818. new_col_name <- paste0('is',names(mark_gene))
  2819. all_ms_tab <- lapply(all_ms_tab,function(x){
  2820. g1 <- x$geneSymbol
  2821. r1 <- do.call(base::cbind,lapply(mark_gene,function(x1){
  2822. ifelse(g1 %in% x1,'TRUE','FALSE')
  2823. }))
  2824. new_x <- base::cbind(x,r1)
  2825. colnames(new_x) <- c(colnames(x),new_col_name)
  2826. new_x
  2827. })
  2828. }
  2829. }
  2830. z_column_index <- 'defined'
  2831. if(is.null(z_column)==TRUE) z_column_index <- 'auto'
  2832. i <- 0
  2833. headerStyle <- openxlsx::createStyle(fontSize = 14, fontColour = "#FFFFFF", halign = "center",fgFill = "#4F81BD",
  2834. border="TopBottom", borderColour = "#4F81BD",wrapText=TRUE) ## style for header line
  2835. for(sheetname in names(all_ms_tab)){ ## list, each item create one sheet
  2836. i <- i +1
  2837. d <- as.data.frame(all_ms_tab[[sheetname]])
  2838. if(z_column_index=='auto') use_z_column <- colnames(d)[grep('^Z\\.',colnames(d),ignore.case = TRUE)] else use_z_column <- base::intersect(colnames(d),z_column)
  2839. openxlsx::addWorksheet(wb,sheetName=sheetname)
  2840. openxlsx::writeData(wb,sheet = i,d)
  2841. openxlsx::addStyle(wb, sheet = i, headerStyle, rows = 1, cols = 1:ncol(d), gridExpand = TRUE) ## add header style
  2842. all_c <- list()
  2843. for(z_col in use_z_column){ ## find colnames with z. (only applied to pipeline excel)
  2844. j <- which(colnames(d)==z_col)
  2845. z1 <- d[,j]; c1 <- z2col(z1,sig_thre=sig_thre)
  2846. all_c[[as.character(j)]] <- c1
  2847. }
  2848. mat_c <- do.call(base::cbind,all_c)
  2849. uni_c <- base::unique(unlist(all_c))
  2850. for(r in uni_c){
  2851. w1 <- which(mat_c==r)
  2852. nn <- nrow(mat_c)
  2853. rr <- w1%%nn+1
  2854. cc <- w1%/%nn+1
  2855. cc[which(rr==1)] <- cc[which(rr==1)]-1
  2856. rr[which(rr==1)] <- nn+1
  2857. openxlsx::addStyle(wb, sheet = i, createStyle(fgFill=r), rows =rr, cols = as.numeric(names(all_c)[cc]))
  2858. }
  2859. if(is.null(mark_gene)==FALSE){
  2860. for(k in names(mark_gene)){
  2861. openxlsx::addStyle(wb, sheet = i, openxlsx::createStyle(fgFill=mark_col[k]),
  2862. rows =which(toupper(d[,which(colnames(d)=='geneSymbol')]) %in% toupper(mark_gene[[k]]))+1,
  2863. cols = which(colnames(d)=='geneSymbol')) ## find column with geneSymbol and mark color
  2864. }
  2865. }
  2866. w1 <- which(gsub("\\s","",as.matrix(d))=='TRUE')
  2867. nn <- nrow(d)
  2868. rr <- w1%%nn+1
  2869. cc <- w1%/%nn+1
  2870. cc[which(rr==1)] <- cc[which(rr==1)]-1
  2871. rr[which(rr==1)] <- nn+1
  2872. openxlsx::addStyle(wb, sheet = i, openxlsx::createStyle(fontColour='#FF0000'), rows =rr, cols = as.numeric(cc)) ## find column with TRUE/FALSE and mark with color
  2873. }
  2874. openxlsx::saveWorkbook(wb, out.xlsx, overwrite = TRUE)
  2875. return(TRUE)
  2876. ##
  2877. }
  2878. #' Load MSigDB Database into R Workspace
  2879. #'
  2880. #' \code{gs.preload} downloads data from MSigDB and stores it into two variables in R workspace, \code{all_gs2gene} and \code{all_gs2gene_info}.
  2881. #' \code{all_gs2gene} is a list object with elements of gene sets collections.
  2882. #' \code{all_gs2gene_info} is a data.frame contains the description of each gene sets.
  2883. #'
  2884. #' This is a pre-processing function for NetBID2 advanced analysis. User only need to input the species name (e.g. "Homo sapiens", "Mus musculus").
  2885. #' It will call \code{msigdbr} to download data from MSigDB and save it as RData under the \code{db/} directory with species name.
  2886. #'
  2887. #' @param use_spe character, name of interested species (e.g. "Homo sapiens", "Mus musculus").
  2888. #' Users can call \code{msigdbr_species()} to access the full list of available species names.
  2889. #' Default is "Homo sapiens".
  2890. #' @param update logical, if TRUE, the previous loaded RData will be updated. Default is FALSE.
  2891. #' @param main.dir character, the main file path of user's NetBID2 project.
  2892. #' If NULL, will be set to \code{system.file(package = "NetBID2")}. Default is NULL.
  2893. #' @param db.dir character, the file path to save the RData. Default is \code{db} directory under the \code{main.dir}, if one has a \code{main.dir}.
  2894. #'
  2895. #' @return Reture a logical value. If TRUE, MsigDB database is loaded successfully, with \code{all_gs2gene} and \code{all_gs2gene_info} created
  2896. #' in the workspace.
  2897. #'
  2898. #' @examples
  2899. #' gs.preload(use_spe='Homo sapiens',update=FALSE)
  2900. #' gs.preload(use_spe='Mus musculus',update=FALSE)
  2901. #' print(all_gs2gene_info)
  2902. #' # contain the information for all gene set collection, collection info, collection size,
  2903. #' ## sub collection,sub collection info, sub collection size
  2904. #' print(names(all_gs2gene)) # the first level of the list is the collection and sub-collection IDs
  2905. #' print(str(all_gs2gene$`CP:KEGG_MEDICUS`))
  2906. #'
  2907. #' \dontrun{
  2908. #' gs.preload(use_spe='Homo sapiens',update=TRUE)
  2909. #' }
  2910. #' @export
  2911. gs.preload <- function(use_spe = 'Homo sapiens',
  2912. update = FALSE,
  2913. main.dir = NULL,
  2914. db.dir = sprintf("%s/db/",main.dir)){
  2915. #
  2916. all_input_para <- c('use_spe','update')
  2917. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  2918. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2919. all_spe <- msigdbr::msigdbr_species()[["species_name"]]
  2920. check_res <- c(check_option('update',c(TRUE,FALSE),envir=environment()),
  2921. check_option('use_spe',all_spe,envir=environment()))
  2922. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  2923. #
  2924. ## only support geneSymbol (because pipeline-generated master table will contain geneSymbol column)
  2925. if(is.null(main.dir)==TRUE){
  2926. main.dir <- system.file(package = "NetBID2")
  2927. message(sprintf('main.dir not set, will use package directory: %s',main.dir))
  2928. }
  2929. if(is.null(db.dir)==TRUE){
  2930. db.dir <- sprintf("%s/db/",main.dir)
  2931. }
  2932. message(sprintf('Will use directory %s as the db.dir',db.dir))
  2933. use_spe1 <- gsub(' ','_',use_spe)
  2934. out_file <- sprintf('%s/%s_gs2gene.RData',db.dir,use_spe1)
  2935. if(file.exists(out_file)==FALSE | update==TRUE){
  2936. message('Begin generating all_gs2gene !')
  2937. all_gs_info <- msigdbr::msigdbr(species = use_spe) ## use msigdbr_species() to check possible available species
  2938. # for gs_collection
  2939. all_gs_cat <- base::unique(all_gs_info$gs_collection)
  2940. all_gs2gene_1 <- lapply(all_gs_cat,function(x){
  2941. x1 <- all_gs_info[which(all_gs_info$gs_collection==x),]
  2942. all_gs <- base::unique(x1$gs_name)
  2943. x2 <- lapply(all_gs, function(y){
  2944. base::unique(x1$gene_symbol[which(x1$gs_name==y)])
  2945. })
  2946. names(x2) <- all_gs;x2
  2947. })
  2948. names(all_gs2gene_1) <- all_gs_cat
  2949. all_gs_subcat <- base::setdiff(base::unique(all_gs_info$gs_subcollection),"")
  2950. all_gs2gene_2 <- lapply(all_gs_subcat,function(x){
  2951. x1 <- all_gs_info[which(all_gs_info$gs_subcollection==x),]
  2952. all_gs <- base::unique(x1$gs_name)
  2953. x2 <- lapply(all_gs, function(y){
  2954. base::unique(x1$gene_symbol[which(x1$gs_name==y)])
  2955. })
  2956. names(x2) <- all_gs;x2
  2957. })
  2958. names(all_gs2gene_2) <- all_gs_subcat
  2959. all_gs2gene <- c(all_gs2gene_1,all_gs2gene_2)
  2960. #
  2961. all_gs2gene <- all_gs2gene[sort(names(all_gs2gene))]
  2962. gs_size <- unlist(lapply(all_gs2gene,length))
  2963. # info for cat
  2964. info_cat <- c('C1'='positional gene sets', 'C2'='curated gene sets', 'C3'='regulatory target gene sets', 'C4'='computational gene sets', 'C5'='ontology gene sets',
  2965. 'C6'='oncogenic signature gene sets', 'C7'='immunologic signature gene sets', 'C8'='cell type signature gene sets', 'H'='hallmark gene sets')
  2966. info_subcat <- c('CGP'='Chemical and Genetic Perturbations', 'CP'='Canonical Pathways', 'CP:BIOCARTA'='BioCarta subset', 'CP:KEGG_MEDICUS'='KEGG MEDICUS subset', 'CP:KEGG_LEGACY'='KEGG LEGACY subset', 'CP:REACTOME'='Reactome subset', 'CP:WIKIPATHWAYS'='WikiPahtways subset', 'CP:PID'='PID subset',
  2967. 'MIR:MIRDB'='microRNA targets predicted by miRDB v6.0', 'MIR:MIR_LEGACY'='microRNA targets predicted by motif matching', 'TFT:GTRD'='Transcription Factor Targets mined from GTRD', 'TFT:TFT_LEGACY'='Transcription Factor Targets predicted by motif matching',
  2968. '3CA'='Computational gene sets mined from Curated Cancer Cell Atlas (3CA) metaprograms', 'CGN'='Computational gene sets defined by expression neighborhoods centered on 380 cancer-associated genes', 'CM'='Computational gene sets compiled from cancer modules significantly changed in a variety of cancer conditions',
  2969. 'GO:BP'='GO Biological Process', 'GO:MF'='GO Molecular Function', 'GO:CC'='GO Cellular Component', 'HPO'='Human Phenotype Ontology', 'VAX'='Immune signatures curated from vaccine response studies', 'IMMUNESIGDB'='Immune signatures collected from ImmuneSigDB')
  2970. cat_rel <- base::unique(as.data.frame(all_gs_info[,c('gs_collection','gs_subcollection')]))
  2971. all_gs2gene_info <- data.frame(cat_rel[,1],info_cat[cat_rel[,1]],gs_size[cat_rel[,1]],cat_rel[,2],info_subcat[cat_rel[,2]],gs_size[cat_rel[,2]],stringsAsFactors = FALSE)
  2972. colnames(all_gs2gene_info) <- c('Collection','Collection_Info','Collection_Size','Subcollection','Subcollection_Info','Subcollection_Size')
  2973. all_gs2gene_info <- all_gs2gene_info[order(all_gs2gene_info[,1]),]
  2974. all_gs2gene_info[,c(1,2,4,5)] <- as.data.frame(apply(all_gs2gene_info[,c(1,2,4,5)],2,function(x){x[which(is.na(x)==TRUE)] <- "";x}),stringsAsFactors=FALSE)
  2975. save(all_gs2gene,all_gs2gene_info,file=out_file)
  2976. }
  2977. load(out_file,.GlobalEnv)
  2978. message('all_gs2gene loaded, you could see all_gs2gene_info to check the details !')
  2979. return(TRUE)
  2980. }
  2981. ######################################################### visualization functions
  2982. ## simple functions to get info
  2983. #' Create a vector of each sample's selected phenotye descriptive information.
  2984. #'
  2985. #' \code{get_obs_label} creates a vector of each sample's selected phenotype descriptive information.
  2986. #' This is a helper function for data visualization.
  2987. #'
  2988. #' @param phe_info data.frame, the phenotype data of the samples.
  2989. #' It is a data frame that can store any number of descriptive columns (covariates) for each sample row.
  2990. #' To get the phenotype data, using the accessor function \code{pData}.
  2991. #' @param use_col a vector of numerics or characters.
  2992. #' Users can select the interested descriptive column(s) by calling index or name of the column(s).
  2993. #' @param collapse character, an optional character string to separate the results when the length
  2994. #' of \code{use_col} is more than 1. Not NA_character. Default is "|".
  2995. #'
  2996. #' @return
  2997. #' Return a vector of selected phenotype descriptive information (covariates) for each sample.
  2998. #' Vector name is the sample name.
  2999. #' @examples
  3000. #' analysis.par <- list()
  3001. #' analysis.par$out.dir.DATA <- system.file('demo1','driver/DATA/',package = "NetBID2")
  3002. #' NetBID.loadRData(analysis.par=analysis.par,step='ms-tab')
  3003. #' phe_info <- Biobase::pData(analysis.par$cal.eset)
  3004. #' use_obs_class <- get_obs_label(phe_info = phe_info,'subgroup')
  3005. #' print(use_obs_class)
  3006. #' \dontrun{
  3007. #'}
  3008. #' @export
  3009. get_obs_label <- function(phe_info,use_col,collapse='|'){
  3010. #
  3011. all_input_para <- c('phe_info','use_col','collapse')
  3012. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3013. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3014. #
  3015. w1 <- base::setdiff(use_col,colnames(phe_info))
  3016. if(length(w1)>0){
  3017. message(sprintf('%s not in the colnames(phe_info),please check and re-try!',paste(w1,collapse=';')));return(FALSE);
  3018. }
  3019. obs_label<-phe_info[,use_col];
  3020. if(base::length(use_col)>1){
  3021. obs_label<-apply(obs_label,1,function(x)base::paste(x,collapse=collapse))
  3022. }
  3023. names(obs_label) <- rownames(phe_info);
  3024. obs_label
  3025. }
  3026. #' Get interested phenotype groups from pData slot of the ExpressionSet object.
  3027. #'
  3028. #' \code{get_int_group} is a function to extract interested phenotype groups from the ExpressionSet object
  3029. #' with 'cluster-meaningful' sample features.
  3030. #'
  3031. #' @param eset an ExpressionSet object.
  3032. #' @return Return a vector of phenotype groups which could be used for sample cluster analysis.
  3033. #'
  3034. #' @examples
  3035. #' network.par <- list()
  3036. #' network.par$out.dir.DATA <- system.file('demo1','network/DATA/',package = "NetBID2")
  3037. #' NetBID.loadRData(network.par=network.par,step='exp-QC')
  3038. #' intgroups <- get_int_group(network.par$net.eset)
  3039. #' @export
  3040. get_int_group <- function(eset){
  3041. all_input_para <- c('eset')
  3042. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3043. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3044. phe <- Biobase::pData(eset)
  3045. feature_len <- apply(phe,2,function(x)base::length(base::unique(x)))
  3046. intgroup <- colnames(phe)[which(feature_len>1 & feature_len<nrow(phe))]
  3047. return(intgroup)
  3048. }
  3049. #' Get Score to Measure Similarity Between Observed Classification and Predicted Classification.
  3050. #'
  3051. #' \code{get_clustComp} calculates a score to measure the similarity between two classifications.
  3052. #'
  3053. #' @param pred_label a vector of characters, the predicted classification labels.
  3054. #' @param obs_label a vector of characters, the observed classification labels.
  3055. #' @param strategy character, the method applied to calculate the score.
  3056. #' Users can choose "ARI (adjusted rand index)", "NMI (normalized mutual information)" or "Jaccard".
  3057. #' Default is "ARI".
  3058. #' @return Return a score for the measurement of similarity.
  3059. #' @examples
  3060. #' obs_label <- c('A','A','A','B','B','C','D')
  3061. #' pred_label <- c(1,1,1,1,2,2,2)
  3062. #' get_clustComp(pred_label,obs_label)
  3063. #' @export
  3064. get_clustComp <- function(pred_label, obs_label,strategy='ARI') {
  3065. #
  3066. all_input_para <- c('pred_label','obs_label','strategy')
  3067. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3068. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3069. check_res <- c(check_option('strategy',c('ARI','NMI','Jaccard'),envir=environment()))
  3070. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3071. #
  3072. if(is.null(names(pred_label))==TRUE & is.null(names(obs_label))==FALSE){
  3073. message('The names of pred_label will use the order of obs_label')
  3074. names(pred_label) <- names(obs_label)
  3075. }
  3076. if(is.null(names(obs_label))==TRUE & is.null(names(pred_label))==FALSE){
  3077. message('The names of obs_label will use the order of pred_label')
  3078. names(obs_label) <- names(pred_label)
  3079. }
  3080. if(is.null(names(obs_label))==TRUE & is.null(names(pred_label))==TRUE){
  3081. message('Assume pred_label and obs_label have the same order!')
  3082. names(obs_label) <- as.character(1:base::length(obs_label))
  3083. names(pred_label) <- names(obs_label)
  3084. }
  3085. if(strategy=='Jaccard') res1 <- get_jac(pred_label, obs_label) else res1 <- clustComp(pred_label, obs_label)[[strategy]]
  3086. return(res1)
  3087. }
  3088. # get jaccard accuracy
  3089. get_jac <- function(pred_label, obs_label) {
  3090. jac1 <- c()
  3091. for (i in base::unique(pred_label)) {
  3092. jac_index <- c()
  3093. x1 <- names(pred_label)[which(pred_label == i)]
  3094. for (j in base::unique(obs_label)) {
  3095. x2 <- names(obs_label)[which(obs_label == j)]
  3096. jac_index <-
  3097. c(jac_index, base::length(base::intersect(x1, x2)) / base::length(union(x1, x2)))
  3098. }
  3099. jac1 <- c(jac1, base::max(jac_index) * base::length(x1))
  3100. }
  3101. jac1 <- sum(jac1) / base::length(pred_label)
  3102. return(jac1)
  3103. }
  3104. #' Visualize Each Sample's Observed Label vs. Predicted Label in Table
  3105. #'
  3106. #' \code{draw.clustComp} draws a table to show each sample's observed label vs. its predicted label.
  3107. #' Each row represents an observed label (e.g. one subgroup of disease), each column represents the predicted label created by classification algorithm (e.g K-means).
  3108. #'
  3109. #' The table provides more details about the side-by-side PCA biplot created by \code{draw.emb.kmeans}.
  3110. #' The purpose is to find if any abnormal sample (outlier) exists. The darker the table cell is,
  3111. #' the more samples are gathered in the corresponding label.
  3112. #'
  3113. #' @param pred_label a vector of characters, the predicted labels created by classification (e.g K-means).
  3114. #' @param obs_label a vector of characters, the observed labels annotated by phenotype data.
  3115. #' @param strategy character, method to quantify the similarity between predicted labels vs. observed labels.
  3116. #' Users can choose from "ARI (adjusted rand index)", "NMI (normalized mutual information)" and "Jaccard".
  3117. #' Default is "ARI".
  3118. #' @param use_col logical, If TRUE, the table will be colored. The more sample gathered in one table cell, the darker shade it has.
  3119. #' Default is TRUE.
  3120. #' @param low_K integer, a threshold of sample number to be shown in a single cell.
  3121. #' If too many samples gathered in a single table cell, it will be challenging for eyes.
  3122. #' By setting the value of this threshold, if the number of samples gathered in one table cell exceeded the threshold,
  3123. #' only the number will be shown. Otherwise, all samples' names will be listed.
  3124. #' Default is 5.
  3125. #' @param highlight_clust a vector of characters, the predicted label need to be highlighted in the figure.
  3126. #' @param main character, an overall title for the plot.
  3127. #' @param clust_cex numeric, text size for the predicted label (column names). Default is 1.
  3128. #' @param outlier_cex numeric, text size for the observed label (row names). Default is 0.3.
  3129. #' @return Return a matrix of integers and a table for visualization. Rows are predicted label, columns are observed label.
  3130. #' Integer is the number of samples gathered in the corresponding label.
  3131. #' @examples
  3132. #' network.par <- list()
  3133. #' network.par$out.dir.DATA <- system.file('demo1','network/DATA/',package = "NetBID2")
  3134. #' NetBID.loadRData(network.par=network.par,step='exp-QC')
  3135. #' mat <- Biobase::exprs(network.par$net.eset)
  3136. #' phe <- Biobase::pData(network.par$net.eset)
  3137. #' intgroup <- 'subgroup'
  3138. #' pred_label <- draw.emb.kmeans(mat=mat,all_k = NULL,
  3139. #' obs_label=get_obs_label(phe,intgroup),
  3140. #' kmeans_strategy='consensus')
  3141. #' draw.clustComp(pred_label,get_obs_label(phe,intgroup),outlier_cex=1,low_K=2,use_col=TRUE)
  3142. #' draw.clustComp(pred_label,get_obs_label(phe,intgroup),outlier_cex=1,low_K=2,use_col=FALSE)
  3143. #' @export
  3144. draw.clustComp <- function(pred_label, obs_label,strategy='ARI',
  3145. use_col=TRUE,low_K=5,
  3146. highlight_clust=NULL,
  3147. main=NULL,clust_cex=1,outlier_cex=0.3) {
  3148. #
  3149. all_input_para <- c('pred_label','obs_label','strategy','use_col','low_K','clust_cex','outlier_cex')
  3150. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3151. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3152. check_res <- c(check_option('use_col',c(TRUE,FALSE),envir=environment()),
  3153. check_option('strategy',c('ARI','NMI','Jaccard'),envir=environment()))
  3154. if(base::min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3155. #
  3156. if(is.null(names(pred_label))==TRUE & is.null(names(obs_label))==FALSE){
  3157. message('The names of pred_label will use the order of obs_label')
  3158. names(pred_label) <- names(obs_label)
  3159. }
  3160. if(is.null(names(obs_label))==TRUE & is.null(names(pred_label))==FALSE){
  3161. message('The names of obs_label will use the order of pred_label')
  3162. names(obs_label) <- names(pred_label)
  3163. }
  3164. if(is.null(names(obs_label))==TRUE & is.null(names(pred_label))==TRUE){
  3165. message('Assume pred_label and obs_label have the same order!')
  3166. names(obs_label) <- as.character(1:base::length(obs_label))
  3167. names(pred_label) <- names(obs_label)
  3168. }
  3169. nn <- names(pred_label)
  3170. k1 <- get_clustComp(pred_label,obs_label,strategy=strategy)
  3171. if(is.null(main)==TRUE){
  3172. mm <- sprintf('%s:%s',strategy,format(k1,digits=4),
  3173. format(k1,digits=4))
  3174. }else{
  3175. mm <- main
  3176. }
  3177. t1 <- base::table(list(pred_label[nn],obs_label[nn]))
  3178. graphics::layout(1)
  3179. textWidth <- base::max(strwidthMod(colnames(t1),units='inch',cex=clust_cex))+par.char2inch()[1]*1.5
  3180. par(mai=c(0.5,textWidth,1,1))
  3181. if(use_col==TRUE){
  3182. graphics::image(t1,col=c('white',grDevices::colorRampPalette(brewer.pal(8,'Reds'))(base::length(base::unique(as.numeric(t1)))-1)),bty='n',xaxt='n',yaxt='n',
  3183. main=mm)
  3184. }else{
  3185. graphics::image(t1,bty='n',xaxt='n',yaxt='n',
  3186. main=mm,col='white')
  3187. }
  3188. pp <- par()$usr
  3189. graphics::rect(xleft=pp[1],xright=pp[2],ybottom=pp[3],ytop=pp[4])
  3190. xx <- base::seq(pp[1],pp[2],length.out = nrow(t1)+1)
  3191. yy <- base::seq(pp[3],pp[4],length.out = ncol(t1)+1)
  3192. xxx <- (xx[1:(base::length(xx)-1)]+xx[2:base::length(xx)])/2
  3193. yyy <- (yy[1:(base::length(yy)-1)]+yy[2:base::length(yy)])/2
  3194. graphics::abline(h=yy);graphics::abline(v=xx)
  3195. graphics::text(pp[1]-par.char2pos()[1],yyy,colnames(t1),adj=1,xpd=TRUE,col=ifelse(colnames(t1) %in% highlight_clust,2,1),cex=clust_cex)
  3196. graphics::text(xxx,pp[4],rownames(t1),srt=0,xpd=TRUE,
  3197. col=ifelse(rownames(t1) %in% highlight_clust,2,1),cex=clust_cex,pos=3)
  3198. for(i in 1:nrow(t1)){
  3199. for(j in 1:ncol(t1)){
  3200. v1 <- t1[i,j]
  3201. if(v1==0) next
  3202. if(v1>low_K){
  3203. graphics::text(xxx[i],yyy[j],v1,cex=clust_cex)
  3204. }else{
  3205. v2 <- names(obs_label)[which(pred_label==rownames(t1)[i] & obs_label==colnames(t1)[j])]
  3206. v2 <- base::paste(v2,collapse='\n')
  3207. graphics::text(xxx[i],yyy[j],v2,cex=outlier_cex)
  3208. }
  3209. }
  3210. }
  3211. return(t1)
  3212. }
  3213. #' Set Color Scale for Z Statistics Value
  3214. #'
  3215. #' \code{z2col} is a helper function in \code{out2excel}. It defines the color scale of the Z statistics value.
  3216. #'
  3217. #' @param x a vector of numerics, a vector of Z statistics.
  3218. #' @param n_len integer, number of unique colors. Default is 60.
  3219. #' @param sig_thre numeric, the threshold for significance (absolute value of Z statistics). Z values failed to pass the threshold will be colored "white".
  3220. #' @param col_min_thre numeric, the lower threshold for the color bar value. Default is 0.01.
  3221. #' @param col_max_thre numeric, the upper threshold for the color bar value. Default is 3.
  3222. #' @param blue_col a vector of characters, the blue colors used to show the negative Z values. Default is brewer.pal(9,'Set1')[2].
  3223. #' @param red_col a vector of characters, the red colors used to show positive Z values. Default is brewer.pal(9,'Set1')[1].
  3224. #' @return Return a vector of color codes.
  3225. #' @examples
  3226. #' t1 <- sort(rnorm(mean=0,sd=2,n=100))
  3227. #' graphics::image(as.matrix(t1),col=z2col(t1))
  3228. #' @export
  3229. z2col <- function(x,n_len=60,sig_thre=0.01,col_min_thre=0.01,col_max_thre=3,
  3230. blue_col=brewer.pal(9,'Set1')[2],
  3231. red_col=brewer.pal(9,'Set1')[1]){
  3232. #
  3233. tmp_x <- setdiff(x,c(Inf,-Inf))
  3234. if(length(tmp_x)==0) return(ifelse(x>0,'red','blue'))
  3235. all_input_para <- c('x','n_len','sig_thre','col_min_thre','col_max_thre','blue_col','red_col')
  3236. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3237. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3238. #
  3239. ## create vector for z-score, can change sig threshold
  3240. x[which(is.na(x)==TRUE)] <- 0
  3241. x[which(x==Inf)]<- base::max(x[which(x!=Inf)])+1
  3242. x[which(x==-Inf)]<- base::min(x[which(x!=-Inf)])-1
  3243. if(col_min_thre<0) col_min_thre<-0.01
  3244. if(col_max_thre<0) col_max_thre<-3
  3245. c2 <- grDevices::colorRampPalette(c(blue_col,'white',red_col))(n_len)
  3246. r1 <- 1.05*base::max(abs(x)) ## -r1~r1
  3247. if(r1 < col_max_thre){
  3248. r1 <- col_max_thre
  3249. }
  3250. if(col_min_thre>r1){
  3251. r2 <- seq(-r1,r1,length.out=n_len+1)
  3252. }else{
  3253. r21 <- seq(-r1,-col_min_thre,length.out=n_len/2)
  3254. r22 <- base::seq(col_min_thre,r1,length.out=n_len/2)
  3255. r2 <- c(r21,r22)
  3256. }
  3257. x1 <- cut(x,r2)
  3258. names(c2) <- levels(x1)
  3259. x2 <- c2[x1]
  3260. x2[which(abs(x)<sig_thre)] <- 'white'
  3261. x2
  3262. }
  3263. #' Create Color Codes for a Vector of Characters
  3264. #'
  3265. #' \code{get.class.color} creates a vector of color codes for the input character vector. This is a helper function to assign nice looking colors for better visualization.
  3266. #'
  3267. #' @param x a vector of characters, names or labels.
  3268. #' @param use_color a vector of color codes, colors to be assigned to each member of \code{x}. Default is brewer.pal(9, 'Set1').
  3269. #' @param pre_define a vector of characters, pre-defined color codes for a certain input (e.g. c("blue", "red") with names c("A", "B")). Default is NULL.
  3270. #'
  3271. #' @return Return a vector of color codes, with input character vector as names.
  3272. #' @examples
  3273. #' get.class.color(c('ClassA','ClassB','ClassC','ClassA','ClassC','ClassC'))
  3274. #' get.class.color(c('ClassA','ClassB','ClassC','SHH','WNT','Group3','Group4'))
  3275. #' get.class.color(c('ClassA','ClassB','ClassC','SHH','WNT','Group3','Group4'),
  3276. #' use_color=brewer.pal(8, 'Set1'))
  3277. #'
  3278. #' pre_define <- c('blue', 'red', 'yellow', 'green','yellow', 'green')
  3279. #' ## pre-defined colors for MB
  3280. #' names(pre_define) <- c('WNT', 'SHH', 'Group3', 'Group4','GroupC', 'GroupD')
  3281. #' ##pre-defined color name for MB
  3282. #' get.class.color(c('ClassA','ClassB','ClassC','SHH','WNT','Group3','Group4'),
  3283. #' pre_define=pre_define)
  3284. #'
  3285. #' \dontrun{
  3286. #'}
  3287. #' @export
  3288. get.class.color <- function(x,use_color=NULL,pre_define=NULL) {
  3289. #
  3290. all_input_para <- c('x')
  3291. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3292. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3293. #
  3294. x <- clean_charVector(x);
  3295. #
  3296. if(is.null(pre_define)==FALSE & is.null(names(pre_define))==TRUE){
  3297. message('No class name for the color vector, please check and re-try !');return(FALSE);
  3298. }
  3299. x <- clean_charVector(x);
  3300. if(is.null(use_color)==TRUE){
  3301. use_color <- brewer.pal(9, 'Set1')
  3302. }
  3303. if (base::length(base::intersect(x, names(pre_define))) == 0) {
  3304. w1 <- base::length(base::unique(x))
  3305. if(w1 < length(use_color)){
  3306. cc2 <- use_color[1:w1]
  3307. }else{
  3308. cc2 <- grDevices::colorRampPalette(use_color)(base::length(base::unique(x)))
  3309. }
  3310. names(cc2) <- base::unique(x)
  3311. cc2 <- cc2[x]
  3312. } else{
  3313. x1 <- base::unique(x)
  3314. x2 <- base::setdiff(x1, names(pre_define))
  3315. cc1 <- NULL
  3316. w1 <- base::length(x2)
  3317. if (w1 > 0) {
  3318. if(w1 < length(use_color)){
  3319. cc1 <- use_color[1:w1]
  3320. }else{
  3321. cc1 <- grDevices::colorRampPalette(use_color)(w1)
  3322. }
  3323. names(cc1) <- x2
  3324. }
  3325. cc2 <- c(pre_define, cc1)
  3326. cc2 <- cc2[x]
  3327. }
  3328. return(cc2)
  3329. }
  3330. ## get color box text,inner function ## refer from web
  3331. # https://stackoverflow.com/questions/45366243/text-labels-with-background-colour-in-r
  3332. boxtext <- function(x, y, labels = NA, col.text = NULL, col.bg = NA,
  3333. border.bg = NA, adj = NULL, pos = NULL, offset = 0.5,
  3334. padding = c(0.5, 0.5), cex = 1, font = graphics::par('font')){
  3335. ## The Character expansion factro to be used:
  3336. theCex <- graphics::par('cex')*cex
  3337. ## Is y provided:
  3338. if (missing(y)) y <- x
  3339. ## Recycle coords if necessary:
  3340. if (base::length(x) != base::length(y)){
  3341. lx <- base::length(x)
  3342. ly <- base::length(y)
  3343. if (lx > ly){
  3344. y <- rep(y, ceiling(lx/ly))[1:lx]
  3345. } else {
  3346. x <- rep(x, ceiling(ly/lx))[1:ly]
  3347. }
  3348. }
  3349. ## Width and height of text
  3350. textHeight <- graphics::strheight(labels, cex = theCex, font = font)
  3351. textWidth <- graphics::strwidth(labels, cex = theCex, font = font)
  3352. ## Width of one character:
  3353. charWidth <- graphics::strwidth("e", cex = theCex, font = font)
  3354. ## Is 'adj' of length 1 or 2?
  3355. if (!is.null(adj)){
  3356. if (base::length(adj == 1)){
  3357. adj <- c(adj[1], 0.5)
  3358. }
  3359. } else {
  3360. adj <- c(0.5, 0.5)
  3361. }
  3362. ## Is 'pos' specified?
  3363. if (!is.null(pos)){
  3364. if (pos == 1){
  3365. adj <- c(0.5, 1)
  3366. offsetVec <- c(0, -offset*charWidth)
  3367. } else if (pos == 2){
  3368. adj <- c(1, 0.5)
  3369. offsetVec <- c(-offset*charWidth, 0)
  3370. } else if (pos == 3){
  3371. adj <- c(0.5, 0)
  3372. offsetVec <- c(0, offset*charWidth)
  3373. } else if (pos == 4){
  3374. adj <- c(0, 0.5)
  3375. offsetVec <- c(offset*charWidth, 0)
  3376. } else {
  3377. stop('Invalid argument pos')
  3378. }
  3379. } else {
  3380. offsetVec <- c(0, 0)
  3381. }
  3382. ## Padding for boxes:
  3383. if (base::length(padding) == 1){
  3384. padding <- c(padding[1], padding[1])
  3385. }
  3386. ## Midpoints for text:
  3387. xMid <- x + (-adj[1] + 1/2)*textWidth + offsetVec[1]
  3388. yMid <- y + (-adj[2] + 1/2)*textHeight + offsetVec[2]
  3389. ## Draw rectangles:
  3390. rectWidth <- textWidth + 2*padding[1]*charWidth
  3391. rectHeight <- textHeight + 2*padding[2]*charWidth
  3392. graphics::rect(xleft = xMid - rectWidth/2,ybottom = yMid - rectHeight/2,
  3393. xright = xMid + rectWidth/2,ytop = yMid + rectHeight/2,
  3394. col = col.bg, border = border.bg,xpd=TRUE)
  3395. ## Place the text:
  3396. graphics::text(xMid, yMid, labels, col = col.text, cex = theCex, font = font,adj = c(0.5, 0.5),xpd=TRUE)
  3397. ## Return value:
  3398. if (base::length(xMid) == 1){
  3399. invisible(c(xMid - rectWidth/2, xMid + rectWidth/2, yMid - rectHeight/2,yMid + rectHeight/2))
  3400. } else {
  3401. invisible(base::cbind(xMid - rectWidth/2, xMid + rectWidth/2, yMid - rectHeight/2,yMid + rectHeight/2))
  3402. }
  3403. }
  3404. #' Visualize Sample Clustering Result in 2D Plot
  3405. #'
  3406. #' \code{draw.2D} creats a 2D plot to visualize the sample clustering result.
  3407. #'
  3408. #' @param X a vector of numerics, the x coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the first component.
  3409. #' @param Y a vector of numerics, the y coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the second component.
  3410. #' @param class_label a vector of characters, labels or categories of samples. The vector name should be sample names.
  3411. #' @param xlab character, the label for x-axis. Default is "PC1".
  3412. #' @param ylab character, the label for y-axis. Default is "PC2".
  3413. #' @param legend_cex numeric, giving the amount by which the text of legend should be magnified relative to the default. Default is 0.8.
  3414. #' @param main character, an overall title for the plot. Default is "".
  3415. #' @param point_cex numeric, giving the amount by which the size of the data points should be magnified relative to the default. Default is 1.
  3416. #' @param use_color a vector of color codes, colors to be assigned to each member of display label. Default is brewer.pal(9, 'Set1').
  3417. #' @param pre_define a vector of characters, pre-defined color codes for a certain input (e.g. c("blue", "red") with names c("A", "B")). Default is NULL.
  3418. #'
  3419. #' @return Return a logical value. If TRUE, the plot has been created successfully.
  3420. #' @examples
  3421. #' mat1 <- matrix(rnorm(2000,mean=0,sd=1),nrow=100,ncol=20)
  3422. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  3423. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  3424. #' pc <- stats::prcomp(t(mat1))$x
  3425. #' pred_label <- kmeans(pc,centers=4)$cluster ## this can use other cluster results
  3426. #' draw.2D(X=pc[,1],Y=pc[,2],class_label=pred_label)
  3427. #' @export
  3428. draw.2D <- function(X,Y,class_label,xlab='PC1',ylab='PC2',legend_cex=0.8,main="",point_cex=1,use_color=NULL,pre_define=NULL){
  3429. #
  3430. all_input_para <- c('X','Y','class_label','xlab','ylab','legend_cex','main','point_cex')
  3431. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3432. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3433. class_label <- clean_charVector(class_label)
  3434. #
  3435. if(base::length(X)!=base::length(Y)){
  3436. message('Input two dimension vector with different length, please check and re-try !');return(FALSE);
  3437. }
  3438. if(base::length(X)!=base::length(class_label)){
  3439. message('Input dimension vector has different length with the class_label, please check and re-try !');return(FALSE);
  3440. }
  3441. par(mai = c(1, 1, 1, 0.5+base::max(strwidthMod(class_label,units='inch',cex=legend_cex,ori=FALSE,mod=FALSE))))
  3442. cls_cc <- get.class.color(class_label,use_color=use_color,pre_define=pre_define) ## get color for each label
  3443. graphics::plot(Y ~ X,pch = 16,cex = point_cex,col = cls_cc,main=main,xlab=xlab,ylab=ylab)
  3444. graphics::legend(par()$usr[2],par()$usr[4],sort(base::unique(class_label)),fill = cls_cc[sort(base::unique(class_label))],
  3445. horiz = FALSE,xpd = TRUE,border = NA,bty = 'n',cex=legend_cex)
  3446. return(TRUE)
  3447. }
  3448. #' Visualize Sample Clustering Result in 2D Plot with interactive mode
  3449. #'
  3450. #' \code{draw.2D.interactive} creats a 2D plot to visualize the sample clustering result with interactive mode realized by plotly.
  3451. #'
  3452. #' @param X a vector of numerics, the x coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the first component.
  3453. #' @param Y a vector of numerics, the y coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the second component.
  3454. #' @param sample_label a vector of characters, name of samples to be displayed on the figure.
  3455. #' @param color_label a vector of characters, labels used to define the point color.
  3456. #' @param shape_label a vector of characters, labels used to define the point shape.
  3457. #' @param xlab character, the label for x-axis. Default is "PC1".
  3458. #' @param ylab character, the label for y-axis. Default is "PC2".
  3459. #' @param main character, an overall title for the plot. Default is "".
  3460. #' @param point_cex numeric, giving the amount by which the size of the data points should be magnified relative to the default. Default is 1.
  3461. #' @param use_color a vector of color codes, colors to be assigned to each member of display label. Default is brewer.pal(9, 'Set1').
  3462. #' @param pre_define a vector of characters, pre-defined color codes for a certain input (e.g. c("blue", "red") with names c("A", "B")). Default is NULL.
  3463. #'
  3464. #' @return Return the plotly class object for interactive visualization.
  3465. #' @examples
  3466. #' mat1 <- matrix(rnorm(2000,mean=0,sd=1),nrow=100,ncol=20)
  3467. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  3468. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  3469. #' pc <- stats::prcomp(t(mat1))$x
  3470. #' pred_label <- kmeans(pc,centers=4)$cluster ## this can use other cluster results
  3471. #' draw.2D.interactive(X=pc[,1],Y=pc[,2],
  3472. #' sample_label=rownames(pc),
  3473. #' color_label=pred_label,
  3474. #' pre_define = c('1'='blue','2'='red','3'='yellow','4'='green'))
  3475. #' draw.2D.interactive(X=pc[,1],Y=pc[,2],
  3476. #' sample_label=rownames(pc),
  3477. #' shape_label=pred_label)
  3478. #' @export
  3479. draw.2D.interactive <- function(X,Y,sample_label=NULL,color_label=NULL,shape_label=NULL,
  3480. xlab='PC1',ylab='PC2',main="",point_cex=1,
  3481. use_color=NULL,pre_define=NULL){
  3482. if(!'plotly' %in% rownames(installed.packages())){
  3483. message('plotly not installed!');return(FALSE);
  3484. }
  3485. #
  3486. all_input_para <- c('X','Y','sample_label','xlab','ylab','main','point_cex')
  3487. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3488. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3489. sample_label <- clean_charVector(sample_label)
  3490. if(is.null(color_label)==FALSE){
  3491. color_label <- clean_charVector(color_label)
  3492. }
  3493. if(is.null(shape_label)==FALSE){
  3494. shape_label <- clean_charVector(shape_label)
  3495. }
  3496. #
  3497. if(base::length(X)!=base::length(Y)){
  3498. message('Input two dimension vector with different length, please check and re-try !');return(FALSE);
  3499. }
  3500. if(base::length(X)!=base::length(sample_label)){
  3501. message('Input dimension vector has different length with the sample_label, please check and re-try !');return(FALSE);
  3502. }
  3503. if(is.null(shape_label)==TRUE & is.null(color_label)==TRUE){
  3504. message('Either color_label or shape_label is required!');return(FALSE);
  3505. }
  3506. if(is.null(shape_label)==FALSE & is.null(color_label)==FALSE){
  3507. if(base::length(X)!=base::length(color_label)){
  3508. message('Input dimension vector has different length with the color_label, please check and re-try !');return(FALSE);
  3509. }
  3510. color_label.factor <- as.factor(color_label)
  3511. cls_cc <- get.class.color(levels(color_label.factor),use_color=use_color,pre_define=pre_define) ## get color for each label
  3512. if(base::length(X)!=base::length(shape_label)){
  3513. message('Input dimension vector has different length with the shape_label, please check and re-try !');return(FALSE);
  3514. }
  3515. shape_label.factor <- as.factor(shape_label)
  3516. data <- data.frame(X=X,Y=Y,color_label=color_label.factor,shape_label=shape_label.factor);
  3517. display_text <- paste0(sample_label,':',color_label,':',shape_label)
  3518. p <- plotly::plot_ly(data = data, x = ~X, y = ~Y,
  3519. marker = list(size = point_cex*12),type='scatter',color=~color_label,colors=cls_cc,
  3520. hoverinfo='text',text=display_text,
  3521. mode='markers',symbol=~shape_label) %>%
  3522. plotly::layout(title = main,
  3523. xaxis = list(zeroline = FALSE,title=list(text=xlab)),#20240327
  3524. yaxis = list(zeroline = FALSE,title=list(text=ylab),
  3525. showlegend=TRUE)
  3526. )
  3527. }
  3528. if(is.null(shape_label)==TRUE & is.null(color_label)==FALSE){
  3529. if(base::length(X)!=base::length(color_label)){
  3530. message('Input dimension vector has different length with the color_label, please check and re-try !');return(FALSE);
  3531. }
  3532. color_label.factor <- as.factor(color_label)
  3533. cls_cc <- get.class.color(levels(color_label.factor),use_color=use_color,pre_define=pre_define) ## get color for each label
  3534. data <- data.frame(X=X,Y=Y,color_label=color_label.factor);
  3535. display_text <- paste0(sample_label,':',color_label)
  3536. p <- plotly::plot_ly(data = data, x = ~X, y = ~Y,
  3537. marker = list(size = point_cex*12),type='scatter',color=~color_label,colors=cls_cc,
  3538. hoverinfo='text',text=display_text,
  3539. mode='markers') %>%
  3540. plotly::layout(title = main,
  3541. yaxis = list(zeroline = FALSE,title=list(text=xlab)),
  3542. xaxis = list(zeroline = FALSE,title=list(text=ylab),showlegend=TRUE)
  3543. )
  3544. }
  3545. if(is.null(shape_label)==FALSE & is.null(color_label)==TRUE){
  3546. if(base::length(X)!=base::length(shape_label)){
  3547. message('Input dimension vector has different length with the shape_label, please check and re-try !');return(FALSE);
  3548. }
  3549. shape_label.factor <- as.factor(shape_label)
  3550. data <- data.frame(X=X,Y=Y,shape_label=shape_label.factor);
  3551. display_text <- paste0(sample_label,':',shape_label)
  3552. p <- plotly::plot_ly(data = data, x = ~X, y = ~Y,
  3553. marker = list(size = point_cex*12),type='scatter',color = I('black'),
  3554. hoverinfo='text',text=display_text,
  3555. mode='markers',symbol=~shape_label) %>%
  3556. plotly::layout(title = main,
  3557. yaxis = list(zeroline = FALSE,title=list(text=xlab)),
  3558. xaxis = list(zeroline = FALSE,title=list(text=ylab))
  3559. )
  3560. }
  3561. return(p)
  3562. }
  3563. #' Visualize Sample Clustering Result in 2D Plot with Sample Names
  3564. #'
  3565. #' \code{draw.2D.text} creates a 2D plot with sample names labeled, to visualize the sample clustering result.
  3566. #'
  3567. #' @param X a vector of numerics, the x coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the first component.
  3568. #' @param Y a vector of numerics, the y coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the second component.
  3569. #' @param class_label a vector of characters, labels or categories of samples. The vector name should be sample names.
  3570. #' @param xlab character, the label for x-axis. Default is "PC1".
  3571. #' @param ylab character, the label for y-axis. Default is "PC2".
  3572. #' @param legend_cex numeric, giving the amount by which the text of legend should be magnified relative to the default. Default is 0.8.
  3573. #' @param main character, an overall title for the plot. Default is "".
  3574. #' @param point_cex numeric, giving the amount by which the size of the data points should be magnified relative to the default. Default is 1.
  3575. #' @param class_text a vector of characters, the user-defined sample names to label each data points in the plot.
  3576. #' If NULL, will use the names of \code{class_label}. Default is NULL.
  3577. #' @param text_cex numeric, giving the amount by which the text of \code{class_text} should be magnified relative to the default. Default is NULL.
  3578. #' @param use_color a vector of color codes, colors to be assigned to each member of display label. Default is brewer.pal(9, 'Set1').
  3579. #' @param pre_define a vector of characters, pre-defined color codes for a certain input (e.g. c("blue", "red") with names c("A", "B")). Default is NULL.
  3580. #'
  3581. #' @return Return a logical value. If TRUE, the plot has been created successfully.
  3582. #' @examples
  3583. #' mat1 <- matrix(rnorm(2000,mean=0,sd=1),nrow=100,ncol=20)
  3584. #' rownames(mat1) <- paste0('Gene',1:nrow(mat1))
  3585. #' colnames(mat1) <- paste0('Sample',1:ncol(mat1))
  3586. #' pc <- stats::prcomp(t(mat1))$x
  3587. #' pred_label <- kmeans(pc,centers=4)$cluster ## this can use other cluster results
  3588. #' draw.2D.text(X=pc[,1],Y=pc[,2],class_label=pred_label,
  3589. #' point_cex=5,text_cex=0.5)
  3590. #' @export
  3591. draw.2D.text <- function(X,Y,class_label,class_text=NULL,xlab='PC1',ylab='PC2',legend_cex=0.8,main="",
  3592. point_cex=1,text_cex=NULL,use_color=NULL,pre_define=NULL){
  3593. #
  3594. all_input_para <- c('X','Y','class_label','xlab','ylab','legend_cex','main','point_cex')
  3595. check_res <- sapply(all_input_para,function(x)check_para(x,envir=environment()))
  3596. if(min(check_res)==0){message('Please check and re-try!');return(FALSE)}
  3597. class_label <- clean_charVector(class_label)
  3598. #
  3599. if(base::length(X)!=base::length(Y)){
  3600. message('Input two dimension vector with different length, please check and re-try !');return(FALSE);
  3601. }
  3602. if(base::length(X)!=base::length(class_label)){
  3603. message('Input dimension vector has different length with the class_label, please check and re-try !');return(FALSE);
  3604. }
  3605. par(mai = c(1, 1, 1, 0.5+base::max(strwidthMod(class_label,units='inch',cex=legend_cex,ori=FALSE,mod=FALSE))))
  3606. if(is.null(class_text)==TRUE){
  3607. class_text <- names(class_label)
  3608. }
  3609. cc <- 10/base::length(class_label)
  3610. if(cc<0.05) cc<-0.05
  3611. if(cc>1) cc<-1
  3612. if(is.null(text_cex)==FALSE) cc <- text_cex
  3613. cls_cc <- get.class.color(class_label,use_color=use_color,pre_define=pre_define) ## get color for each label
  3614. graphics::plot(Y ~ X,pch = 16,cex = point_cex,col = cls_cc,main=main,xlab=xlab,ylab=ylab)
  3615. graphics::text(x=X,y=Y,labels=class_text,cex=cc,xpd=TRUE,adj=0.5)
  3616. #print(cc);print(str(nn))
  3617. graphics::legend(par()$usr[2],par()$usr[4],sort(base::unique(class_label)),fill = cls_cc[sort(base::unique(class_label))],
  3618. horiz = FALSE,xpd = TRUE,border = NA,bty = 'n',cex=legend_cex)
  3619. return(TRUE)
  3620. }
  3621. #' Visualize Sample Clustering Result in 3D Plot
  3622. #'
  3623. #' \code{draw.3D} creates a 3D plot to visualize the sample clustering result.
  3624. #' @param X a vector of numerics, the x coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the first component.
  3625. #' @param Y a vector of numerics, the y coordinates of points in the plot. If user would like to create a PCA biplot, this parameter should be the

pipeline_functions.R at commit 5defa45, under Apache-2.0 · at the source

Overview

Authors: Winson S Ho1,2, Isha Mondal1,2, Jingjing Liu3, Raymond Sun1,2, Jiawei Huo4, Chao Gao1,2, Oishika Das1,2, Daren Tieu1,2, Jingqi Sun4, Hanchen Lin4, Peng Zhang4, Jiyang Yu3, Rongze Olivia Lu1,2
ORCID iDs: Jiawei Huo, Jiyang Yu
  1. Department of Neurological Surgery, and
  2. Helen Diller Comprehensive Cancer Center, UCSF, San Francisco, California, USA
  3. Department of Computational Biology, St. Jude Children’s Research Hospital, Memphis, Tennessee, USA
  4. Department of Neurological Surgery, Malnati Brain Tumor Institute of the Robert H. Lurie Comprehensive Cancer Center, Feinberg School of Medicine, Northwestern University, Chicago, Illinois, USA
Journal: The Journal of clinical investigation, volume 136, issue 13, article e196753
Dates: received 24 July 2025; accepted 15 April 2026; published online 23 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1172/jci196753 · PMID 42085538 · PMCID PMC13318113 · OpenAlex W7160296515
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), mouse (organism), other condition (population), cellular / molecular (subfield)
Methods: Statistics, fMRI & imaging
Keywords: Immunology, Oncology, Cancer immunotherapy, Cellular senescence, Phosphoprotein phosphatases
MeSH: Cellular Senescence*, Cerebellar Neoplasms*, Medulloblastoma*, Neoplasm Proteins*, Neoplasms, Experimental*, Protein Phosphatase 2*, Animals, CD8-Positive T-Lymphocytes, Cell Line, Tumor, Humans, Mice, Mice, Knockout, Piperazines, Protein Phosphatase 2C (* major topic)
Topic: Protein Tyrosine Phosphatases (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: NINDS NIH HHS (R01 NS126501)
Citations: not cited yet (Europe PMC); 46 references in the paper

Abstract

Medulloblastoma (MB) is the most common malignant pediatric brain tumor. Current therapies are associated with substantial morbidity, and prognosis remains poor in high-risk subgroups, particularly those with TP53 mutations or relapsed disease. Cellular senescence is a tumor-suppressive program implicated in MB, but its role in antitumor immunity remains incompletely understood. We found that protein phosphatase 2A (PP2A) regulated immunogenic senescence in MB. Genetic ablation of the PP2A catalytic subunit PP2Ac or depletion of the regulatory subunit PP2A-B56α induced senescence in MB models. PP2Ac-deficient senescent cells exhibited increased MHC class I expression and enhanced immunogenicity. In syngeneic orthotopic models, PP2Ac loss prolonged survival in an immune- and CD8+ T cell–dependent manner. Analysis of patient datasets showed that senescence-associated gene signatures correlated with improved survival. Single-cell transcriptomic analysis further revealed that senescent MB cells were heterogeneous and that reduced PP2A activity was associated with an immunogenic senescence state. Because the PP2A inhibitor LB-100 has limited potency and off-target effects, we developed a lipid nanoparticle (LNP) platform to deliver siRNA targeting PPP2CA. LNP–small-interfering PP2Ac efficiently silenced PP2Ac in vitro and, when delivered locally in vivo, prolonged survival in a CD8+ T cell–dependent manner. Together, these findings identify PP2A as a regulator of immunogenic senescence in MB and support PP2Ac targeting as a therapeutic strategy.

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 1 match between paragraphs and lines of code.

jyyulab/NetBID

License: Apache-2.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 5defa454d600b94f5dd6d1f9f4428f99759a6821, 23 July 2026
Languages: R (7), JavaScript (3), Shell (2)
Size: 273 files, 12 scripts
Software Heritage: not archived
Found in: the text, “RNA-Seq.”
Holds: README, license file, environment (DESCRIPTION, Dockerfile, Dockerfile.cgc), continuous integration, documentation, 2 notebooks
Not found: CITATION.cff, tests
Tools: igraph (2 files), Plotly (2 files), ComplexHeatmap (1 file), DESeq2 (1 file), limma (1 file), lme4 (1 file), reshape2 (1 file), UMAP (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
14 files

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;
  • 12 scripts, each with its path and the digest of its content;
  • 1 match 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

Datasets cited

Data availability

Publicly available datasets analyzed in this study include GEO GSE85217 (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE85217) and GSE155446 (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE155446). Sequencing data generated in this study are available in the GEO under accession GSE302307 (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE302307). All other data generated in this study are provided in the Supporting Data Values file or are available from the corresponding author upon reasonable request. Supporting data values underlying all figures are provided in an XLS file.

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

Versions

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

Version 1, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 13 authors, 5 keywords, 14 MeSH terms, 1 funder, 46 references.

Cite

This paper

Ho, W. S., Mondal, I., Liu, J., Sun, R., Huo, J., Gao, C., Das, O., Tieu, D., Sun, J., Lin, H., Zhang, P., Yu, J., & Lu, R. O. (2026). Protein phosphatase 2A regulates senescence and immunogenicity in medulloblastoma models. The Journal of clinical investigation, 136(13), e196753. https://doi.org/10.1172/jci196753

BibTeX

@article{ho2026protein,
author = {Ho, Winson S and Mondal, Isha and Liu, Jingjing and Sun, Raymond and Huo, Jiawei and Gao, Chao and Das, Oishika and Tieu, Daren and Sun, Jingqi and Lin, Hanchen and Zhang, Peng and Yu, Jiyang and Lu, Rongze Olivia},
title = {{Protein phosphatase 2A regulates senescence and immunogenicity in medulloblastoma models}},
journal = {The Journal of clinical investigation},
year = {2026},
month = apr,
volume = {136},
number = {13},
pages = {e196753},
publisher = {American Society for Clinical Investigation},
issn = {0021-9738},
doi = {10.1172/jci196753},
url = {https://doi.org/10.1172/jci196753},
pmid = {42085538},
pmcid = {PMC13318113}
}

RIS

TY - JOUR
AU - Ho, Winson S
AU - Mondal, Isha
AU - Liu, Jingjing
AU - Sun, Raymond
AU - Huo, Jiawei
AU - Gao, Chao
AU - Das, Oishika
AU - Tieu, Daren
AU - Sun, Jingqi
AU - Lin, Hanchen
AU - Zhang, Peng
AU - Yu, Jiyang
AU - Lu, Rongze Olivia
TI - Protein phosphatase 2A regulates senescence and immunogenicity in medulloblastoma models
T2 - The Journal of clinical investigation
J2 - J Clin Invest
PY - 2026
DA - 2026/04/23
VL - 136
IS - 13
SP - e196753
SN - 0021-9738
PB - American Society for Clinical Investigation
DO - 10.1172/jci196753
UR - https://doi.org/10.1172/jci196753
LA - en
ER -

CSL-JSON

{
"id": "10.1172/jci196753",
"type": "article-journal",
"title": "Protein phosphatase 2A regulates senescence and immunogenicity in medulloblastoma models",
"container-title": "The Journal of clinical investigation",
"author": [
{
"family": "Ho",
"given": "Winson S"
},
{
"family": "Mondal",
"given": "Isha"
},
{
"family": "Liu",
"given": "Jingjing"
},
{
"family": "Sun",
"given": "Raymond"
},
{
"family": "Huo",
"given": "Jiawei"
},
{
"family": "Gao",
"given": "Chao"
},
{
"family": "Das",
"given": "Oishika"
},
{
"family": "Tieu",
"given": "Daren"
},
{
"family": "Sun",
"given": "Jingqi"
},
{
"family": "Lin",
"given": "Hanchen"
},
{
"family": "Zhang",
"given": "Peng"
},
{
"family": "Yu",
"given": "Jiyang"
},
{
"family": "Lu",
"given": "Rongze Olivia"
}
],
"container-title-short": "J Clin Invest",
"volume": "136",
"issue": "13",
"page": "e196753",
"DOI": "10.1172/jci196753",
"PMID": "42085538",
"PMCID": "PMC13318113",
"ISSN": "0021-9738",
"publisher": "American Society for Clinical Investigation",
"URL": "https://doi.org/10.1172/jci196753",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
23
]
]
}
}

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.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: limma, UMAP, igraph, 5 other tools, other condition, cellular / molecular
[2] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: limma, UMAP, igraph, 5 other tools, mouse, cellular / molecular
[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: limma, UMAP, igraph, 5 other tools, other condition
[4] doi:10.3390/ijms27104466 [code]
Uncovering the Key Circuit FOSL2/FOS/EGR3/EGR1, Contributing to the Hyperexcitability of Excitatory Neurons in the Epileptic Temporal Cortex and Hippocampus.
Journal: International journal of molecular sciences
In common: limma, UMAP, igraph, 4 other tools
[5] doi:10.1002/ctm2.70683 [code]
Niacin promotes motor function recovery after spinal cord injury via Hcar2-dependent microglia immunometabolic regulation.
Journal: Clinical and translational medicine
In common: limma, UMAP, igraph, 3 other tools, other condition, mouse, cellular / molecular
[6] doi:10.1038/s44318-026-00818-9 [code]
FAM134B-mediated ER-phagy degrades APP and suppresses Alzheimer's disease pathology.
Journal: The EMBO journal
In common: limma, UMAP, igraph, 3 other tools, mouse, cellular / molecular
[7] doi:10.1016/j.xcrm.2026.102651 [code]
Integrative CSF profiling identifies disease-specific immune responses in leptomeningeal disease.
Journal: Cell reports. Medicine
In common: UMAP, igraph, DESeq2, 3 other tools, other condition, cellular / molecular
[8] doi:10.1186/s12974-026-03838-8 [code]
Acarbose modulates microglial Pkm2 acetylation to reshape immunometabolism and preserve retinal neurons after ischemia-reperfusion.
Journal: Journal of neuroinflammation
In common: limma, UMAP, igraph, 3 other tools, mouse, cellular / molecular
[9] doi:10.1002/imt2.70163 [code]
Spatial multi-omics unveils sphingolipid metabolic reprogramming within the retinal pathological niche.
Journal: iMeta
In common: limma, igraph, DESeq2, 3 other tools, mouse, cellular / molecular
[10] 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: limma, igraph, DESeq2, 3 other tools, mouse, cellular / molecular

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.