| Title: | GExPipe: Gene Expression Pipeline Shiny Application |
|---|---|
| Description: | Shiny application (GExPipe) for high-throughput genomic analysis of bulk RNA-seq and microarray data (e.g. from GEO). Four analysis types: RNA-seq only, microarray only, Merged (Both) (per-dataset normalize then one joint batch and limma DE), and Parallel DE then merge (separate pipelines through DE, then RNA-seq intersect microarray). Each RNA-seq and microarray box accepts one or more GSE IDs in the same run. Integrates with Bioconductor (GEOquery, Biobase, limma, DESeq2, edgeR, clusterProfiler) for download, QC, normalization, batch correction, differential expression, WGCNA, pathway enrichment, PPI, and machine learning. Uses common data structures (ExpressionSet, DGEList) for interoperability. For a full dependency tree (including STRINGdb + PPI helpers), use BiocManager::install("GExPipe", dependencies = TRUE). STRING data are downloaded on first PPI use (internet required); they cannot be bundled in the package. Microarray CEL normalization uses affy and/or oligo when supplementary CEL files are available. |
| Authors: | Safa Rafique [aut, cre] (ORCID: <https://orcid.org/0000-0003-2646-8106>), Naeem Mahmood Ashraf [aut], Prof. Dr. Muhammad Farooq Sabar [aut] |
| Maintainer: | Safa Rafique <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.117 |
| Built: | 2026-10-28 00:10:31 UTC |
| Source: | https://github.com/bioc/GExPipe |
GExPipe users enter GSE accessions in the app; this helper runs
automatically during GEO download when a count file has no sample headers
(e.g. data.table::fread assigns V2, V3, ...). Columns are renamed
to GSM IDs from GEO pData so QC, normalization, and group selection see
individual samples (fixes issues such as a single V2 bar in outlier QC).
gexp_align_rnaseq_sample_names(count_matrix, metadata = NULL, gse_id = NULL)gexp_align_rnaseq_sample_names(count_matrix, metadata = NULL, gse_id = NULL)
count_matrix |
Numeric matrix (genes x samples). |
metadata |
Optional GEO pData with sample IDs as row names. |
gse_id |
GEO series accession (used for fallback naming). |
Matrix with improved column names.
# GSE137136-style: headerless columns after download, GSM IDs in metadata. mat <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("GENE1", "GENE2"), c("V2", "V3"))) meta <- data.frame(title = c("sample 1", "sample 2"), row.names = c("GSM111", "GSM222")) out <- gexp_align_rnaseq_sample_names(mat, meta, "GSE137136") colnames(out)# GSE137136-style: headerless columns after download, GSM IDs in metadata. mat <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("GENE1", "GENE2"), c("V2", "V3"))) meta <- data.frame(title = c("sample 1", "sample 2"), row.names = c("GSM111", "GSM222")) out <- gexp_align_rnaseq_sample_names(mat, meta, "GSE137136") colnames(out)
This helper wraps the Step 5 logic:
filters genes by a variance percentile,
applies one of several batch-correction methods,
reports gene reduction statistics.
gexp_batch_correct( expr, metadata, variance_percentile = 25, method = c("combat_ref", "sva", "limma", "combat", "quantile_limma", "hybrid") )gexp_batch_correct( expr, metadata, variance_percentile = 25, method = c("combat_ref", "sva", "limma", "combat", "quantile_limma", "hybrid") )
expr |
Combined expression matrix (genes x samples), after normalization. |
metadata |
Data.frame with at least columns |
variance_percentile |
Numeric between 0 and 50; bottom percentile to remove. |
method |
Batch method: one of "limma", "combat", "combat_ref", "quantile_limma", "hybrid", "sva". |
A list with elements:
expr_filtered |
filtered expression matrix before batch correction |
batch_corrected |
batch-corrected matrix |
genes_before |
integer, genes before filtering |
genes_after |
integer, genes after filtering |
filter_percent |
numeric, percent genes removed |
log_text |
character, human-readable log string |
expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) metadata <- data.frame( Dataset = rep(c("D1", "D2"), each = 5), Condition = rep(c("Normal", "Disease"), times = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) out <- gexp_batch_correct(expr, metadata, variance_percentile = 10, method = "limma") dim(out$batch_corrected)expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) metadata <- data.frame( Dataset = rep(c("D1", "D2"), each = 5), Condition = rep(c("Normal", "Disease"), times = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) out <- gexp_batch_correct(expr, metadata, variance_percentile = 10, method = "limma") dim(out$batch_corrected)
Finalize common genes and combined matrix after download/mapping
gexp_download_finalize_common_genes( micro_expr_list, rna_counts_list, all_genes_list, subset_matrices = FALSE, keep_platforms_separate = FALSE )gexp_download_finalize_common_genes( micro_expr_list, rna_counts_list, all_genes_list, subset_matrices = FALSE, keep_platforms_separate = FALSE )
micro_expr_list |
Named list of microarray matrices (genes x samples). |
rna_counts_list |
Named list of RNA-seq matrices (genes x samples). |
all_genes_list |
Named list of rowname vectors per dataset. |
subset_matrices |
If |
keep_platforms_separate |
If |
List with updated lists, common_genes, combined_expr_raw, and status.
m1 <- matrix(1:12, nrow = 3, dimnames = list(c("A", "B", "C"), paste0("S", 1:4))) m2 <- matrix(1:12, nrow = 3, dimnames = list(c("B", "C", "D"), paste0("T", 1:4))) out <- gexp_download_finalize_common_genes( micro_expr_list = list(D1 = m1), rna_counts_list = list(D2 = m2), all_genes_list = list(D1 = rownames(m1), D2 = rownames(m2)) ) out$common_genesm1 <- matrix(1:12, nrow = 3, dimnames = list(c("A", "B", "C"), paste0("S", 1:4))) m2 <- matrix(1:12, nrow = 3, dimnames = list(c("B", "C", "D"), paste0("T", 1:4))) out <- gexp_download_finalize_common_genes( micro_expr_list = list(D1 = m1), rna_counts_list = list(D2 = m2), all_genes_list = list(D1 = rownames(m1), D2 = rownames(m2)) ) out$common_genes
Normalize dataset row IDs to gene symbols for overlap
gexp_download_normalize_ids_for_overlap( micro_expr_list, rna_counts_list, platform_per_gse = NULL, all_genes_list = NULL, micro_eset_list = NULL )gexp_download_normalize_ids_for_overlap( micro_expr_list, rna_counts_list, platform_per_gse = NULL, all_genes_list = NULL, micro_eset_list = NULL )
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
platform_per_gse |
Optional named list/vector of GPL IDs. |
all_genes_list |
Optional prebuilt gene lists. |
micro_eset_list |
Optional named list of |
List with updated lists and appended log text.
m <- matrix(1:6, nrow = 2, dimnames = list(c("A", "B"), paste0("S", 1:3))) r <- matrix(1:6, nrow = 2, dimnames = list(c("B", "C"), paste0("T", 1:3))) out <- gexp_download_normalize_ids_for_overlap( micro_expr_list = list(M = m), rna_counts_list = list(R = r) ) names(out)m <- matrix(1:6, nrow = 2, dimnames = list(c("A", "B"), paste0("S", 1:3))) r <- matrix(1:6, nrow = 2, dimnames = list(c("B", "C"), paste0("T", 1:3))) out <- gexp_download_normalize_ids_for_overlap( micro_expr_list = list(M = m), rna_counts_list = list(R = r) ) names(out)
Download and parse one microarray GSE
gexp_download_one_microarray_gse(gse_id, micro_dir, download_cel = NULL, fast = NULL)gexp_download_one_microarray_gse(gse_id, micro_dir, download_cel = NULL, fast = NULL)
gse_id |
GEO series ID. |
micro_dir |
Directory for supplementary files. |
download_cel |
Logical; if |
fast |
Logical; if |
List with status, log text, reason, expression, metadata, eset, platform_id, and cel_paths.
if (interactive()) { out <- gexp_download_one_microarray_gse("GSE10072", tempdir()) out$ok }if (interactive()) { out <- gexp_download_one_microarray_gse("GSE10072", tempdir()) out$ok }
Download and parse one RNA-seq GSE
gexp_download_one_rnaseq_gse(gse_id, rna_dir, fast = NULL)gexp_download_one_rnaseq_gse(gse_id, rna_dir, fast = NULL)
gse_id |
GEO series ID. |
rna_dir |
Directory containing |
fast |
Logical; if |
List with status, reason, log text, count matrix, and metadata.
if (interactive()) { out <- gexp_download_one_rnaseq_gse("GSE50760", tempdir()) out$ok }if (interactive()) { out <- gexp_download_one_rnaseq_gse("GSE50760", tempdir()) out$ok }
Fetch sample metadata from GEO series matrix fallback
gexp_fetch_geo_series_matrix_metadata(gse_id)gexp_fetch_geo_series_matrix_metadata(gse_id)
gse_id |
GEO series ID. |
data.frame or NULL.
if (interactive()) { md <- gexp_fetch_geo_series_matrix_metadata("GSE10072") if (!is.null(md)) head(md) }if (interactive()) { md <- gexp_fetch_geo_series_matrix_metadata("GSE10072") if (!is.null(md)) head(md) }
Build diagnostic log text when no common genes are found
gexp_no_common_genes_diagnostic_log(all_genes_list)gexp_no_common_genes_diagnostic_log(all_genes_list)
all_genes_list |
Named list of row IDs per dataset. |
Character string for log appending.
txt <- gexp_no_common_genes_diagnostic_log( list(D1 = c("1007_s_at", "1053_at"), D2 = c("ENSG000001", "ENSG000002")) ) nchar(txt) > 0txt <- gexp_no_common_genes_diagnostic_log( list(D1 = c("1007_s_at", "1053_at"), D2 = c("ENSG000001", "ENSG000002")) ) nchar(txt) > 0
This is a non-Shiny helper version of the Step 2 (Normalize) logic:
per-dataset normalization (microarray + RNA-seq),
intersection of gene sets (common genes),
global quantile normalization of the combined matrix,
optional extraction of RNA-seq raw counts for count-based DE.
gexp_normalize_and_intersect( micro_expr_list, rna_counts_list, micro_norm_method = "quantile", rnaseq_norm_method = "TMM", micro_cel_paths = NULL, platform_per_gse = NULL, micro_eset_list = NULL, de_method = "limma", apply_global_quantile = TRUE, keep_platforms_separate = FALSE )gexp_normalize_and_intersect( micro_expr_list, rna_counts_list, micro_norm_method = "quantile", rnaseq_norm_method = "TMM", micro_cel_paths = NULL, platform_per_gse = NULL, micro_eset_list = NULL, de_method = "limma", apply_global_quantile = TRUE, keep_platforms_separate = FALSE )
micro_expr_list |
named list of microarray expression matrices. |
rna_counts_list |
named list of RNA-seq count matrices. |
micro_norm_method |
"quantile" or "rma" (RMA requires CEL paths). |
rnaseq_norm_method |
"TMM" or "log2cpm_only". |
micro_cel_paths |
optional named list of CEL paths per GSE (for RMA). |
platform_per_gse |
optional named vector giving platform IDs per GSE. |
micro_eset_list |
optional named list of ExpressionSet objects per GSE (for RMA ID mapping). |
de_method |
differential expression method (used to decide whether to save raw counts for DESeq2/edgeR/limma-voom); one of "limma", "limma_voom", "deseq2", "edger". |
apply_global_quantile |
logical; if TRUE, apply limma quantile normalization across all samples. |
keep_platforms_separate |
logical; if TRUE (Parallel DE), normalize each platform on its own gene set. Do not intersect RNA-seq with microarray, do not drop genes low on the other platform, and do not apply global quantile. Matrices meet only at Step 7 consensus DEGs. |
A list with elements:
combined_expr |
globally quantile-normalized matrix (genes x samples) |
combined_expr_before_global |
matrix before global quantile |
all_expr_norm_list |
normalized per-dataset matrices |
expr_micro |
microarray-only normalized matrix (or NULL) |
expr_rna |
RNA-seq-only normalized matrix (or NULL) |
common_genes |
character vector of overlapping symbols (applied to matrices only when not keep_platforms_separate) |
normalization_stats |
list of per-dataset normalization info |
normalization_summary_table |
data.frame summarizing gene counts |
raw_counts_for_deseq2 |
integer matrix of RNA-seq counts (optional) |
raw_counts_metadata |
data.frame with sample metadata for raw counts (optional) |
unified_metadata |
data.frame with SampleID, Platform, Dataset, Condition=NA |
log_text |
character string with a human-readable log |
out <- withr::with_seed(1, { m1 <- matrix(abs(rnorm(120)), nrow = 20, ncol = 6) m2 <- matrix(abs(rnorm(120)), nrow = 20, ncol = 6) rownames(m1) <- rownames(m2) <- paste0("Gene", seq_len(20)) colnames(m1) <- paste0("D1_S", seq_len(6)) colnames(m2) <- paste0("D2_S", seq_len(6)) gexp_normalize_and_intersect( micro_expr_list = list(D1 = m1, D2 = m2), rna_counts_list = list(), de_method = "limma" ) }) dim(out$combined_expr)out <- withr::with_seed(1, { m1 <- matrix(abs(rnorm(120)), nrow = 20, ncol = 6) m2 <- matrix(abs(rnorm(120)), nrow = 20, ncol = 6) rownames(m1) <- rownames(m2) <- paste0("Gene", seq_len(20)) colnames(m1) <- paste0("D1_S", seq_len(6)) colnames(m2) <- paste0("D2_S", seq_len(6)) gexp_normalize_and_intersect( micro_expr_list = list(D1 = m1, D2 = m2), rna_counts_list = list(), de_method = "limma" ) }) dim(out$combined_expr)
RNA-seq and microarray boxes are always read. If both contain GSE IDs and the user did not choose Parallel, the run is treated as Merged. Parallel keeps both platforms separate through DE and accepts several GSEs per box.
gexp_parse_gse_inputs( analysis_type, rnaseq_gses = "", microarray_gses = "", dataset_mode = "multi" )gexp_parse_gse_inputs( analysis_type, rnaseq_gses = "", microarray_gses = "", dataset_mode = "multi" )
analysis_type |
One of "rnaseq", "microarray", "merged", or "parallel". |
rnaseq_gses |
Text field for RNA-seq IDs. |
microarray_gses |
Text field for microarray IDs. |
dataset_mode |
"single" or "multi". Single truncates only for RNA-seq-only or microarray-only. Merged and Parallel keep every GSE in each platform box. |
List with rnaseq_ids, micro_ids, dataset_mode, analysis_type, and messages.
x <- gexp_parse_gse_inputs( analysis_type = "parallel", rnaseq_gses = "GSE1, GSE2", microarray_gses = "GSE3, GSE4", dataset_mode = "single" ) x$rnaseq_idsx <- gexp_parse_gse_inputs( analysis_type = "parallel", rnaseq_gses = "GSE1, GSE2", microarray_gses = "GSE3, GSE4", dataset_mode = "single" ) x$rnaseq_ids
Prepare clean download directories for current run
gexp_prepare_download_dirs( base_dir = getwd(), has_micro = FALSE, has_rna = FALSE, clear_cache = NULL )gexp_prepare_download_dirs( base_dir = getwd(), has_micro = FALSE, has_rna = FALSE, clear_cache = NULL )
base_dir |
Base working directory. |
has_micro |
Logical; whether microarray IDs exist. |
has_rna |
Logical; whether RNA-seq IDs exist. |
clear_cache |
If |
Character vector log lines describing cleanup.
td <- tempdir() gexp_prepare_download_dirs(td, has_micro = TRUE, has_rna = TRUE)td <- tempdir() gexp_prepare_download_dirs(td, has_micro = TRUE, has_rna = TRUE)
Used by QC outlier plots before normalization builds unified metadata.
gexp_qc_build_sample_dataset_map(micro_expr_list, rna_counts_list)gexp_qc_build_sample_dataset_map(micro_expr_list, rna_counts_list)
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
Named character vector: sample ID -> GSE accession.
m <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("A", "B"), c("S1", "S2"))) r <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("A", "B"), c("S3", "S4"))) gexp_qc_build_sample_dataset_map(list(GSE1 = m), list(GSE2 = r))m <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("A", "B"), c("S1", "S2"))) r <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c("A", "B"), c("S3", "S4"))) gexp_qc_build_sample_dataset_map(list(GSE1 = m), list(GSE2 = r))
Uses two methods on the top variable genes:
PCA Mahalanobis distance (97.5% chi-square cutoff),
Sample connectivity in signed network (power = 6; mean - 2*SD threshold).
gexp_qc_detect_outliers(expr, top_n = 5000L)gexp_qc_detect_outliers(expr, top_n = 5000L)
expr |
Numeric matrix with genes in rows and samples in columns. |
top_n |
Integer number of top variable genes to use (default 5000). |
List with outlier detection results.
expr <- matrix(rnorm(400), nrow = 40, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) out <- gexp_qc_detect_outliers(expr, top_n = 20) length(out$all_outliers)expr <- matrix(rnorm(400), nrow = 40, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) out <- gexp_qc_detect_outliers(expr, top_n = 20) length(out$all_outliers)
Combined-matrix PCA on mixed microarray + RNA-seq (or un-normalized
multi-study counts) is not comparable. This helper runs
gexp_qc_detect_outliers on each GSE separately and unions the flags.
gexp_qc_detect_outliers_per_dataset( micro_expr_list = list(), rna_counts_list = list(), top_n = 5000L )gexp_qc_detect_outliers_per_dataset( micro_expr_list = list(), rna_counts_list = list(), top_n = 5000L )
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
top_n |
Integer number of top variable genes per dataset. |
List with pca_outliers, conn_outliers, all_outliers,
skipped (datasets with fewer than 5 samples), and log.
set.seed(1) make_mat <- function(prefix) { m <- matrix(rnorm(400), nrow = 40, ncol = 10) dimnames(m) <- list(paste0("Gene", seq_len(40)), paste0(prefix, seq_len(10))) m } out <- gexp_qc_detect_outliers_per_dataset( micro_expr_list = list(GSE_A = make_mat("A")), rna_counts_list = list(GSE_B = make_mat("B")), top_n = 20 ) out$all_outliersset.seed(1) make_mat <- function(prefix) { m <- matrix(rnorm(400), nrow = 40, ncol = 10) dimnames(m) <- list(paste0("Gene", seq_len(40)), paste0(prefix, seq_len(10))) m } out <- gexp_qc_detect_outliers_per_dataset( micro_expr_list = list(GSE_A = make_mat("A")), rna_counts_list = list(GSE_B = make_mat("B")), top_n = 20 ) out$all_outliers
Exclude selected samples from download/QC state lists
gexp_qc_exclude_samples( combined_expr_raw, micro_expr_list, rna_counts_list, unified_metadata = NULL, samples_to_exclude )gexp_qc_exclude_samples( combined_expr_raw, micro_expr_list, rna_counts_list, unified_metadata = NULL, samples_to_exclude )
combined_expr_raw |
Matrix genes x samples. |
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
unified_metadata |
Optional metadata data.frame with SampleID column. |
samples_to_exclude |
Character vector of sample IDs. |
List containing updated matrices/lists/metadata.
expr <- matrix(rnorm(300), nrow = 30, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame(SampleID = colnames(expr), stringsAsFactors = FALSE) res <- gexp_qc_exclude_samples( combined_expr_raw = expr, micro_expr_list = list(D1 = expr[, 1:5, drop = FALSE]), rna_counts_list = list(D2 = expr[, 6:10, drop = FALSE]), unified_metadata = meta, samples_to_exclude = c("S1", "S10") ) ncol(res$combined_expr_raw)expr <- matrix(rnorm(300), nrow = 30, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame(SampleID = colnames(expr), stringsAsFactors = FALSE) res <- gexp_qc_exclude_samples( combined_expr_raw = expr, micro_expr_list = list(D1 = expr[, 1:5, drop = FALSE]), rna_counts_list = list(D2 = expr[, 6:10, drop = FALSE]), unified_metadata = meta, samples_to_exclude = c("S1", "S10") ) ncol(res$combined_expr_raw)
Build gene-overlap summary table for QC UI
gexp_qc_gene_overlap_summary(all_genes_list, common_genes)gexp_qc_gene_overlap_summary(all_genes_list, common_genes)
all_genes_list |
Named list of gene vectors by dataset. |
common_genes |
Character vector of common genes. |
Data.frame with Dataset, Total_Genes, Pct_in_Common.
all_genes <- list( D1 = c("A", "B", "C", "D"), D2 = c("B", "C", "E") ) gexp_qc_gene_overlap_summary(all_genes, common_genes = c("B", "C"))all_genes <- list( D1 = c("A", "B", "C", "D"), D2 = c("B", "C", "E") ) gexp_qc_gene_overlap_summary(all_genes, common_genes = c("B", "C"))
Prepare QC boxplot data from combined expression
gexp_qc_prepare_boxplot_data( combined_expr_raw, micro_expr_list, rna_counts_list, max_points = 500000L )gexp_qc_prepare_boxplot_data( combined_expr_raw, micro_expr_list, rna_counts_list, max_points = 500000L )
combined_expr_raw |
Matrix genes x samples. |
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
max_points |
Maximum rows to keep for plotting. |
data.frame with Expression, Sample, Platform.
expr <- matrix(rnorm(120), nrow = 12, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) df <- gexp_qc_prepare_boxplot_data( combined_expr_raw = expr, micro_expr_list = list(D1 = expr[, 1:5, drop = FALSE]), rna_counts_list = list(D2 = expr[, 6:10, drop = FALSE]), max_points = 1000 ) head(df)expr <- matrix(rnorm(120), nrow = 12, ncol = 10) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) df <- gexp_qc_prepare_boxplot_data( combined_expr_raw = expr, micro_expr_list = list(D1 = expr[, 1:5, drop = FALSE]), rna_counts_list = list(D2 = expr[, 6:10, drop = FALSE]), max_points = 1000 ) head(df)
Prepare density curves for QC density plot
gexp_qc_prepare_density_data(combined_expr_raw, max_samples = 50L)gexp_qc_prepare_density_data(combined_expr_raw, max_samples = 50L)
combined_expr_raw |
Matrix genes x samples. |
max_samples |
Maximum number of sample density lines to include. |
List with first density, additional densities, and colors.
expr <- matrix(rnorm(200), nrow = 20, ncol = 10) d <- gexp_qc_prepare_density_data(expr, max_samples = 5) length(d$others)expr <- matrix(rnorm(200), nrow = 20, ncol = 10) d <- gexp_qc_prepare_density_data(expr, max_samples = 5) length(d$others)
Prepare UpSet matrix data from per-dataset genes
gexp_qc_prepare_upset_data(all_genes_list)gexp_qc_prepare_upset_data(all_genes_list)
all_genes_list |
Named list of gene vectors by dataset. |
List with ok, upset_df, gene_lists, max_set_size, and message.
sets <- list( D1 = c("A", "B", "C"), D2 = c("B", "C", "D") ) out <- gexp_qc_prepare_upset_data(sets) names(out)sets <- list( D1 = c("A", "B", "C"), D2 = c("B", "C", "D") ) out <- gexp_qc_prepare_upset_data(sets) names(out)
Prepare cleaned gene sets for Venn plotting
gexp_qc_prepare_venn_sets(all_genes_list, max_sets = 5L)gexp_qc_prepare_venn_sets(all_genes_list, max_sets = 5L)
all_genes_list |
Named list of gene vectors by dataset. |
max_sets |
Maximum number of sets to keep for Venn plotting. |
List with ok, sets, and message.
sets <- list( D1 = c("A", "B", "C"), D2 = c("B", "C", "D"), D3 = c("A", "D") ) out <- gexp_qc_prepare_venn_sets(sets) out$oksets <- list( D1 = c("A", "B", "C"), D2 = c("B", "C", "D"), D3 = c("A", "D") ) out <- gexp_qc_prepare_venn_sets(sets) out$ok
Rebuild per-dataset gene lists from expression/count lists
gexp_rebuild_all_genes_list(micro_expr_list, rna_counts_list)gexp_rebuild_all_genes_list(micro_expr_list, rna_counts_list)
micro_expr_list |
Named list of microarray matrices. |
rna_counts_list |
Named list of RNA-seq matrices. |
Named list of gene vectors.
m <- matrix(1:6, nrow = 2, dimnames = list(c("A", "B"), paste0("S", 1:3))) r <- matrix(1:6, nrow = 2, dimnames = list(c("B", "C"), paste0("T", 1:3))) gexp_rebuild_all_genes_list(list(M = m), list(R = r))m <- matrix(1:6, nrow = 2, dimnames = list(c("A", "B"), paste0("S", 1:3))) r <- matrix(1:6, nrow = 2, dimnames = list(c("B", "C"), paste0("T", 1:3))) gexp_rebuild_all_genes_list(list(M = m), list(R = r))
Run differential expression analysis
gexp_run_de( expr, metadata, method = c("limma", "limma_voom", "deseq2", "edger"), logfc_cutoff = 0.5, padj_cutoff = 0.05 )gexp_run_de( expr, metadata, method = c("limma", "limma_voom", "deseq2", "edger"), logfc_cutoff = 0.5, padj_cutoff = 0.05 )
expr |
Matrix of (batch-corrected, normalized) expression values with genes in rows and samples in columns. |
metadata |
Data.frame with at least a |
method |
Currently only |
logfc_cutoff |
Numeric log2 fold-change cutoff. |
padj_cutoff |
Numeric adjusted P-value cutoff. |
list with de_results, sig_genes, filter_note, sample_info, formula_desc
expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("G", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) gexp_run_de(expr, meta)expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("G", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) gexp_run_de(expr, meta)
This helper mirrors the "Prepare WGCNA Data" step:
uses batch-corrected expression (or combined expression if needed),
drops samples/genes with too many missing values,
optionally filters to top variable genes,
enforces a minimum fraction of non-missing samples per gene,
returns a WGCNA-ready datExpr matrix (samples x genes) and
companion sample info and gene-variance table.
gexp_wgcna_prepare( expr, metadata, gene_mode = c("top_variable", "all_common"), top_genes = 5000L, min_samples_frac = 0.5 )gexp_wgcna_prepare( expr, metadata, gene_mode = c("top_variable", "all_common"), top_genes = 5000L, min_samples_frac = 0.5 )
expr |
Matrix of expression values (genes x samples), typically batch-corrected. |
metadata |
Data.frame with sample metadata; should contain
either rownames matching |
gene_mode |
"all_common" or "top_variable" (default). |
top_genes |
If |
min_samples_frac |
Minimum fraction of samples with non-missing expression required for a gene to be kept (default 0.5). |
A list with elements:
datExpr |
numeric matrix (samples x genes) for WGCNA |
sample_info |
data.frame with sample metadata aligned to datExpr |
gene_variance_table |
data.frame with gene variance and rank |
expr <- matrix(rnorm(600), nrow = 60) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) metadata <- data.frame( SampleID = colnames(expr), Condition = rep(c("Normal", "Disease"), each = 5), Dataset = rep(c("D1", "D2"), each = 5), stringsAsFactors = FALSE ) prep <- gexp_wgcna_prepare(expr, metadata, gene_mode = "top_variable", top_genes = 50) dim(prep$datExpr)expr <- matrix(rnorm(600), nrow = 60) rownames(expr) <- paste0("Gene", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) metadata <- data.frame( SampleID = colnames(expr), Condition = rep(c("Normal", "Disease"), each = 5), Dataset = rep(c("D1", "D2"), each = 5), stringsAsFactors = FALSE ) prep <- gexp_wgcna_prepare(expr, metadata, gene_mode = "top_variable", top_genes = 50) dim(prep$datExpr)
Build reproducibility report text for export
gexpipe_analysis_report_text(params = list(), include_session = TRUE)gexpipe_analysis_report_text(params = list(), include_session = TRUE)
params |
Named list of scalar analysis parameters (character or numeric). |
include_session |
Include |
Character vector of report lines.
gexpipe_analysis_report_text(list(method = "limma"), include_session = FALSE)gexpipe_analysis_report_text(list(method = "limma"), include_session = FALSE)
Summarise Dataset x Condition confounding for batch/DE guidance
gexpipe_batch_confounding_summary(metadata)gexpipe_batch_confounding_summary(metadata)
metadata |
data.frame with Dataset and Condition columns. |
list with confounded (logical), table (matrix), message (character)
meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3) ) gexpipe_batch_confounding_summary(meta)meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3) ) gexpipe_batch_confounding_summary(meta)
Summarise how Platform should enter batch/DE models
gexpipe_batch_covariate_info(metadata)gexpipe_batch_covariate_info(metadata)
metadata |
sample metadata data.frame. |
list with mixed_platforms, platform_dataset_confounded, include_platform_covariate
meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 2), Platform = rep(c("Microarray", "RNAseq"), each = 2) ) gexpipe_batch_covariate_info(meta)meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 2), Platform = rep(c("Microarray", "RNAseq"), each = 2) ) gexpipe_batch_covariate_info(meta)
Build ComBat / removeBatchEffect model matrix (biology to preserve)
gexpipe_build_batch_mod(metadata)gexpipe_build_batch_mod(metadata)
metadata |
sample metadata data.frame. |
model matrix
meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_build_batch_mod(meta)meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_build_batch_mod(meta)
Build a model matrix for DE (limma / edgeR / voom)
gexpipe_build_de_design(metadata)gexpipe_build_de_design(metadata)
metadata |
data.frame with Dataset, Platform, Condition columns. |
list with design, coef_condition, formula_desc, info
meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_build_de_design(meta)meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_build_de_design(meta)
Keeps genes significant on both platforms. When
require_same_direction is TRUE (default), the logFC sign must match.
gexpipe_consensus_degs( rna_sig, micro_sig, require_same_direction = TRUE, combine = c("intersection", "union") )gexpipe_consensus_degs( rna_sig, micro_sig, require_same_direction = TRUE, combine = c("intersection", "union") )
rna_sig |
Significant-gene table from RNA-seq DE ( |
micro_sig |
Significant-gene table from microarray DE (same columns). |
require_same_direction |
logical; if TRUE, drop genes with opposite
fold-change signs (only affects genes significant on both platforms;
has no effect on a platform-exclusive gene under |
combine |
|
Downstream tables use mean logFC and the more conservative (larger)
adjusted p-value so WGCNA / common-gene steps can reuse sig_genes.
list with table, genes, n_rna, n_micro, n_overlap,
n_consensus, n_discordant, rna_only, micro_only.
rna <- data.frame( Gene = c("A", "B", "C"), logFC = c(1.2, -0.8, 1.1), adj.P.Val = c(0.01, 0.02, 0.03), Significance = c("Up-regulated", "Down-regulated", "Up-regulated"), stringsAsFactors = FALSE ) micro <- data.frame( Gene = c("A", "B", "D"), logFC = c(0.9, -1.0, 1.4), adj.P.Val = c(0.02, 0.01, 0.04), Significance = c("Up-regulated", "Down-regulated", "Up-regulated"), stringsAsFactors = FALSE ) out <- gexpipe_consensus_degs(rna, micro) out$genesrna <- data.frame( Gene = c("A", "B", "C"), logFC = c(1.2, -0.8, 1.1), adj.P.Val = c(0.01, 0.02, 0.03), Significance = c("Up-regulated", "Down-regulated", "Up-regulated"), stringsAsFactors = FALSE ) micro <- data.frame( Gene = c("A", "B", "D"), logFC = c(0.9, -1.0, 1.4), adj.P.Val = c(0.02, 0.01, 0.04), Significance = c("Up-regulated", "Down-regulated", "Up-regulated"), stringsAsFactors = FALSE ) out <- gexpipe_consensus_degs(rna, micro) out$genes
Summarise samples used in a DE run (transparency for mixed-platform runs)
gexpipe_de_sample_info(meta_used, total_meta = NULL, method = "limma")gexpipe_de_sample_info(meta_used, total_meta = NULL, method = "limma")
meta_used |
Metadata rows actually used in the DE fit. |
total_meta |
Full unified metadata before subsetting (optional). |
method |
DE method name. |
list with human-readable fields for the Shiny UI.
meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), Platform = "RNAseq", row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_de_sample_info(meta)meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), Platform = "RNAseq", row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_de_sample_info(meta)
DESeq2 design formula for count-based DE
gexpipe_deseq2_design(metadata)gexpipe_deseq2_design(metadata)
metadata |
sample metadata data.frame. |
list with formula (formula object) and formula_desc (character)
meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_deseq2_design(meta)meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 3), row.names = paste0("S", 1:6), stringsAsFactors = FALSE ) gexpipe_deseq2_design(meta)
Detect microarray + RNA-seq in the same analysis
gexpipe_has_mixed_platforms(metadata)gexpipe_has_mixed_platforms(metadata)
metadata |
data.frame with optional |
logical
meta <- data.frame(Platform = c("Microarray", "RNAseq")) gexpipe_has_mixed_platforms(meta)meta <- data.frame(Platform = c("Microarray", "RNAseq")) gexpipe_has_mixed_platforms(meta)
Removes lowly expressed genes using a design-aware filter so filtering is not tied to differential expression statistics (avoids FDR bias from variance-percentile pre-filtering).
gexpipe_independent_filter(expr, design = NULL, group = NULL)gexpipe_independent_filter(expr, design = NULL, group = NULL)
expr |
Numeric matrix (genes x samples) or integer counts. |
design |
Model matrix for the DE analysis. |
group |
Optional factor when design is NULL (single-factor designs). |
list with expr (filtered), keep (logical vector), n_before, n_after, note
expr <- matrix(stats::rpois(5000, lambda = 50), nrow = 500, ncol = 10) design <- stats::model.matrix(~ factor(rep(c("A", "B"), each = 5))) gexpipe_independent_filter(expr, design = design)expr <- matrix(stats::rpois(5000, lambda = 50), nrow = 500, ncol = 10) design <- stats::model.matrix(~ factor(rep(c("A", "B"), each = 5))) gexpipe_independent_filter(expr, design = design)
Polar PCA coordinates for batch/platform diagnostic plots
gexpipe_pca_polar_df(expr, metadata, color_by = "Dataset")gexpipe_pca_polar_df(expr, metadata, color_by = "Dataset")
expr |
genes x samples matrix. |
metadata |
sample metadata aligned to expr columns. |
color_by |
column name in metadata to colour points. |
data.frame with theta, r, and colour column.
expr <- matrix(rnorm(120), 20, 6, dimnames = list(paste0("G", 1:20), paste0("S", 1:6))) meta <- data.frame(Dataset = rep(c("A", "B"), each = 3), row.names = colnames(expr)) gexpipe_pca_polar_df(expr, meta)expr <- matrix(rnorm(120), 20, 6, dimnames = list(paste0("G", 1:20), paste0("S", 1:6))) meta <- data.frame(Dataset = rep(c("A", "B"), each = 3), row.names = colnames(expr)) gexpipe_pca_polar_df(expr, meta)
Returns TRUE when Platform is nested within Dataset (each GSE has one technology)
or when a tentative ~ Dataset + Platform (+ Condition) design is rank-deficient.
gexpipe_platform_dataset_confounded(metadata)gexpipe_platform_dataset_confounded(metadata)
metadata |
data.frame with |
logical
meta <- data.frame( Dataset = c("GSE1", "GSE1", "GSE2", "GSE2"), Platform = c("Microarray", "Microarray", "RNAseq", "RNAseq") ) gexpipe_platform_dataset_confounded(meta)meta <- data.frame( Dataset = c("GSE1", "GSE1", "GSE2", "GSE2"), Platform = c("Microarray", "Microarray", "RNAseq", "RNAseq") ) gexpipe_platform_dataset_confounded(meta)
Sample IDs for one platform in unified metadata
gexpipe_platform_sample_ids(metadata, platform = c("Microarray", "RNAseq"))gexpipe_platform_sample_ids(metadata, platform = c("Microarray", "RNAseq"))
metadata |
data.frame with a |
platform |
|
Character vector of sample IDs (may be empty).
meta <- data.frame( Platform = c("Microarray", "RNAseq"), row.names = c("S1", "S2") ) gexpipe_platform_sample_ids(meta, "RNAseq")meta <- data.frame( Platform = c("Microarray", "RNAseq"), row.names = c("S1", "S2") ) gexpipe_platform_sample_ids(meta, "RNAseq")
Aligns metadata to expression columns, runs PCA on samples, and estimates
the mean fraction of variance in the top PCs explained by Dataset, Platform,
and Condition. Residual is 1 - sum(factor fractions) (approximate).
gexpipe_pvca_df(expr, metadata, max_samples = 100L, max_pcs = 10L)gexpipe_pvca_df(expr, metadata, max_samples = 100L, max_pcs = 10L)
expr |
Numeric matrix, genes x samples. |
metadata |
Sample metadata with |
max_samples |
Subsample at most this many columns for speed (default 100). |
max_pcs |
Use at most this many principal components (default 10). |
A list with ok (logical), data (data.frame or NULL),
and message (character, empty on success).
expr <- matrix(rnorm(120), 20, 6, dimnames = list(paste0("G", 1:20), paste0("S", 1:6))) meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3), row.names = colnames(expr), stringsAsFactors = FALSE ) gexpipe_pvca_df(expr, meta)expr <- matrix(rnorm(120), 20, 6, dimnames = list(paste0("G", 1:20), paste0("S", 1:6))) meta <- data.frame( Dataset = rep(c("GSE1", "GSE2"), each = 3), Condition = rep(c("Normal", "Disease"), times = 3), row.names = colnames(expr), stringsAsFactors = FALSE ) gexpipe_pvca_df(expr, meta)
Uses per-platform normalized expression (not the cross-platform
batch-corrected matrix). Single-dataset subsets use a Condition contrast;
multiple datasets on the same platform use gexpipe_build_de_design().
gexpipe_run_limma_on_subset( expr, metadata, logfc_cutoff = 0.5, padj_cutoff = 0.05, ref_lab = "Normal", alt_lab = "Disease" )gexpipe_run_limma_on_subset( expr, metadata, logfc_cutoff = 0.5, padj_cutoff = 0.05, ref_lab = "Normal", alt_lab = "Disease" )
expr |
Numeric matrix (genes x samples). |
metadata |
Sample metadata with |
logfc_cutoff |
Numeric log2 fold-change cutoff. |
padj_cutoff |
Numeric adjusted P-value cutoff. |
ref_lab |
Reference condition label (default |
alt_lab |
Alternate condition label (default |
list with de_results, sig_genes, filter_note, sample_info,
formula_desc.
expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("G", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) gexpipe_run_limma_on_subset(expr, meta)expr <- matrix(rnorm(200), nrow = 20) rownames(expr) <- paste0("G", seq_len(nrow(expr))) colnames(expr) <- paste0("S", seq_len(ncol(expr))) meta <- data.frame( Condition = rep(c("Normal", "Disease"), each = 5), row.names = colnames(expr), stringsAsFactors = FALSE ) gexpipe_run_limma_on_subset(expr, meta)
Detects your R version and verifies (or optionally installs) required packages.
When GExPipe is installed from Bioconductor, dependencies are installed at
package install time; gexpipe_setup() only checks availability unless
options(gexpipe.auto_install = TRUE) is set.
gexpipe_setup( update = TRUE, optional = TRUE, launch = FALSE, port = 3838L, ... )gexpipe_setup( update = TRUE, optional = TRUE, launch = FALSE, port = 3838L, ... )
update |
Logical. If |
optional |
Logical. Reserved for future optional feature packages.
Currently all runtime dependencies are in |
launch |
Logical. If |
port |
Integer. Port for the Shiny app when |
... |
Additional arguments passed to |
Invisibly returns a named logical vector indicating which packages were successfully installed/available.
# Check and install missing packages only (no updates, no launch) if (interactive()) { gexpipe_setup(update = FALSE, optional = FALSE, launch = FALSE) }# Check and install missing packages only (no updates, no launch) if (interactive()) { gexpipe_setup(update = FALSE, optional = FALSE, launch = FALSE) }
For a two-group design, WGCNA may add a combined "X vs Y" contrast column that duplicates one group indicator. This helper removes that column before plotting.
gexpipe_wgcna_heatmap_cor(cor_mat, combined)gexpipe_wgcna_heatmap_cor(cor_mat, combined)
cor_mat |
Numeric matrix of module-trait correlations. |
combined |
Name of the combined contrast column to drop, or NULL. |
The correlation matrix with the combined column removed when appropriate.
cor_mat <- matrix(runif(6), 2, 3, dimnames = list(c("ME1", "ME2"), c("A", "B", "A vs B"))) gexpipe_wgcna_heatmap_cor(cor_mat, "A vs B")cor_mat <- matrix(runif(6), 2, 3, dimnames = list(c("ME1", "ME2"), c("A", "B", "A vs B"))) gexpipe_wgcna_heatmap_cor(cor_mat, "A vs B")
Launches the GExPipe Shiny app for multi-omics RNA analysis (bulk RNA-seq, microarray, GEO download, QC, normalization, differential expression, WGCNA, pathway enrichment, PPI, machine learning, and more).
runGExPipe( launch.browser = TRUE, port = getOption("shiny.port", 3838), host = getOption("shiny.host", "127.0.0.1") )runGExPipe( launch.browser = TRUE, port = getOption("shiny.port", 3838), host = getOption("shiny.host", "127.0.0.1") )
launch.browser |
If TRUE, open the app in the default browser (default). Set to FALSE in Google Colab or headless servers (no local browser). |
port |
TCP port for Shiny. Use a positive port (e.g. |
host |
The host to bind. Use "0.0.0.0" in Google Colab so port forwarding works. |
A Shiny app object. Users should run it with shiny::runApp(), which
starts the server and blocks the R session until the app is stopped
(e.g. RStudio Stop button).
When GExPipe is installed from Bioconductor (or any normal R library),
dependencies must be installed at package install time via
BiocManager::install("GExPipe", dependencies = TRUE). runGExPipe()
then verifies imports and opens the app without downloading packages.
For the GitHub / first-run workflow, set
options(gexpipe.auto_install = TRUE) before runGExPipe() to enable
background dependency installation (10-30 minutes on a fresh machine).
The welcome screen loads first; the full 15-tab dashboard is built only
after the user clicks Go to Analysis, so the initial browser response is
fast even on slow networks. Prefer GExPipe::runGExPipe() from an
installed package rather than opening inst/shinyapp/ in RStudio
directly (that path runs global.R, which duplicates the install logic).
Optional tuning before runGExPipe():
options(gexpipe.wgcna_threads = 1L) - fewer WGCNA threads (less parallel setup noise).
options(gexpipe.wgcna_threads = 0L) or FALSE - skip WGCNA::enableWGCNAThreads() at app start.
options(shiny.testmode = TRUE) with options(gexpipe.minimal_attach_in_testmode = TRUE) (default
when unset) attaches only the Shiny stack first, then the rest after the first flush, so
automated tests can connect quickly. Set gexpipe.minimal_attach_in_testmode = FALSE for a
full attach on the first tick (e.g. deep shinytest2 scenarios).
Install with full dependencies so STRINGdb support packages resolve:
BiocManager::install("GExPipe", dependencies = TRUE). STRINGdb is installed as a dependency.
The first PPI run may download large STRING files (requires network). If initialization fails,
try options(gexpipe.stringdb_try_versions = c("11.5", "11", "12")) or update STRINGdb.
# Non-interactive (R CMD check / example()): build an app object only. # Dependency auto-install is skipped when interactive() is FALSE, so this # runs quickly without touching the network. app <- runGExPipe(launch.browser = FALSE, port = 0L) stopifnot(inherits(app, "shiny.appobj")) # Interactive: two-step launch. runGExPipe() builds the app object # (installing any missing packages on first run); shiny::runApp() starts it. if (interactive()) { app <- GExPipe::runGExPipe() # Step 1: build the app object shiny::runApp(app) # Step 2: start the server }# Non-interactive (R CMD check / example()): build an app object only. # Dependency auto-install is skipped when interactive() is FALSE, so this # runs quickly without touching the network. app <- runGExPipe(launch.browser = FALSE, port = 0L) stopifnot(inherits(app, "shiny.appobj")) # Interactive: two-step launch. runGExPipe() builds the app object # (installing any missing packages on first run); shiny::runApp() starts it. if (interactive()) { app <- GExPipe::runGExPipe() # Step 1: build the app object shiny::runApp(app) # Step 2: start the server }