Basic annotation pipeline#
This example creates a small spatial dataset, performs marker-priority tree gating, selects high-confidence labels, and visualises the result. It is executed from start to finish whenever the documentation is built.
Create an AnnData object#
For demonstration purposes, half the cells resemble T cells and half resemble
B cells. Real data can be loaded from any source that produces an AnnData
object with cells in rows and markers in columns.
import anndata as ad
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import cytogater
rng = np.random.default_rng(42)
n_cells = 600
truth = np.repeat(["T cell", "B cell"], n_cells // 2)
expression = np.column_stack(
[
np.where(truth == "T cell", rng.normal(4, 0.7, n_cells), rng.normal(1, 0.5, n_cells)),
np.where(truth == "B cell", rng.normal(4, 0.7, n_cells), rng.normal(1, 0.5, n_cells)),
]
)
adata = ad.AnnData(expression)
adata.var_names = ["CD3e", "CD20"]
adata.layers["exprs"] = expression.copy()
adata.obs["truth"] = pd.Categorical(truth)
adata.obsm["spatial"] = rng.uniform(0, 100, size=(n_cells, 2))
adata
AnnData object with n_obs × n_vars = 600 × 2
obs: 'truth'
obsm: 'spatial'
layers: None (.X), 'exprs'
Define marker knowledge#
Positive markers support a cell-type assignment; negative markers penalise an assignment when they are expressed.
lineage_table = pd.DataFrame(
{
"cell_type": ["T cell", "B cell"],
"pos_markers": [["CD3e"], ["CD20"]],
"neg_markers": [["CD20"], ["CD3e"]],
}
)
lineage_table
| cell_type | pos_markers | neg_markers | |
|---|---|---|---|
| 0 | T cell | [CD3e] | [CD20] |
| 1 | B cell | [CD20] | [CD3e] |
Run tree gating#
run_tree_gating() fits marker distributions, constructs a marker-priority
tree for each cell type, and calculates a probability for every cell and cell
type.
result = cytogater.run_tree_gating(
adata,
lineage_table,
assay_name="exprs",
min_cells=50,
min_score=0.1,
workers=1,
)
result["prob_mat"].head()
| T cell | B cell | |
|---|---|---|
| 0 | 0.286886 | 0.033399 |
| 1 | 0.228794 | 0.046117 |
| 2 | 0.299059 | 0.029000 |
| 3 | 0.303233 | 0.027430 |
| 4 | 0.174275 | 0.044564 |
The hard labels and probabilities are available both in the returned result and on the annotated object.
adata.obs[["truth", "cell_type_hard", "prob_best"]].head()
| truth | cell_type_hard | prob_best | |
|---|---|---|---|
| 0 | T cell | T cell | 0.286886 |
| 1 | T cell | Uncertain | 0.228794 |
| 2 | T cell | T cell | 0.299059 |
| 3 | T cell | T cell | 0.303233 |
| 4 | T cell | Uncertain | 0.174275 |
Select confident reference cells#
Probability cutoffs can identify an initial reference set for supervised
prediction. Cells that do not uniquely exceed a cell-type cutoff remain
Unknown.
result = cytogater.apply_cutoff_labels(result)
adata.obs["cutoff_label"].value_counts()
cutoff_label
Unknown 576
T cell 12
B cell 12
Name: count, dtype: int64
Inspect the spatial labels#
The selected labels can now be used with
cytogater.train_custom_knn(),
cytogater.train_custom_randomforest(), or the hierarchical prediction
workflow documented in the API reference.