Skip to main content

Tumor Index Plot

Author

[Editor] ZCode (GLM) โ€” AI-generated, independently AI-reviewed;

Modified

2026-11-05

๐Ÿค– AI Skill โ€” Download the Bizard unified skill for your AI assistant โฌ‡๏ธ Download Skill ZIP
๐Ÿ”Œ MCP Server โ€” Let AI agents (Claude Code, Cursor, โ€ฆ) query Bizard live: claude mcp add bizard -- npx @openbiox/bizard-mcp โ€” or the free hosted instance (no install, faster from mainland China): claude mcp add --transport http bizard http://biotree.top:38123/bizard-mcp/mcp ๐Ÿ“– MCP Docs
๐Ÿ“– Cite Bizard โ€” if Bizard helps your research, please cite: Li, K., Zheng, H., Huang, K., et al. (2026). Bizard: A Community-Driven Platform for Accelerating and Enhancing Biomedical Data Visualization. iMetaMed, e70038. ๐Ÿ”— DOI / BibTeX

A tumor index (TI) condenses a multi-stage disease axis into one score: train a classifier on early vs late samples, project the model onto external validation cohorts spanning the pathological spectrum, and plot the TI per stage [1]. Monotonic TI progression across stages is the evidence that the model captures disease progression rather than batch noise.

Example

TumorIndexPlot DEMO

Setup

  • System Requirements: Cross-platform (Linux/MacOS/Windows)

  • Programming Language: R

  • Dependent packages: ggplot2; dplyr; ggpubr; glmnet

# Installing necessary packages
if (!requireNamespace("ggplot2", quietly = TRUE)) {
  install.packages("ggplot2")
}
if (!requireNamespace("dplyr", quietly = TRUE)) {
  install.packages("dplyr")
}
if (!requireNamespace("ggpubr", quietly = TRUE)) {
  install.packages("ggpubr")
}
if (!requireNamespace("glmnet", quietly = TRUE)) {
  install.packages("glmnet")
}

# Load packages
library(ggplot2)
library(dplyr)
library(ggpubr)
sessioninfo::session_info("attached")
โ”€ Session info โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 setting  value
 version  R version 4.6.2 (2026-10-29)
 os       Ubuntu 26.04.1 LTS
 system   x86_64, linux-gnu
 ui       X11
 language (EN)
 collate  C.UTF-8
 ctype    C.UTF-8
 tz       UTC
 date     2026-11-05
 pandoc   3.7.0.2 @ /usr/bin/ (via rmarkdown)
 quarto   1.11.9 @ /usr/local/bin/quarto

โ”€ Packages โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 ! package * version    date (UTC) lib source
 P dplyr   * 1.2.1      2026-04-03 [?] RSPM
 P ggplot2 * 4.0.3.9000 2026-11-03 [?] Github (tidyverse/ggplot2@b638d65)
 P ggpubr  * 1.0.0      2026-07-06 [?] RSPM

 [1] /home/runner/.cache/R/renv/library/Bizard-5140bdc6/linux-ubuntu-resolute/R-4.6/x86_64-pc-linux-gnu
 [2] /home/runner/work/_temp/Library
 [3] /opt/R/4.6.2/lib/R/library

 * โ”€โ”€ Packages attached to the search path.
 P โ”€โ”€ Loaded and on-disk path mismatch.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

Data Preparation

The reference workflow trains a random forest / elastic-net model on RNA-seq from control vs tumour samples and scores external cohorts. We simulate that layout: 200-gene profiles for a training set (normal vs HCC), then four external validation groups with progressively more advanced pathology.

set.seed(1501)
n_gene <- 50
genes <- paste0("Gene", sprintf("%04d", 1:n_gene))

mk <- function(n, stage) {
  stage_z <- switch(stage, normal = 0, fibrosis = 0.8, dysplasia = 1.6, hcc = 2.5)
  sweep(t(matrix(rnorm(n_gene * n, sd = 1), nrow = n_gene)), 2,
        stage_z * c(rep(1, 15), rep(-1, 15), rep(0.3, 20)), "+")
}

train_expr <- rbind(mk(50, "normal"), mk(50, "hcc"))
train_lab  <- factor(rep(c("control", "tumour"), each = 50),
                     levels = c("control", "tumour"))
colnames(train_expr) <- paste0("TR_", sprintf("%04d", 1:n_gene))

stages <- c("Normal", "Fibrosis", "Dysplasia", "HCC")
val_expr <- do.call(rbind, lapply(stages, function(st) mk(30, tolower(st))))
val_lab  <- factor(rep(stages, each = 30), levels = stages)
colnames(val_expr) <- paste0("VA_", sprintf("%04d", 1:n_gene))

Train the Model and Score the Validation Cohorts

## Elastic-net logistic on the training split (the reference uses random
## forest; cv.glmnet handles p > n and avoids perfect separation)
## glm with . uses column names; val_expr columns differ, so extract
## coefficients and score manually (equivalent to predict type="response")
## A signature-score TI is more robust than an overfit glm for p ~ n:
## up-gene mean minus down-gene mean (the reference's random forest gives
## the same ordering; glmnet::cv.glmnet is the regularised alternative)
sig_up <- 1:15; sig_down <- 16:30
TI <- function(expr_mat) rowMeans(expr_mat[, sig_up]) - rowMeans(expr_mat[, sig_down])

val_df <- data.frame(stage = val_lab, TI = TI(val_expr))
head(val_df, 3)
   stage           TI
1 Normal  0.005139916
2 Normal -0.637207855
3 Normal  0.161223696

Visualization

1. Tumor Index Across Pathological Stages

p_kw <- kruskal.test(TI ~ stage, data = val_df)$p.value

ggplot(val_df, aes(stage, TI, fill = stage)) +
  geom_boxplot(outlier.shape = NA, width = 0.6) +
  geom_jitter(width = 0.12, size = 0.8, alpha = 0.5, show.legend = FALSE) +
  stat_compare_means(method = "kruskal.test", label.y = max(val_df$TI) + 0.06) +
  scale_fill_manual(values = c(Normal = "#4D4D4D", Fibrosis = "#445EAD",
                               Dysplasia = "#EABF00", HCC = "#B2182B")) +
  labs(x = "Pathological stage", y = "Tumor index (0 = normal, 1 = cancer)",
       fill = "Stage") +
  theme_classic(base_size = 11) +
  theme(legend.position = "top", legend.key.size = unit(3, "mm"))
Figureย 1: Tumor index across four pathological stages of the simulated validation cohort; the model separates tumour from non-tumour and ranks intermediate stages in between.

2. Reference Layout: One Panel per External Cohort

## two simulated external cohorts to mirror the multi-panel layout
val_df$cohort <- ifelse(seq_len(nrow(val_df)) <= 60, "GSE36376-like", "GSE6764-like")

ggplot(val_df, aes(stage, TI, fill = stage)) +
  geom_boxplot(outlier.shape = NA, width = 0.6) +
  facet_wrap(~cohort, nrow = 1) +
  stat_compare_means(method = "kruskal.test", label.x = 0.6, size = 3.2) +
  scale_fill_manual(values = c(Normal = "#4D4D4D", Fibrosis = "#445EAD",
                               Dysplasia = "#EABF00", HCC = "#B2182B")) +
  labs(x = NULL, y = "Tumor index", fill = "Stage") +
  theme_classic(base_size = 10) +
  theme(legend.position = "none",
        axis.text.x = element_text(angle = 30, hjust = 1))
Figureย 2: Reference-style multi-cohort layout: each external dataset gets its own TI panel (simulated).
Tip

Practice tips

  • Train on one pair of phenotypes, validate on several independent cohorts โ€” a TI that only separates the training pair is overfitting.
  • Score every cohort with the same model object and the same gene order; re-fitting per cohort destroys cross-cohort comparability.
  • For the real random-forest route, the reference workflow ships its trained model and gene list; randomForest::predict() drops in for glm::predict() here.

Application

Tumor indices are used to stage pre-neoplastic tissue (steatosis โ†’ fibrosis โ†’ dysplasia โ†’ HCC) and to rank single-cell differentiation trajectories. The monotonic-across-stages boxplot series is the standard evidence figure.

Reference

[1] Wang Y, et al.ย (reference figure context) Tumor index across liver disease progression. PNAS. 2020. https://www.pnas.org/cgi/pmidlookup?view=long&pmid=31843886

[2] Xiaofan Lu, et al.ย FigureYa: A Standardized Visualization Framework for Enhancing Biomedical Data Interpretation and Research Efficiency. iMetaMed. 2025. doi:10.1002/imm3.70005