Skip to main content

Automatic Workflow

The automatic workflow is recommended for most datasets. It uses TF-IDF marker gene detection combined with Bayesian posterior aggregation to estimate the contamination fraction ρ without requiring prior biological knowledge.

Overview

load_10x() → set_clusters() → auto_est_cont() → adjust_counts()

Minimal example

from SoupX import load_10x, set_clusters, auto_est_cont, adjust_counts

sc = load_10x('path/to/cellranger/outs/')
sc = set_clusters(sc, cluster_labels)
sc = auto_est_cont(sc)
corrected = adjust_counts(sc)

Step-by-step

1. Load data

from SoupX import load_10x

sc = load_10x(
data_dir = 'path/to/cellranger/outs/',
verbose = True,
)
print(sc)
# SoupChannel with 33538 genes and 10209 cells

load_10x automatically detects CellRanger v2 and v3 layouts and loads cluster/tSNE/UMAP projections from the analysis/ directory when present.

2. Add cluster labels

from SoupX import set_clusters

# From a pandas Series indexed by cell barcode:
sc = set_clusters(sc, cluster_series)

# From an array (order must match sc.cells):
sc = set_clusters(sc, cluster_array)

Clusters from Seurat, Scanpy, or any other tool work. The contamination estimate is more stable with more clusters.

3. Estimate contamination

from SoupX import auto_est_cont

sc = auto_est_cont(
sc,
# marker selection
tfidf_min = 1.0,
soup_quantile = 0.90,
max_markers = 100,
# Bayesian prior
prior_rho = 0.05,
prior_rho_std_dev = 0.10,
# search bounds
contamination_range = (0.01, 0.80),
# per-cell refinement
cell_rho_method = None, # None | 'empirical_bayes' | 'glm' | 'decontx'
verbose = True,
do_plot = True,
)

print(f"rho = {sc.meta_data['rho'].mean():.3f}")

The posterior density plot shows the prior (dashed) and posterior (solid) distributions over ρ, with the MAP estimate (red vertical line).

4. Remove contamination

from SoupX import adjust_counts

corrected = adjust_counts(
sc,
method = 'subtraction', # 'subtraction' | 'multinomial' | 'soupOnly'
round_to_int = False,
verbose = 1,
)
# Returns scipy.sparse.csc_matrix, same shape as sc.toc

Per-cell rho refinement

After estimating a global rho, you can refine to per-cell estimates.

Empirical Bayes - Gamma-Poisson conjugate model. Fast, requires soup marker genes.

sc = auto_est_cont(sc, cell_rho_method='empirical_bayes')

GLM - Poisson GLM with log(nUMI) covariate. Captures the nUMI → rho relationship.

sc = auto_est_cont(sc, cell_rho_method='glm')

DecontX EM - Full Dirichlet-Multinomial EM (no topics). More accurate but slower.

sc = auto_est_cont(sc, cell_rho_method='decontx')

Troubleshooting

SymptomLikely causeFix
"No plausible marker genes found"TF-IDF or soup filter too strictReduce tfidf_min or soup_quantile
MAP rho at boundaryContamination outside search rangeAdjust contamination_range
ValueError: Clustering must be setMissing clustersCall set_clusters() first
rho > 0.5 warningVery high contamination or estimation errorCheck soup profile; use force_accept=True if expected