Epitope query

This notebook will demonstrate how to query epitopes in Dandelion against public API including the IEDB (Immune Epitope Database) and VDJdb.

[ ]:
import dandelion as ddl
import scanpy as sc
import warnings
import os

warnings.filterwarnings("ignore")

os.chdir("dandelion_tutorial")

First, we will load in the results from the TCR tutorial. If you have not run the TCR tutorial, please do so first.

[2]:
vdj = ddl.read_zipddl("dandelion_results_tcr.zipddl")  # from the tcr tutorial
vdj
[2]:
Lazy Dandelion object with n_obs = 6094 and n_contigs = 11300
    data: cell_id, sequence_id, sequence, sequence_aa, productive, rev_comp, v_call, v_cigar, d_call, d_cigar, j_call, j_cigar, c_call, c_cigar, sequence_alignment, germline_alignment, junction, junction_aa, junction_length, junction_aa_length, v_sequence_start, v_sequence_end, d_sequence_start, d_sequence_end, j_sequence_start, j_sequence_end, c_sequence_start, c_sequence_end, consensus_count, umi_count, is_cell, locus, rearrangement_status, extra, ambiguous, clone_id
    metadata: cell_id, clone_id, clone_id_rank, productive_VDJ, productive_VJ, v_call_VDJ, v_call_VJ, d_call_VDJ, j_call_VDJ, j_call_VJ, c_call_VDJ, c_call_VJ, junction_VDJ, junction_VJ, junction_aa_VDJ, junction_aa_VJ, locus_VDJ, locus_VJ, umi_count_VDJ, umi_count_VJ, productive_VDJ_main, productive_VJ_main, v_call_VDJ_main, v_call_VJ_main, d_call_VDJ_main, j_call_VDJ_main, j_call_VJ_main, c_call_VDJ_main, c_call_VJ_main, junction_VDJ_main, junction_VJ_main, junction_aa_VDJ_main, junction_aa_VJ_main, locus_VDJ_main, locus_VJ_main, umi_count_VDJ_main, umi_count_VJ_main, locus_status, chain_status, rearrangement_status_VDJ, rearrangement_status_VJ
    layout: layout for 6094 vertices, layout for 151 vertices
    graph: igraph graph of 6094 vertices, igraph graph of 151 vertices
    distances: distance matrix of shape (6094, 6094)
[3]:
adata = sc.read_h5ad("adata_tcr.h5ad")
adata
[3]:
AnnData object with n_obs × n_vars = 17275 × 2305
    obs: 'sample_id', 'n_genes', 'n_genes_by_counts', 'total_counts', 'total_counts_mt', 'pct_counts_mt', 'scrublet_score', 'is_doublet', 'filter_rna', 'has_contig', 'productive_VDJ', 'productive_VJ', 'v_call_VDJ', 'v_call_VJ', 'd_call_VDJ', 'j_call_VDJ', 'j_call_VJ', 'c_call_VDJ', 'c_call_VJ', 'junction_VDJ', 'junction_VJ', 'junction_aa_VDJ', 'junction_aa_VJ', 'locus_VDJ', 'locus_VJ', 'umi_count_VDJ', 'umi_count_VJ', 'productive_VDJ_main', 'productive_VJ_main', 'v_call_VDJ_main', 'v_call_VJ_main', 'd_call_VDJ_main', 'j_call_VDJ_main', 'j_call_VJ_main', 'c_call_VDJ_main', 'c_call_VJ_main', 'junction_VDJ_main', 'junction_VJ_main', 'junction_aa_VDJ_main', 'junction_aa_VJ_main', 'locus_VDJ_main', 'locus_VJ_main', 'umi_count_VDJ_main', 'umi_count_VJ_main', 'locus_status', 'chain_status', 'rearrangement_status_VDJ', 'rearrangement_status_VJ', 'leiden', 'clone_id', 'clone_id_rank'
    var: 'n_cells', 'highly_variable', 'means', 'dispersions', 'dispersions_norm', 'mean', 'std'
    uns: 'chain_status_colors', 'clone_id', 'dandelion', 'gex_neighbors', 'hvg', 'leiden', 'leiden_colors', 'locus_status_colors', 'log1p', 'neighbors', 'pca', 'rearrangement_status_VDJ_colors', 'rearrangement_status_VJ_colors', 'sample_id_colors', 'umap'
    obsm: 'X_pca', 'X_umap', 'X_vdj'
    varm: 'PCs'
    obsp: 'connectivities', 'distances'
[4]:
ddl.tl.clone_size(vdj)
ddl.tl.transfer(adata, vdj)

Next, we can query the epitopes in the Dandelion results against the IEDB and VDJdb databases. The results will be returned as a Pandas DataFrame, which can be further analyzed or visualized.

[5]:
ddl.tl.get_epitope(vdj, adata, receptor_type="TCR", database="vdjdb")
INFO: get_epitope: database=vdjdb, receptor_type=TCR, chain=None
INFO: Resolved VDJdb release URL via latest-version.txt: https://github.com/antigenomics/vdjdb-db/releases/download/2026-05-16/vdjdb-2026-05-16.zip
INFO: Fetching VDJdb from https://github.com/antigenomics/vdjdb-db/releases/download/2026-05-16/vdjdb-2026-05-16.zip …
INFO: Downloaded 34643377 bytes from https://github.com/antigenomics/vdjdb-db/releases/download/2026-05-16/vdjdb-2026-05-16.zip
INFO: Parsing VDJdb file: vdjdb-2026-05-16/vdjdb.txt
INFO: VDJdb: 25263 rows after score filter (>=1)
INFO: VDJdb: 25263 rows after receptor_type filter 'TCR'
INFO: VDJdb: returning 25263 records

The results of the epitope query is mapped to vdj.metadata as well as adata.obs. So then you can plot and interact with the results in the same way as you would with any other metadata in Dandelion or .obs in Scanpy. For example, you can plot the epitope query results on a UMAP or tSNE plot, or you can filter the results based on specific epitopes or other metadata.

[6]:
ddl.pl.clone_network(adata, color="organism_vdjdb_primary", size=20, edges=1)
... storing 'productive_VDJ' as categorical
... storing 'productive_VJ' as categorical
... storing 'v_call_VDJ' as categorical
... storing 'v_call_VJ' as categorical
... storing 'd_call_VDJ' as categorical
... storing 'j_call_VDJ' as categorical
... storing 'j_call_VJ' as categorical
... storing 'c_call_VDJ' as categorical
... storing 'c_call_VJ' as categorical
... storing 'junction_VDJ' as categorical
... storing 'junction_VJ' as categorical
... storing 'junction_aa_VDJ' as categorical
... storing 'junction_aa_VJ' as categorical
... storing 'locus_VDJ' as categorical
... storing 'locus_VJ' as categorical
... storing 'productive_VDJ_main' as categorical
... storing 'productive_VJ_main' as categorical
... storing 'v_call_VDJ_main' as categorical
... storing 'v_call_VJ_main' as categorical
... storing 'd_call_VDJ_main' as categorical
... storing 'j_call_VDJ_main' as categorical
... storing 'j_call_VJ_main' as categorical
... storing 'c_call_VDJ_main' as categorical
... storing 'c_call_VJ_main' as categorical
... storing 'junction_VDJ_main' as categorical
... storing 'junction_VJ_main' as categorical
... storing 'junction_aa_VDJ_main' as categorical
... storing 'junction_aa_VJ_main' as categorical
... storing 'locus_VDJ_main' as categorical
... storing 'locus_VJ_main' as categorical
... storing 'locus_status' as categorical
... storing 'chain_status' as categorical
... storing 'rearrangement_status_VDJ' as categorical
... storing 'rearrangement_status_VJ' as categorical
... storing 'clone_id' as categorical
... storing 'clone_id_size_category' as categorical
... storing 'epitope_vdjdb' as categorical
... storing 'epitope_vdjdb_primary' as categorical
... storing 'epitope_vdjdb_VDJ' as categorical
... storing 'epitope_vdjdb_VJ' as categorical
... storing 'organism_vdjdb' as categorical
... storing 'organism_vdjdb_primary' as categorical
... storing 'organism_vdjdb_VDJ' as categorical
... storing 'organism_vdjdb_VJ' as categorical
../../_images/notebooks_polars_9-epitope_query_9_1.png
[ ]:
adata[adata.obs["organism_vdjdb_primary"] == "InfluenzaA"].obs[
    [
        "clone_id",
        "clone_id_size",
        "junction_aa_VDJ",
        "junction_aa_VJ",
        "epitope_vdjdb_primary",
        "organism_vdjdb_primary",
    ]
].sort_values("clone_id_size", ascending=False)
clone_id clone_id_size junction_aa_VDJ junction_aa_VJ epitope_vdjdb_primary organism_vdjdb_primary
sc5p_v2_hs_PBMC_10k_t_GCTTGAACAGGATCGA-1 abT_VDJ_323_1_3_VJ_34_1_3 3.0 CASSLGETQYF CAVRPYGQNFVF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_GTCGGGTGTCCAGTAT-1 abT_VDJ_323_1_3_VJ_34_1_3 3.0 CASSLGETQYF CAVRPYGQNFVF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_CTCAGAACACCACCAG-1 abT_VDJ_323_1_3_VJ_34_1_3 3.0 CASSLGETQYF CAVRPYGQNFVF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_GGATTACCATGACATC-1 abT_VDJ_276_2_6_VJ_564_2_1 1.0 CASSLTGGTEAFF CAVQAYSGGGADGLTF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_GTTCGGGAGGCCATAG-1 abT_VDJ_406_3_4_VJ_41_2_23 1.0 CASSDGTGGTDTQYF CAVRDSNYQLIW GILGFVFTL InfluenzaA
... ... ... ... ... ... ...
sc5p_v2_hs_PBMC_10k_t_CTCGTCATCGTTGCCT-1 abT_VDJ_384_2_3_VJ_634_1_1 1.0 CASSERHATDTQYF CAVPPDSWGKLQF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_CTGAAACCAGACGCAA-1 abT_VDJ_493_5_1_VJ_413_1_1 1.0 CASSAEGDGSNEKLFF CALNDYKLSF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_CTGCCTAGTCGTCTTC-1 abT_VDJ_150_4_4_VJ_925_3_3 1.0 CASSPGLGQSYEQYF CAATPSNTGKLIF GILGFVFTL InfluenzaA
sc5p_v2_hs_PBMC_10k_t_CTGCTGTCACTATCTT-1 abT_VDJ_323_1_1_VJ_34_1_2 1.0 CASSLGETQYF CALRGYGQNFVF GILGFVFTL InfluenzaA
sc5p_v1p1_hs_melanoma_10k_t_TACCTATCAACTGGCC-1 abT_VDJ_128_4_1_VJ_590_4_1 1.0 CATSRGLGGPYNEQFF CAVRDNYGQNFVF GILGFVFTL InfluenzaA

70 rows × 6 columns

[ ]: