Integration score calculator for single-cell batch correction evaluation
Project description
scintegration
Integration Score Calculator for Single-Cell Batch Correction Evaluation
scintegration is a Python package that provides a comprehensive metric for evaluating batch correction methods in single-cell RNA-seq data. It calculates an integration score that balances biology preservation against batch effect removal, helping researchers choose the best batch correction approach for their data.
Key Features
- Single Metric: One integration score that captures the tradeoff between preserving biological signal and removing batch effects
- cz-benchmarks Integration: Works seamlessly with cz-benchmarks clustering and classification tasks
- Multiple Metrics: Combines ARI, NMI, Silhouette, and F1 scores for robust evaluation
- Interpretable: Scores range from -0.5 (poor) to +0.5 (excellent) with clear interpretation
- Class Imbalance Aware: Uses F1 score (macro-averaged) to handle datasets with rare cell types
Installation
Basic Installation
pip install scintegration
With Visualization Tools
pip install scintegration[visualization]
Development Installation
# Clone the repository
git clone https://github.com/SkylarPurks/scintegration.git
cd scintegration
# Install in editable mode with dev dependencies
pip install -e .[dev]
Quick Start
from scintegration import IntegrationScoreEvaluator
from czbenchmarks.tasks import ClusteringTask, MetadataLabelPredictionTask
# Step 1: Run czbenchmarks tasks (clustering + classification)
# For biological labels (e.g., cell types)
clustering_task_bio = ClusteringTask()
clustering_results_bio = clustering_task_bio.evaluate(
embeddings={'scvi': scvi_embeddings, 'scgpt': scgpt_embeddings},
labels=celltype_labels
)
classification_task_bio = MetadataLabelPredictionTask()
classification_results_bio = classification_task_bio.evaluate(
embeddings={'scvi': scvi_embeddings, 'scgpt': scgpt_embeddings},
labels=celltype_labels
)
# For batch labels (e.g., donors)
clustering_task_batch = ClusteringTask()
clustering_results_batch = clustering_task_batch.evaluate(
embeddings={'scvi': scvi_embeddings, 'scgpt': scgpt_embeddings},
labels=donor_labels
)
classification_task_batch = MetadataLabelPredictionTask()
classification_results_batch = classification_task_batch.evaluate(
embeddings={'scvi': scvi_embeddings, 'scgpt': scgpt_embeddings},
labels=donor_labels
)
# Step 2: Calculate integration scores
evaluator = IntegrationScoreEvaluator()
results = evaluator.evaluate(
clustering_results_biology=clustering_results_bio,
clustering_results_batch=clustering_results_batch,
classification_results_biology=classification_results_bio,
classification_results_batch=classification_results_batch
)
# Step 3: Analyze results
print(results.summary())
print(f"\nBest model: {results.best_model}")
print(f"Integration Score: {results.best_score.integration_score:.4f}")
print(f"Biology preservation: {results.best_score.B:.4f}")
print(f"Batch leakage: {results.best_score.L:.4f}")
# Convert to DataFrame for further analysis
df = results.to_dataframe()
print(df)
Understanding the Integration Score
Formula
The integration score is calculated as:
IS = (B - L) / (2 × (B + L))
Where:
- B (Biology): Average of ARI, NMI, Silhouette, and F1 for biological labels (e.g., cell types)
- L (Leakage): Average of ARI, NMI, Silhouette, and F1 for batch labels (e.g., donors)
Score Interpretation
| Score Range | Interpretation | Description |
|---|---|---|
| IS ≥ 0.2 | Excellent | Strong biology preservation, minimal batch effects |
| 0.1 ≤ IS < 0.2 | Very Good | Clear biology signal over batch effects |
| 0.05 ≤ IS < 0.1 | Good | Biology preservation exceeds batch leakage |
| 0.0 ≤ IS < 0.05 | Marginal | Biology and batch effects similar |
| -0.1 ≤ IS < 0.0 | Poor | Batch effects exceed biology signal |
| IS < -0.1 | Very Poor | Strong batch effects, weak biology |
Why This Metric?
- Balances Two Goals: Good batch correction should preserve biology (high B) while removing batch effects (low L)
- Single Number: Easy to compare methods and choose the best one
- Handles Imbalance: Uses F1 score instead of accuracy, treating rare cell types fairly
- Theory-Grounded: Based on established metrics (ARI, NMI, Silhouette, F1)
Advanced Usage
Custom Metric Weights
# Emphasize clustering over classification
evaluator = IntegrationScoreEvaluator(
weights={
'ari': 0.3,
'nmi': 0.3,
'silhouette': 0.3,
'f1': 0.1
}
)
Access Individual Components
results = evaluator.evaluate(...)
for model_name, score in results.scores.items():
print(f"\n{model_name}:")
print(f" Integration Score: {score.integration_score:.4f}")
print(f" Biology (B): {score.B:.4f}")
print(f" - ARI: {score.biology_components['ari']:.3f}")
print(f" - NMI: {score.biology_components['nmi']:.3f}")
print(f" - Silhouette: {score.biology_components['silhouette']:.3f}")
print(f" - F1: {score.biology_components['f1']:.3f}")
Ranked Models
# Get models sorted by performance
for rank, (model_name, score) in enumerate(results.get_ranked_models(), 1):
print(f"#{rank}: {model_name} (IS = {score.integration_score:.4f})")
Integrated vs Non-Integrated Baseline
from scintegration import IntegrationScoreEvaluator
evaluator = IntegrationScoreEvaluator()
comparison = evaluator.evaluate_embeddings_with_baseline(
embeddings_by_model={
"scvi": {
"with_integration": scvi_integrated,
"without_integration": scvi_non_integrated, # optional
},
"scgpt": {
"with_integration": scgpt_integrated,
# missing without_integration -> optional PCA fallback
},
},
obs=adata.obs,
biology_labels=celltype_labels,
batch_labels=donor_labels,
raw_features=adata.X, # used only when baseline is missing
use_pca_baseline_when_missing=True,
)
print(comparison.summary())
df = comparison.to_dataframe()
For each model, deltas are computed as:
delta_integration_score = IS_with_integration - IS_without_integrationdelta_B = B_with_integration - B_without_integrationdelta_L = L_with_integration - L_without_integration
Interpretation:
- Positive
delta_integration_scoremeans integration improved overall score. - Positive
delta_Bmeans better biology preservation. - Negative
delta_Lmeans lower batch leakage (better batch removal).
Example Results
From a real dataset with 8,045 cells, 25 cell types (extreme imbalance: 2003:1), and 3 donors:
================================================================================
INTEGRATION SCORE RESULTS
================================================================================
#1 SCVI:
Integration Score: +0.0702
Biology (B): 0.6970
Leakage (L): 0.5597
Biology metrics: ARI=0.732, NMI=0.785, Sil=0.717, F1=0.554
Batch metrics: ARI=0.605, NMI=0.657, Sil=0.564, F1=0.413
#2 SCGPT:
Integration Score: +0.0533
Biology (B): 0.6822
Leakage (L): 0.5764
Biology metrics: ARI=0.720, NMI=0.774, Sil=0.699, F1=0.536
Batch metrics: ARI=0.620, NMI=0.672, Sil=0.577, F1=0.437
================================================================================
BEST MODEL: SCVI (IS = +0.0702)
================================================================================
Interpretation: scVI achieves the best integration with ~70% biology preservation and ~56% batch leakage, resulting in a positive integration score indicating good batch correction.
API Reference
Main Classes
IntegrationScoreEvaluator: Main API for calculating scoresIntegrationScoreResults: Container for results with helper methodsModelScore: Individual model's score with components
Core Functions
calculate_integration_score(B, L): Core formulacompute_B_score(...): Biology preservation scorecompute_L_score(...): Batch leakage scorenormalize_silhouette(sil_score): Convert silhouette from [-1,1] to [0,1]
Utility Functions
interpret_integration_score(score): Get qualitative interpretationbatch_effect_percentage(L): Convert L to percentagebiology_preservation_percentage(B): Convert B to percentage
Citation
If you use this package in your research, please cite:
@software{scintegration2026,
author = {SkylarPurks},
title = {scintegration: Integration Score Calculator for Single-Cell Batch Correction},
year = {2026},
url = {https://github.com/SkylarPurks/scintegration}
}
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run tests (
pytest) - Format code (
black .) - Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built on top of czbenchmarks
- Inspired by integration metrics from the single-cell community
- Uses established metrics: ARI, NMI, Silhouette coefficient, F1 score
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scintegration-0.1.0.tar.gz.
File metadata
- Download URL: scintegration-0.1.0.tar.gz
- Upload date:
- Size: 43.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
893322937249e5d24600aeb1bfe36211ad98b35edd8946664e3d72565aa6ab2d
|
|
| MD5 |
96e7621c4ccc82857e0c4c97eb97c4db
|
|
| BLAKE2b-256 |
03fdd80d6a443cbd8fd26873d9e4926951f1a294687f86e1c52e43d8d6b30424
|
File details
Details for the file scintegration-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scintegration-0.1.0-py3-none-any.whl
- Upload date:
- Size: 35.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2f9b4d4ed933090f783246375dfba01c0307e0bff099809c32637bc30675431
|
|
| MD5 |
819a006bdb2d9667bcca7208df25a9ae
|
|
| BLAKE2b-256 |
6b4dcd50163411b36922ed4a2235d69789d0f8764c91176a95f0a1f461f22f69
|