An R package for predicting patient response and resistance to cancer treatment using single-cell transcriptomics.
PERCEPTIONx implements the PERCEPTION approach (PERsonalized single-Cell Expression-based Planning for Treatments In ONcology), a computational framework that predicts how individual patients respond to drug treatments by leveraging both bulk and single-cell RNA sequencing data. It trains models on DepMap cell line data and applies them to patient single-cell expression profiles, enabling clone-level drug sensitivity prediction and patient-level response stratification, together with an interactive visualization suite and a Shiny web application.
Reference: Sinha, S., Vegesna, R., Mukherjee, S. et al. PERCEPTION predicts patient response and resistance to treatment using single-cell transcriptomics of their tumors. Nat Cancer 5, 938β952 (2024). https://doi.org/10.1038/s43018-024-00756-7
Install the development version from GitHub using devtools.
# install.packages("devtools")
devtools::install_github("WangLabCSU/PERCEPTIONx")PERCEPTIONx relies on DepMap reference data and optional pre-trained models. Both can be downloaded automatically with the built-in loading functions.
# From the package source tree (development mode):
devtools::load_all()
# Load pre-trained models
models <- load_model("abemaciclib")
# Load DepMap reference data
load_depmap(read = TRUE)Before training, identify the genes available across both bulk and single-cell expression datasets. The train_models() function then performs feature ranking, model building, and hyperparameter tuning in a single call.
# Identify available genes across expression and scRNA datasets
available_genes <- intersect(rownames(DepMap$expression_20Q4),
rownames(DepMap$scRNA_complete))
# Sample genes of interest
set.seed(123)
GOI_100 <- sample(available_genes, 100)
# Train a model for a single drug
models <- train_models(
drug_list = "abemaciclib",
cancer_type = "PanCan",
exclude_cancer = "PanCan",
GOI = GOI_100,
ncores = 1
)Important: Rank Normalization
PERCEPTIONx models are trained on rank-normalized expression data. If you provide your own expression data (e.g., from scRNA-seq), you must normalize it first using
rank_normalization_mat(), or predictions will be unreliable.How rank normalization works: For each cell (column), every gene's expression value is replaced by its rank within that column, divided by the total number of genes:
x_norm = rank(x) / n. This transforms each column into a uniform distribution over (0, 1], making the data robust to batch effects, library size differences, and outliers. Since the model coefficients capture the relationship between relative gene expression ranks and drug response (not absolute values), the same normalization must be applied to any new data.# If your data is NOT already rank-normalized: my_expr_norm <- rank_normalization_mat(my_raw_expr) # Then use my_expr_norm in predict_drugs()
Prediction proceeds in two stages: first, predict_drugs() scores each clone's drug sensitivity from the expression matrix; then, predict_patients() aggregates clone-level scores into a patient-level prediction using clone proportions.
# Clone-level prediction (returns matrix: clones x drugs)
clone_killing <- predict_drugs(
model_list = models,
expr = sc_expression_rnorm
)
# Build clone_killing_matrix with patient and clone_id columns
# (clone_ids from rownames, patients extracted via strsplit_customv0)
clone_killing_df <- data.frame(
patient = strsplit_customv0(rownames(clone_killing), "_", 1),
clone_id = rownames(clone_killing),
clone_killing,
check.names = FALSE
)
# Patient-level aggregation (legacy: prepared_data = clone_counts data.frame)
patient_pred <- predict_patients(
clone_pred = clone_killing_df,
prepared_data = clone_counts,
mode = "weighted_max"
)
# Recommended workflow instead: pass the prepare_data() result directly
# patient_pred <- predict_patients(clone_pred, prepared)PERCEPTIONx provides a suite of plotting functions to inspect model predictions from different perspectives: spatial (t-SNE), clonal (distribution and killing), and clinical (ROC and response stratification).
# t-SNE with drug response overlay
plot_tsne_response(
tsne_data = tsne_data,
color_var = "killing_scaled",
title = "Drug Response"
)
# Clone distribution stacked bar chart
plot_clone_distribution(
clone_distribution = clone_distribution,
response_var = "response"
)
# Clone killing lollipop plot
plot_clone_killing(
clone_killing = clone_killing,
killing_var = "comb_killing"
)
# ROC curve with AUC annotation
plot_roc_curve(
response = response,
predictor = predictor,
smooth_curve = TRUE
)
# Response boxplot (responders vs. non-responders)
plot_response_boxplot(
exp_vs_pred = exp_vs_pred,
response_var = "response"
)Interactive tooltips (optional): every plotting function above accepts
tooltip = TRUE(default). When theggiraphpackage is installed, points/bars get hover tooltips (clone id, killing score, proportion, FPR/TPR, ...). Settooltip = FALSEfor a plain staticggplotwith the identical layout. See the package vignette Β§6.9 for details.
| Function | Description |
|---|---|
load_depmap() |
Download and load DepMap reference datasets |
load_model() |
Download and load pre-trained models |
get_mirrors() |
Get available download mirrors |
add_mirrors() |
Add a custom mirror |
list_mirrors() |
List current mirrors |
reset_mirrors() |
Reset to default mirrors |
| Function | Description |
|---|---|
rank_normalization_mat() |
Rank-normalize an expression matrix |
range01() |
Scale a numeric vector to the 0-1 range |
| Function | Description |
|---|---|
train_models() |
Full training pipeline (main entry point) |
get_response_matrix() |
Extract drug response data from DepMap |
get_cellLine_list() |
Get training/test cell line split |
feature_ranking_bulk() |
Rank features by correlation with drug response |
run_parallel_feature_ranking_bulk() |
Parallel feature ranking for multiple drugs |
build_on_BULK_v2() |
Build a single-drug model (glmnet or random forest) |
| Function | Description |
|---|---|
predict_drugs() |
Predict drug sensitivity at clone/cell level |
predict_patients() |
Aggregate clone-level predictions to patient level |
| Function | Description |
|---|---|
compare_performance() |
Compare performance across model configurations |
get_significant_models() |
Filter models with significant stratification |
get_performance() |
Load pre-computed performance metrics |
each_patient_pseudo_bulk() |
Compute patient pseudo-bulk expression |
| Function | Description |
|---|---|
plot_tsne_response() |
t-SNE/UMAP with drug response overlay |
plot_tsne_biomarker_killing() |
Biomarker vs. killing side-by-side on t-SNE |
plot_clone_distribution() |
Clone abundance stacked bar chart |
plot_clone_killing() |
Clone-level killing lollipop plot |
plot_roc_curve() |
ROC curve with AUC |
plot_response_boxplot() |
Responder vs. non-responder boxplot |
plot_model_performance() |
Model performance across thresholds |
plot_seurat_clustering() |
Seurat clustering and UMAP visualization |
plot_patient_response_panel() |
Composite patient response panel |
| Function | Description |
|---|---|
err_handle() |
Error-safe evaluation (returns NA on error) |
stripall2match() |
Normalize strings for fuzzy matching |
strsplit_customv0() |
Split strings and extract elements |
hypergeometric_test_for_twolists() |
Hypergeometric enrichment test |
fdrcorr() |
FDR multiple testing correction |
DepMap Data βββΊ Preprocessing βββΊ Feature Ranking βββΊ Model Training
β β
rank_normalization_mat() train_models()
β
Patient scRNA βββΊ Preprocessing βββΊ Clone Prediction βββΊ Patient Aggregation
β β β
rank_normalization_mat() predict_drugs() predict_patients()
β
Visualization & Evaluation
β
plot_roc_curve() / plot_response_boxplot()
compare_performance() / get_significant_models()
- DepMap reference data: Automatically downloaded via
load_depmap(), including bulk expression, single-cell expression, drug response (AUC), and cell line annotations. - Patient data: Single-cell RNA expression matrix (genes as rows, cells as columns), rank-normalized via
rank_normalization_mat(). - Clone annotations: Mapping from cells to clones/patients, with clone proportions per patient.
The built-in Shiny demo ("Load Demo" in the Data tab) only generates a small synthetic dataset (49 genes x 400 cells x 20 patients) for smoke-testing the UI. For a meaningful large-scale test, use:
- DepMap reference data β click Load DepMap in the Data tab (or run
load_depmap()), which downloads the full reference set (~567 MB, 15k+ genes x 1,000+ cell lines). This is the standard training/reference input and the most demanding step for memory and disk. - Real patient scRNA-seq β upload a gene x cell expression matrix. Accepted
formats: CSV / TSV / TXT / Excel / RDS (a matrix, data.frame, or Seurat object).
Rank-normalize first β either call
rank_normalization_mat()on the matrix or upload raw counts and let the app normalize them duringprepare_data(). The closest public example is the PERCEPTION paper's own demo: PRJNA591860 (lung cancer EGFR-TKI cohort, 24 patients, Maynard et al. 2020; Zenodo doi:10.5281/zenodo.7860559), which ships a matching expression matrix (PRJNA591860.RDS), a cell-to-patient map, and clinical responses (Sample_data_response.xlsx). A widely used alternative is GSE176078 (breast cancer scRNA-seq, 44 patients, Wu et al. 2021). Any dataset with 10,000+ cells and multiple patients will exercise the Seurat clustering, prediction, and visualization steps at realistic scale. - Pre-trained models β
load_model("abemaciclib")(or any of the 44 FDA drugs) avoids the cost of training and lets you go straight to prediction.
Expected runtime: with DepMap + a 44-patient scRNA cohort, Seurat clustering and clone-level prediction take several minutes and several GB of RAM. The demo data can be used first to verify the whole pipeline works end-to-end.
PERCEPTION-shiny ships with an interactive web dashboard (built with Shiny) that wraps the whole pipeline β data loading, model training, prediction, and visualization β in a point-and-click interface.
# From the package source tree (development mode):
devtools::load_all()
run_perception_app() # starts the app in your browseror directly from the source tree:
shiny::runApp(system.file("shiny", "app", package = "PERCEPTIONx"))| Tab | What you can do |
|---|---|
| Data | Load the synthetic demo data (smoke-test), load the full DepMap reference (~567 MB), or upload your own rank-normalized single-cell matrix + clinical responses |
| Train | Train drug-response models (glmnet / random forest) with tunable parameters |
| Predict | Score clone-level and patient-level drug sensitivity for any loaded model |
| Visualize | Clone distribution, clone-killing lollipop, ROC curve, response boxplot, model performance, and UMAP/t-SNE overlays |
| Help | In-app documentation |
All figures are rendered as interactive SVG (via ggiraph): hover any point
or bar to see a tooltip (clone id, killing score, proportion, FPR/TPR, ...).
Because the original ggplot object is rendered directly β never converted to
plotly β facet layouts and legends stay exactly as designed: no re-flow, no
overlapping labels. Static downloads are publication-quality:
| Format | Resolution |
|---|---|
| PNG | 600 dpi, Cairo anti-aliased (6000 Γ 4200 px at default size) |
| PDF / SVG | Vector β infinitely zoomable, recommended for papers |
- Downloaded DepMap data and pre-trained models are written to the R session's
tempdir(), so caches are released automatically when the app closes β nothing persists on disk between sessions. - The app cache-busts its own stylesheet on every start, so you always see the newest UI without manually clearing the browser cache.
If you use this package, please cite the original PERCEPTION study:
Sinha, S., Vegesna, R., Mukherjee, S. et al. PERCEPTION predicts patient response and resistance to treatment using single-cell transcriptomics of their tumors. Nat Cancer 5, 938β952 (2024). https://doi.org/10.1038/s43018-024-00756-7
MIT Β© PERCEPTIONx authors