Visualizing pathway-level data with wikiprofiler

Overview

wikiprofiler is designed around a pipe-friendly grammar for pathway graphics. The core idea is simple: start with a pathway plot, add a data-mapping layer, and then add optional visual refinements. The newer APIs extend that same design upstream and downstream: wp_map() prepares data before plotting, wp_comparefill() adds a comparison layer, and wp_render() scales the workflow to pathway batches.

This vignette walks through the current workflow in English, with the wp_* functions written in a pipe-oriented style whenever composition makes sense.

Setup

library(wikiprofiler)
library(clusterProfiler)
library(DOSE)
library(org.Hs.eg.db)
library(knitr)

A minimal input object

The example below uses DOSE::geneList together with clusterProfiler::enrichWP() so that the same objects can be reused across the basic plotting, comparison plotting, and batch rendering sections.

data(geneList, package = "DOSE")

de <- names(geneList)[1:100]
wp_res <- enrichWP(de, organism = "Homo sapiens")
wp_tbl <- as.data.frame(wp_res)

de_symbol <- bitr(
  de,
  fromType = "ENTREZID",
  toType = "SYMBOL",
  OrgDb = "org.Hs.eg.db"
)

value <- stats::setNames(geneList[de_symbol[, 1]], de_symbol[, 2])
pathway_id <- wp_tbl$ID[1]

kable(head(wp_tbl[, c("ID", "Description", "p.adjust")], 5), digits = 4)
ID Description p.adjust
WP2446 WP2446 Retinoblastoma gene in cancer 0.0000
WP2361 WP2361 Gastric cancer network 1 0.0000
WP179 WP179 Cell cycle 0.0011
WP5039 WP5039 SARS CoV 2 innate immunity evasion and cell specific immune response 0.0043
WP4240 WP4240 Regulation of sister chromatid separation at the metaphase anaphase transition 0.0043

Build a single pathway plot with pipes

The most direct workflow starts with wpplot(), adds a fill layer with wp_bgfill(), and then improves label readability with wp_shadowtext().

wpplot(pathway_id) |>
  wp_bgfill(
    value = value,
    low = "darkgreen",
    high = "firebrick",
    legend_x = 0.88,
    legend_y = 0.95
  ) |>
  wp_shadowtext(bg.r = 2, bg.col = "white")

If you want to save the final plot, keep the piped object and pass it to wpsave().

p_single <- wpplot(pathway_id) |>
  wp_bgfill(
    value = value,
    low = "darkgreen",
    high = "firebrick",
    legend_x = 0.88,
    legend_y = 0.95
  ) |>
  wp_shadowtext()

single_png <- file.path(tempdir(), "wikiprofiler-single-demo.png")
wpsave(p_single, single_png, width = 11, height = 7)
single_png
#> [1] "C:\\Users\\HUAWEI\\AppData\\Local\\Temp\\RtmpKqDcnS/wikiprofiler-single-demo.png"

Prepare plotting values with wp_map()

wp_bgfill() expects a named numeric vector keyed by gene symbol. In real analyses, that is often not the format you start with. wp_map() fills that gap by:

The next example intentionally duplicates part of the table so that the aggregation step is visible.

expr_tbl <- de_symbol[1:40, c("ENTREZID", "SYMBOL")]
expr_tbl$score <- unname(geneList[expr_tbl$ENTREZID])

expr_tbl_dup <- expr_tbl[1:10, ]
expr_tbl_dup$score <- expr_tbl_dup$score * 0.5
expr_tbl2 <- rbind(expr_tbl, expr_tbl_dup)

mapped_value <- wp_map(
  expr_tbl2,
  value_col = "score",
  id_col = "ENTREZID",
  mapping = de_symbol[, c("ENTREZID", "SYMBOL")],
  mapping_from = "ENTREZID",
  mapping_to = "SYMBOL",
  aggregator = "mean"
)

head(mapped_value, 10)
#> APOBEC3B     ASPM   BCL2A1    CCNB2    CDC20    CDC45    CDCA3    CDCA8 
#> 2.674310 2.877002 2.967223 3.125239 2.758393 3.385945 2.595700 3.108056 
#>    CENPE    CEP55 
#> 2.414821 2.784238

wp_map() also stores a mapping table as an attribute so that you can inspect how each symbol-level value was produced.

mapping_table <- attr(mapped_value, "mapping_table")
kable(head(mapping_table, 10), digits = 4)
input_id symbol value aggregated_value
4312 MMP1 4.5726 3.4295
8318 CDC45 4.5146 3.3859
10874 NMU 4.4182 3.3137
55143 CDCA8 4.1441 3.1081
55388 MCM10 3.8763 2.9072
991 CDC20 3.6779 2.7584
6280 S100A9 3.5020 2.6265
2305 FOXM1 3.2918 2.4689
9493 KIF23 3.2862 2.4647
1062 CENPE 3.2198 2.4148

Once the values are prepared, they drop directly into the same pipe-oriented plotting workflow.

wpplot(pathway_id) |>
  wp_bgfill(
    value = mapped_value,
    low = "navy",
    high = "goldenrod",
    legend_x = 0.88,
    legend_y = 0.95
  ) |>
  wp_shadowtext()

Compare two conditions with wp_comparefill()

When both conditions are already represented as named numeric vectors keyed by symbol, wp_comparefill() computes the comparison values and reuses the same plotting grammar.

Here the second condition is simulated from mapped_value only to illustrate the API. The point is the workflow shape, not the biology of this toy example.

control_value <- mapped_value
case_value <- mapped_value + rep(c(-0.6, 0.9), length.out = length(mapped_value))

p_compare <- wpplot(pathway_id) |>
  wp_comparefill(
    value = case_value,
    control = control_value,
    mode = "difference",
    low = "steelblue4",
    high = "darkorange2",
    legend_x = 0.88,
    legend_y = 0.95
  ) |>
  wp_shadowtext()

p_compare

The comparison table is stored on the returned wpplot object.

kable(head(p_compare$comparison, 10), digits = 4)
symbol case control comparison
APOBEC3B 2.0743 2.6743 -0.6
ASPM 3.7770 2.8770 0.9
BCL2A1 2.3672 2.9672 -0.6
CCNB2 4.0252 3.1252 0.9
CDC20 2.1584 2.7584 -0.6
CDC45 4.2859 3.3859 0.9
CDCA3 1.9957 2.5957 -0.6
CDCA8 4.0081 3.1081 0.9
CENPE 1.8148 2.4148 -0.6
CEP55 3.6842 2.7842 0.9

If you prefer ratio-based contrasts, switch the mode to log2_ratio.

wpplot(pathway_id) |>
  wp_comparefill(
    value = case_value,
    control = control_value,
    mode = "log2_ratio",
    pseudocount = 1
  ) |>
  wp_shadowtext()

Render multiple pathways with wp_render()

wp_render() is the batch entry point. It accepts pathway IDs directly, a data.frame, or an enrichment-like S4 object with a result slot. It can return a named list of wpplot objects, write files to disk, or do both.

The example below renders the top two enriched pathways and writes them with a filename template that combines rank, pathway ID, and pathway name.

batch_dir <- file.path(tempdir(), "wikiprofiler-batch-demo")
if (dir.exists(batch_dir)) {
  unlink(batch_dir, recursive = TRUE)
}

batch_plots <- wp_render(
  pathway = wp_tbl[, c("ID", "Description")],
  value = mapped_value,
  n = 2,
  name_col = "Description",
  dir = batch_dir,
  file_ext = "png",
  filename_template = "{index}_{id}_{name}",
  shadowtext = TRUE,
  width = 11,
  height = 7
)

batch_files <- list.files(batch_dir, full.names = TRUE)
batch_files
#> [1] "C:\\Users\\HUAWEI\\AppData\\Local\\Temp\\RtmpKqDcnS/wikiprofiler-batch-demo/1_WP2446_Retinoblastoma_gene_in_cancer.png"
#> [2] "C:\\Users\\HUAWEI\\AppData\\Local\\Temp\\RtmpKqDcnS/wikiprofiler-batch-demo/2_WP2361_Gastric_cancer_network_1.png"

The returned object is still useful even when you also export files.

names(batch_plots)
#> [1] "WP2446" "WP2361"

To work entirely in memory, omit dir.

plots <- wp_render(
  pathway = wp_res,
  value = mapped_value,
  n = 6,
  shadowtext = TRUE
)

A typical analysis pattern

In practice, the workflow often looks like this:

wp_res <- enrichWP(gene_ids, organism = "Homo sapiens")

mapped_value <- wp_map(
  expr_table,
  value_col = "logFC",
  id_col = "ENTREZID",
  mapping = id_map,
  mapping_from = "ENTREZID",
  mapping_to = "SYMBOL",
  aggregator = "mean"
)

plots <- wp_render(
  pathway = wp_res,
  value = mapped_value,
  n = 6,
  dir = "wp_batch",
  name_col = "Description",
  filename_template = "{index}_{id}_{name}",
  shadowtext = TRUE
)

For two-condition analyses, pass both value and control.

plots <- wp_render(
  pathway = wp_res,
  value = case_value,
  control = control_value,
  n = 6,
  dir = "wp_compare_batch",
  name_col = "Description",
  filename_template = "{index}_{id}_{name}",
  shadowtext = TRUE
)

Summary

The package now exposes a clearer layered workflow:

The important part is that the plotting grammar still reads from left to right. The newer APIs do not replace that design; they make the same modular approach easier to use in real analysis pipelines.