Modules

LymphoSeq: Python toolkit for analyzing high-throughput T and B cell receptor sequencing data.

This package provides tools for importing, manipulating, and visualizing Adaptive Immune Receptor Repertoire Sequencing (AIRR-seq) data.

lymphoseq.clonal_relatedness(data: DataFrame | DataFrame, repertoire_ids: List[str] | None = None, by_column: str = 'junction_aa', method: str = 'morisita') DataFrame | DataFrame[source]

Calculate pairwise similarity between repertoires.

Computes similarity metrics between all pairs of repertoires to measure clonal relatedness. Uses Morisita-Horn index by default, which accounts for both presence/absence and relative abundance of sequences.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • repertoire_ids – List of repertoire IDs to compare. If None, uses all repertoires (default: None)

  • by_column – Column containing sequences to compare (default: “junction_aa”)

  • method – Similarity metric to use: - “morisita”: Morisita-Horn index (default, recommended) - “jaccard”: Jaccard index (presence/absence only) - “cosine”: Cosine similarity

Returns:

  • repertoire1, repertoire2: Pair of repertoire IDs

  • similarity: Similarity score (0-1, where 1 = identical)

  • n_shared: Number of shared sequences

  • n_total: Total unique sequences across both repertoires

Return type:

DataFrame with pairwise similarity scores. Contains columns

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Calculate Morisita-Horn index for all pairs
>>> similarity = ls.clonal_relatedness(data)
>>> print(similarity)
>>> # Compare specific samples
>>> similarity = ls.clonal_relatedness(
...     data,
...     repertoire_ids=["S1", "S2", "S3"]
... )
>>> # Use Jaccard index instead
>>> jaccard = ls.clonal_relatedness(data, method="jaccard")

Notes

  • Morisita-Horn index: Recommended for abundance-weighted similarity

  • Jaccard index: Simple overlap coefficient (0-1)

  • Cosine similarity: Angle between frequency vectors

  • All metrics return values between 0 (no similarity) and 1 (identical)

References

Morisita M. (1959) Measuring of the dispersion and analysis of distribution patterns. Memoires of the Faculty of Science, Kyushu University, Series E. Biology, 2, 215-235.

See also

  • common_seqs(): Find shared sequences

  • differential_abundance(): Test for differential abundance

lymphoseq.clonality(data: DataFrame | DataFrame, rarefy: bool = False, min_count: int | None = None, iterations: int = 100, group_by: str = 'repertoire_id') DataFrame | DataFrame[source]

Calculate clonality and diversity metrics for each repertoire.

This function calculates standard repertoire diversity metrics including clonality, Gini coefficient, convergence, and unique productive sequences.

Clonality is calculated as 1 - (normalized Shannon entropy), where Shannon entropy is normalized by dividing by log2(n_unique_sequences). This ensures clonality is in the range [0, 1], where: - 0 indicates maximum diversity (all clones equally abundant) - 1 indicates minimum diversity (single dominant clone)

Parameters:
  • data – Input data frame with AIRR-formatted sequences

  • rarefy – Whether to perform rarefaction analysis

  • min_count – Minimum sequence count for rarefaction

  • iterations – Number of rarefaction iterations

  • group_by – Column to group by (default: repertoire_id)

Returns:

  • clonality: 1 - normalized Shannon entropy (0-1)

  • gini_coefficient: Measure of inequality (0-1)

  • unique_productive_sequences: Number of unique sequences

  • total_count: Total sequence reads

  • top_productive_sequence: Frequency of most abundant clone

  • convergence: Ratio of total sequences to unique sequences

Return type:

Data frame with diversity metrics for each repertoire

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/")
>>> diversity = ls.clonality(data)
>>> print(diversity)
lymphoseq.clone_track(data: DataFrame | DataFrame, by_column: str = 'junction_aa', include_frequency: bool = True) DataFrame | DataFrame[source]

Track clones across multiple repertoires/time points.

Creates a matrix showing the presence and frequency of each unique sequence across all repertoires. This is essential for longitudinal analysis and tracking clonal dynamics over time or across conditions.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain ‘repertoire_id’ and the column specified in by_column.

  • by_column – Column containing sequences to track (default: “junction_aa”)

  • include_frequency – If True, returns frequencies; if False, returns presence/absence (1/0) (default: True)

Returns:

DataFrame in wide format with sequences as rows and repertoires as columns. - First column: sequence (junction_aa or junction) - Remaining columns: one per repertoire - Values: frequencies (if include_frequency=True) or binary presence (0/1)

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Track clones with frequencies
>>> tracking = ls.clone_track(data)
>>> print(tracking.head())
>>> # Track presence/absence only
>>> presence = ls.clone_track(data, include_frequency=False)
>>> # Filter for sequences seen in multiple samples
>>> # Count non-zero values per row
>>> if isinstance(tracking, pl.DataFrame):
...     tracking = tracking.with_columns(
...         pl.sum_horizontal(pl.all().exclude(by_column) > 0).alias("n_samples")
...     )
...     shared = tracking.filter(pl.col("n_samples") > 1)

Notes

  • Missing sequences in a repertoire are represented as 0

  • This function is memory-intensive for large datasets

  • Output is in wide format (sequences × repertoires matrix)

See also

  • common_seqs(): Find sequences present in multiple repertoires

  • clonal_relatedness(): Calculate repertoire similarity

lymphoseq.common_seqs(data: DataFrame | DataFrame, repertoire_ids: List[str] | None = None, by_column: str = 'junction_aa', min_repertoires: int = 2) DataFrame | DataFrame[source]

Find sequences shared across multiple repertoires.

Identifies sequences that appear in two or more repertoires and returns their frequencies across all repertoires where they appear.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain ‘repertoire_id’ column and the column specified in by_column.

  • repertoire_ids – List of repertoire IDs to compare. If None, uses all repertoires in the data (default: None)

  • by_column – Column name containing sequences to compare (default: “junction_aa”)

  • min_repertoires – Minimum number of repertoires a sequence must appear in to be included (default: 2)

Returns:

DataFrame with common sequences and their frequencies across repertoires. Contains columns: sequence column, repertoire_id, duplicate_count, duplicate_frequency, and n_repertoires (count of repertoires sharing this sequence).

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Find sequences common to 2+ samples
>>> common = ls.common_seqs(data)
>>> print(f"Found {len(common)} common sequence occurrences")
>>> # Find sequences in specific repertoires
>>> common = ls.common_seqs(
...     data,
...     repertoire_ids=["S1", "S2", "S3"]
... )
>>> # Find highly shared sequences (3+ repertoires)
>>> highly_shared = ls.common_seqs(data, min_repertoires=3)

See also

  • clonal_relatedness(): Calculate similarity between repertoires

  • differential_abundance(): Test for differential sequence abundance

lymphoseq.common_seqs_venn(data: DataFrame | DataFrame, repertoire_ids: List[str], by_column: str = 'junction_aa', title: str | None = None, width: int = 700, height: int = 700) Figure[source]

Create a Venn diagram showing shared sequences between 2-3 repertoires.

Visualizes the overlap of unique sequences between repertoires, showing which sequences are shared and which are unique to each repertoire.

Parameters:
  • data – DataFrame with sequence data

  • repertoire_ids – List of 2-3 repertoire IDs to compare

  • by_column – Column to use for comparison (default: junction_aa)

  • title – Plot title (auto-generated if None)

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly Figure with Venn diagram

Raises:

ValueError – If number of repertoire_ids is not 2 or 3

Examples

>>> # Compare 2 repertoires
>>> fig = common_seqs_venn(df, ["Sample1", "Sample2"])
>>> fig.show()
>>> # Compare 3 repertoires
>>> fig = common_seqs_venn(df, ["S1", "S2", "S3"], by_column="junction")
lymphoseq.common_sequences(data: DataFrame | DataFrame, group_by: str = 'repertoire_id', min_samples: int = 2) DataFrame | DataFrame[source]

Find sequences that are common across multiple repertoires.

Parameters:
  • data – Input data frame with AIRR-formatted sequences

  • group_by – Column to group by

  • min_samples – Minimum number of samples a sequence must appear in

Returns:

Data frame with common sequences

lymphoseq.differential_abundance(data: DataFrame | DataFrame, group1: List[str], group2: List[str], by_column: str = 'junction_aa', min_count: int = 5, method: str = 'fisher') DataFrame | DataFrame[source]

Test for differential abundance of sequences between two groups.

Performs statistical testing (Fisher’s exact test or Chi-square) to identify sequences with significantly different abundances between two groups of repertoires.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • group1 – List of repertoire IDs in first group

  • group2 – List of repertoire IDs in second group

  • by_column – Column containing sequences to compare (default: “junction_aa”)

  • min_count – Minimum total count across both groups for testing (default: 5)

  • method – Statistical test method: “fisher” or “chi-square” (default: “fisher”)

Returns:

  • sequence column (junction_aa or junction)

  • count_group1, count_group2: Total counts in each group

  • freq_group1, freq_group2: Frequencies in each group

  • p_value: Statistical significance

  • log2_fold_change: log2(freq_group2 / freq_group1)

  • significant: Boolean indicating p < 0.05

Return type:

DataFrame with sequences and test statistics including

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Compare two groups of samples
>>> diff = ls.differential_abundance(
...     data,
...     group1=["Patient1_Pre", "Patient2_Pre"],
...     group2=["Patient1_Post", "Patient2_Post"]
... )
>>> # Filter for significant sequences
>>> sig = diff.filter(pl.col("significant")) if isinstance(diff, pl.DataFrame) else diff[diff["significant"]]
>>> print(f"Found {len(sig)} differentially abundant sequences")

Notes

  • Fisher’s exact test recommended for small counts

  • Chi-square test faster for large datasets

  • P-values are not corrected for multiple testing

See also

  • common_seqs(): Find shared sequences

  • clonal_relatedness(): Measure repertoire similarity

lymphoseq.diversity_metrics(data: DataFrame | DataFrame, group_by: str = 'repertoire_id') DataFrame | DataFrame[source]

Calculate comprehensive diversity metrics for repertoires.

Parameters:
  • data – Input data frame with AIRR-formatted sequences

  • group_by – Column to group by

Returns:

Data frame with diversity metrics

lymphoseq.gene_freq(data: DataFrame | DataFrame, gene: Literal['v', 'd', 'j'] = 'v', repertoire_ids: List[str] | None = None, top_n: int | None = None, normalize: bool = True) DataFrame | DataFrame[source]

Calculate V, D, or J gene usage frequencies.

Computes the frequency of each V, D, or J gene across repertoires. Results can be used for repertoire characterization and comparison of gene usage patterns.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain v_call, d_call, or j_call columns.

  • gene – Gene segment to analyze: “v”, “d”, or “j” (default: “v”)

  • repertoire_ids – List of repertoire IDs to analyze. If None, uses all repertoires (default: None)

  • top_n – Return only the top N most frequent genes. If None, returns all genes (default: None)

  • normalize – If True, returns frequencies (0-1); if False, returns raw counts (default: True)

Returns:

  • gene_name: Gene identifier (e.g., TRBV1-1)

  • repertoire_id: Repertoire identifier

  • count: Number of sequences using this gene

  • frequency: Proportion of sequences (if normalize=True)

Return type:

DataFrame with gene usage statistics. Contains columns

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Calculate V gene usage
>>> v_usage = ls.gene_freq(data, gene="v")
>>> print(v_usage.head())
>>> # Get top 10 J genes
>>> j_top10 = ls.gene_freq(data, gene="j", top_n=10)
>>> # Compare D gene usage in specific samples
>>> d_usage = ls.gene_freq(
...     data,
...     gene="d",
...     repertoire_ids=["S1", "S2", "S3"]
... )
>>> # Get raw counts instead of frequencies
>>> v_counts = ls.gene_freq(data, gene="v", normalize=False)

Notes

  • Gene calls may contain allele information (e.g., TRBV1-1*01)

  • Alleles are typically grouped by removing the *01 suffix

  • Missing or unassigned genes are excluded from calculations

See also

  • top_seqs(): Get most abundant sequences

  • clonal_relatedness(): Compare repertoire similarity

lymphoseq.gene_pair_freq(data: DataFrame | DataFrame, gene_pair: Literal['vj', 'vd', 'dj'] = 'vj', repertoire_ids: List[str] | None = None, top_n: int | None = None, normalize: bool = True) DataFrame | DataFrame[source]

Calculate V-J, V-D, or D-J gene pair usage frequencies.

Analyzes the co-occurrence of gene pairs to understand recombination patterns and biases in repertoires.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • gene_pair – Gene pair to analyze: “vj”, “vd”, or “dj” (default: “vj”)

  • repertoire_ids – List of repertoire IDs to analyze. If None, uses all repertoires (default: None)

  • top_n – Return only the top N most frequent pairs. If None, returns all pairs (default: None)

  • normalize – If True, returns frequencies; if False, returns counts (default: True)

Returns:

  • gene1: First gene in pair (e.g., V gene)

  • gene2: Second gene in pair (e.g., J gene)

  • repertoire_id: Repertoire identifier

  • count: Number of sequences with this pair

  • frequency: Proportion of sequences (if normalize=True)

Return type:

DataFrame with gene pair usage statistics. Contains columns

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Calculate V-J pairing
>>> vj_pairs = ls.gene_pair_freq(data, gene_pair="vj")
>>> print(vj_pairs.head())
>>> # Get top 20 V-J pairs
>>> vj_top20 = ls.gene_pair_freq(data, gene_pair="vj", top_n=20)

See also

  • gene_freq(): Single gene usage analysis

lymphoseq.get_vdjdb_stats(cache_dir: str | None = None) dict[source]

Get statistics about the VDJdb database.

Returns:

Dictionary with database statistics

Examples

>>> stats = get_vdjdb_stats()
>>> print(f"VDJdb contains {stats['total_entries']} entries")
lymphoseq.mergeSeqs(data: DataFrame | DataFrame, merge_column: str = 'repertoire_id', new_name: str = 'merged', repertoire_ids: List[str] | None = None) DataFrame | DataFrame[source]

Merge multiple repertoires into a single combined repertoire.

Combines sequences from multiple repertoires, aggregating counts and recalculating frequencies. Useful for creating grouped/pooled samples or combining technical replicates.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • merge_column – Column to use for grouping (default: “repertoire_id”)

  • new_name – Name for the merged repertoire (default: “merged”)

  • repertoire_ids – List of repertoire IDs to merge. If None, merges all repertoires (default: None)

Returns:

DataFrame with merged repertoire. Sequences are aggregated by junction_aa, counts are summed, and frequencies are recalculated.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Merge all repertoires
>>> merged_all = ls.mergeSeqs(data, new_name="combined")
>>> # Merge specific samples (e.g., technical replicates)
>>> merged = ls.mergeSeqs(
...     data,
...     repertoire_ids=["Sample1_rep1", "Sample1_rep2"],
...     new_name="Sample1_merged"
... )
>>> # Merge by treatment group
>>> treatment_group = data[data["treatment"] == "drug_A"]
>>> merged_treatment = ls.mergeSeqs(
...     treatment_group,
...     new_name="drug_A_pooled"
... )

Notes

  • Duplicate counts are summed across merged repertoires

  • Frequencies are recalculated after merging

  • Metadata from first occurrence is kept

See also

  • productive_seq(): Aggregate sequences within repertoires

lymphoseq.merge_chains(data: DataFrame | DataFrame, repertoire_id: str | None = None, separator: str = ':', keep_chain_columns: bool = False, aggregate: bool = True) DataFrame | DataFrame[source]

Merge alpha and beta chains from 10X data into combined sequences.

This function takes 10X single-cell data with separate TRA and TRB chain information and creates merged junction and junction_aa sequences that can be used with bulk repertoire analysis functions.

The function looks for columns like tra_junction_aa, trb_junction_aa, tra_junction, trb_junction and combines them into junction_aa and junction columns that are compatible with the rest of the package functions.

Parameters:
  • data – DataFrame containing 10X data with separate TRA and TRB columns. Expected columns include: tra_junction_aa, trb_junction_aa, tra_junction, trb_junction, and optionally tra_v_call, trb_v_call, etc.

  • repertoire_id – Optional repertoire ID to filter. If None, processes all repertoires.

  • separator – String to use for joining alpha and beta sequences (default: “:”)

  • keep_chain_columns – If True, keep the original tra_* and trb_* columns

  • aggregate – If True, aggregate identical merged sequences and count them

Returns:

DataFrame with merged junction and junction_aa columns, compatible with bulk analysis functions like clonality(), diversity_metrics(), etc.

Examples

>>> # Read 10X data
>>> data_10x = read_10x("path/to/10x_data/")
>>>
>>> # Merge chains for bulk-style analysis
>>> merged = merge_chains(data_10x)
>>>
>>> # Now use with any bulk analysis function
>>> clonality_scores = clonality(merged)
>>> diversity = diversity_metrics(merged)
>>>
>>> # Keep original chain columns
>>> merged_full = merge_chains(data_10x, keep_chain_columns=True)
>>>
>>> # Custom separator
>>> merged_custom = merge_chains(data_10x, separator=";")
lymphoseq.plot_clonality(data: DataFrame | DataFrame, x_col: str = 'repertoire_id', y_col: str = 'clonality', color_col: str | None = None, title: str = 'Clonality Analysis', width: int = 800, height: int = 600) Figure[source]

Create an interactive clonality plot.

Parameters:
  • data – Data frame with clonality results

  • x_col – Column for x-axis (usually repertoire_id)

  • y_col – Column for y-axis (clonality values)

  • color_col – Optional column for color coding

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/")
>>> diversity = ls.clonality(data)
>>> fig = ls.plot_clonality(diversity)
>>> fig.show()
lymphoseq.plot_common_seqs(data: DataFrame | DataFrame, top_n: int = 20, color_by: str = 'n_repertoires', title: str = 'Sequences Shared Across Repertoires', width: int = 900, height: int = 600) Figure[source]

Plot sequences found in multiple repertoires.

Creates a horizontal bar plot showing shared sequences colored by the number of repertoires they appear in.

Parameters:
  • data – DataFrame from common_seqs() function. Must contain ‘junction_aa’, ‘n_repertoires’, and frequency information.

  • top_n – Number of top sequences to show (default: 20)

  • color_by – Column to color bars by (default: “n_repertoires”)

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with shared sequence bars

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> common = ls.common_seqs(data, min_repertoires=2)
>>> fig = ls.plot_common_seqs(common, top_n=30)
>>> fig.show()
lymphoseq.plot_differential(data: DataFrame | DataFrame, p_threshold: float = 0.05, fc_threshold: float = 1.0, label_top: int = 10, title: str = 'Differential Abundance (Volcano Plot)', width: int = 900, height: int = 700) Figure[source]

Create a volcano plot for differential abundance analysis.

Plots log2 fold change vs -log10(p-value) to visualize significantly different sequences between groups.

Parameters:
  • data – DataFrame from differential_abundance() function. Must contain ‘log2_fold_change’, ‘p_value’, and sequence columns.

  • p_threshold – P-value threshold for significance (default: 0.05)

  • fc_threshold – Absolute log2 fold change threshold (default: 1.0)

  • label_top – Number of top significant sequences to label (default: 10)

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with volcano plot

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> diff = ls.differential_abundance(
...     data,
...     group1=["Pre1", "Pre2"],
...     group2=["Post1", "Post2"]
... )
>>> fig = ls.plot_differential(diff, label_top=15)
>>> fig.show()
lymphoseq.plot_diversity(data: DataFrame | DataFrame, metrics: List[str] = ['clonality', 'gini_coefficient', 'unique_productive_sequences'], title: str = 'Diversity Metrics Comparison', width: int = 1000, height: int = 600) Figure[source]

Create a multi-metric diversity comparison plot.

Parameters:
  • data – Data frame with diversity results

  • metrics – List of metrics to plot

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with subplots

Examples

>>> fig = ls.plot_diversity(diversity_results)
>>> fig.show()
lymphoseq.plot_gene_usage(data: DataFrame | DataFrame, top_n: int = 15, facet_by: str | None = None, title: str = 'Gene Usage Frequencies', width: int = 900, height: int = 600) Figure[source]

Plot V, D, or J gene usage frequencies.

Creates horizontal bar plots showing the most frequent genes, optionally faceted by repertoire or condition.

Parameters:
  • data – DataFrame from gene_freq() function. Must contain ‘gene_name’, ‘frequency’, and ‘repertoire_id’ columns.

  • top_n – Number of top genes to show per group (default: 15)

  • facet_by – Column to create separate subplots (default: None)

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with gene usage bars

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> v_usage = ls.gene_freq(data, gene="v")
>>> fig = ls.plot_gene_usage(v_usage, top_n=20)
>>> fig.show()
>>> # Facet by repertoire
>>> fig = ls.plot_gene_usage(v_usage, facet_by="repertoire_id")
lymphoseq.plot_lorenz_curve(data: DataFrame | DataFrame, repertoire_ids: List[str] | None = None, title: str = 'Lorenz Curve - Clonal Inequality', width: int = 800, height: int = 600) Figure[source]

Plot Lorenz curves to visualize clonal inequality.

The Lorenz curve shows cumulative frequency distribution, helping visualize how evenly sequences are distributed. A more diagonal line indicates higher diversity, while a curve closer to the bottom-right indicates clonal dominance (high inequality).

Parameters:
  • data – DataFrame with sequence frequencies. Must contain ‘repertoire_id’ and ‘duplicate_frequency’ columns.

  • repertoire_ids – Optional list of specific repertoires to plot

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with Lorenz curves

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> fig = ls.plot_lorenz_curve(data)
>>> fig.show()
>>> # Compare specific samples
>>> fig = ls.plot_lorenz_curve(
...     data,
...     repertoire_ids=["Pre_treatment", "Post_treatment"]
... )

Notes

  • Diagonal line represents perfect equality

  • Area between curve and diagonal is the Gini coefficient

  • Steeper curves = more clonal (less diverse)

  • More diagonal = more diverse (even distribution)

See also

  • clonality(): Calculate diversity metrics including Gini

  • plot_clonality(): Visualize clonality scores

lymphoseq.plot_rarefaction(data: DataFrame | DataFrame, color_by: str = 'repertoire_id', show_ci: bool = True, title: str = 'Rarefaction Curves', width: int = 900, height: int = 600) Figure[source]

Plot rarefaction curves showing diversity vs sampling depth.

Creates line plots showing how unique sequences increase with sequencing depth, with optional confidence intervals. Helps assess sampling completeness and compare diversity across samples.

Parameters:
  • data – DataFrame from rarefaction_curve() function. Must contain: ‘sample_size’, ‘unique_sequences’, ‘repertoire_id’ columns.

  • color_by – Column to color lines by (default: “repertoire_id”)

  • show_ci – Whether to show confidence intervals (default: True)

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with rarefaction curves

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> rarefaction = ls.rarefaction_curve(data, iterations=100)
>>> fig = ls.plot_rarefaction(rarefaction)
>>> fig.show()
>>> # Customize appearance
>>> fig = ls.plot_rarefaction(
...     rarefaction,
...     show_ci=False,
...     title="Sample Diversity Rarefaction"
... )
lymphoseq.plot_similarity(data: DataFrame | DataFrame, metric: str = 'similarity', title: str = 'Repertoire Similarity Heatmap', width: int = 700, height: int = 600, colorscale: str = 'RdBu') Figure[source]

Plot repertoire similarity as a heatmap.

Creates a heatmap showing pairwise similarity scores between repertoires from clonal_relatedness() analysis.

Parameters:
  • data – DataFrame from clonal_relatedness() function. Must contain ‘repertoire1’, ‘repertoire2’, and similarity metric column.

  • metric – Column name containing similarity values (default: “similarity”)

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

  • colorscale – Plotly colorscale name (default: “RdBu”)

Returns:

Plotly figure object with similarity heatmap

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> similarity = ls.clonal_relatedness(data, method="morisita")
>>> fig = ls.plot_similarity(similarity)
>>> fig.show()
>>> # Use different colorscale
>>> fig = ls.plot_similarity(similarity, colorscale="Viridis")
lymphoseq.plot_top_seqs(data: DataFrame | DataFrame, top: int = 10, repertoire_ids: List[str] | None = None, title: str = 'Top Sequence Frequencies', width: int = 900, height: int = 600) Figure[source]

1 Create stacked bar plot showing cumulative frequency of top sequences.

Shows the top N most abundant sequences per repertoire as colored bands, with all remaining sequences grouped as “Other”. Useful for visualizing clonal dominance and repertoire diversity at a glance.

Parameters:
  • data – DataFrame with sequence data. Must contain ‘repertoire_id’, ‘junction_aa’, and ‘duplicate_frequency’ columns.

  • top – Number of top sequences to show individually (default: 10)

  • repertoire_ids – Optional list of specific repertoires to plot

  • title – Plot title

  • width – Plot width in pixels

  • height – Plot height in pixels

Returns:

Plotly figure object with stacked bar chart

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Show top 15 sequences per sample
>>> fig = ls.plot_top_seqs(data, top=15)
>>> fig.show()
>>> # Compare specific samples
>>> fig = ls.plot_top_seqs(
...     data,
...     top=10,
...     repertoire_ids=["S1", "S2", "S3"]
... )

Notes

  • Sequences are ranked within each repertoire

  • “Other” category includes all sequences beyond top N

  • Colors cycle through a spectral palette

  • Repertoires ordered by diversity (most diverse first)

See also

  • top_seqs(): Get the top N sequences

  • plot_clonality(): Visualize clonality metrics

lymphoseq.productive_seq(data: DataFrame | DataFrame, aggregate: str = 'junction_aa', productive_only: bool = True) DataFrame | DataFrame[source]

Filter productive sequences and optionally aggregate by amino acid or nucleotide.

Filters the data for productive sequences (no stop codons, in-frame) and aggregates counts by the specified sequence column. This is typically used to prepare data for diversity analysis and visualization.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain ‘productive’ column and the column specified in aggregate parameter.

  • aggregate – Column to aggregate by. Options: - “junction_aa”: CDR3 amino acid sequences (default) - “junction”: CDR3 nucleotide sequences - None: No aggregation, just filter productive sequences

  • productive_only – Whether to filter for productive sequences only (default: True). If False, returns all sequences.

Returns:

DataFrame with filtered and aggregated sequences, maintaining the same type (Polars or Pandas) as the input.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Get productive amino acid sequences (most common use case)
>>> amino_table = ls.productive_seq(data, aggregate="junction_aa")
>>> print(f"Unique amino acid sequences: {len(amino_table)}")
>>> # Get productive nucleotide sequences
>>> nucl_table = ls.productive_seq(data, aggregate="junction")
>>> # Just filter productive, no aggregation
>>> productive = ls.productive_seq(data, aggregate=None)

Notes

  • Aggregation sums duplicate_count and recalculates duplicate_frequency

  • When aggregating, the first occurrence’s metadata is kept

  • Productive sequences are those where productive=True (no stop codons, in-frame)

See also

  • top_seqs(): Get top sequences by frequency

  • unique_seqs(): Get unique sequences without aggregation

lymphoseq.rarefaction_curve(data: DataFrame | DataFrame, repertoire_ids: List[str] | None = None, step_size: int | None = None, iterations: int = 100, group_by: str = 'repertoire_id') DataFrame | DataFrame[source]

Generate rarefaction curves for diversity analysis.

Creates rarefaction curves by subsampling at multiple depths and calculating diversity metrics. This helps compare repertoires with different sequencing depths and assess sampling completeness.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain ‘duplicate_count’, ‘junction_aa’, and group_by columns.

  • repertoire_ids – List of repertoire IDs to analyze. If None, uses all repertoires (default: None)

  • step_size – Number of reads between sampling points. If None, uses 10 steps across the range (default: None)

  • iterations – Number of iterations for each sampling depth (default: 100)

  • group_by – Column to group by (default: “repertoire_id”)

Returns:

  • repertoire_id: Repertoire identifier

  • sample_size: Number of reads sampled

  • unique_sequences: Mean number of unique sequences

  • unique_sequences_sd: Standard deviation across iterations

  • clonality: Mean clonality at this depth

  • clonality_sd: Standard deviation of clonality

Return type:

DataFrame with rarefaction curve data containing

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Generate rarefaction curves for all samples
>>> rarefaction = ls.rarefaction_curve(data, iterations=100)
>>> print(rarefaction.head())
>>> # Generate curves for specific samples
>>> rarefaction = ls.rarefaction_curve(
...     data,
...     repertoire_ids=["S1", "S2"],
...     step_size=1000
... )
>>> # Plot rarefaction curves (if using pandas)
>>> import matplotlib.pyplot as plt
>>> for rep in rarefaction["repertoire_id"].unique():
...     subset = rarefaction[rarefaction["repertoire_id"] == rep]
...     plt.plot(subset["sample_size"], subset["unique_sequences"], label=rep)
>>> plt.xlabel("Sample size")
>>> plt.ylabel("Unique sequences")
>>> plt.legend()
>>> plt.show()

Notes

  • Rarefaction helps compare diversity across samples with different depths

  • Sampling is done with replacement, weighted by sequence abundance

  • Curves that haven’t plateaued suggest undersampling

  • Standard deviations indicate sampling variance

See also

  • clonality(): Calculate diversity metrics with optional rarefaction

  • diversity_metrics(): Comprehensive diversity analysis

lymphoseq.read_10x(path: str | Path | List[str], recursive: bool = False, collapse_chains: bool = True, parallel: bool = True, threads: int | None = None, return_type: str = 'polars', validate_airr: bool = True, enhanced_mappings: bool = True) DataFrame | DataFrame[source]

Read 10X Genomics VDJ sequencing files.

This function imports files from 10X Genomics Cell Ranger VDJ analysis and standardizes them to AIRR format. Can handle both TSV and JSON formats.

Parameters:
  • path – Path to directory containing files, single file, or list of files

  • recursive – Search recursively for files in subdirectories

  • collapse_chains – Combine alpha and beta chains for T-cell data

  • parallel – Process files in parallel

  • threads – Number of threads to use

  • return_type – Return format (“polars”, “pandas”)

  • validate_airr – Enable AIRR schema validation

  • enhanced_mappings – Use comprehensive column mappings

Returns:

Parsed and standardized data frame

Examples

>>> # Read 10X VDJ data directory
>>> data = read_10x("data/10x_vdj/")
>>> # Read specific file
>>> data = read_10x("filtered_contig_annotations.csv")
>>> # Read without chain collapsing
>>> data = read_10x("data/", collapse_chains=False)
lymphoseq.read_immunoseq(path: str | Path | List[str], recursive: bool = False, parallel: bool = True, threads: int | None = None, chunk_size: int | None = None, max_memory_gb: float = 8.0, streaming_mode: bool = False, temp_dir: str | Path | None = None, progress_detail: str = 'basic', return_type: str = 'polars', use_arrow: str | bool = 'auto', validate_airr: bool = True, enhanced_mappings: bool = True, verbose: bool = False) DataFrame | DataFrame | LazyFrame[source]

Read Adaptive ImmunoSEQ files.

This function imports tab-separated value (.tsv) files exported by the Adaptive Biotechnologies ImmunoSEQ analyzer and standardizes them to AIRR format.

Parameters:
  • path – Path to directory containing TSV files, single file, or list of files

  • recursive – Search recursively for files in subdirectories

  • parallel – Process files in parallel

  • threads – Number of threads to use (default: auto-detect)

  • chunk_size – Number of files to process in each chunk for memory efficiency

  • max_memory_gb – Maximum memory to use in GB

  • streaming_mode – Process extremely large files in streaming chunks. IMPORTANT: When True, returns a Polars LazyFrame instead of DataFrame to avoid loading the entire dataset into memory. Use .collect() to materialize, or work with lazy operations.

  • temp_dir – Temporary directory for cache files (default: system temp)

  • progress_detail – Level of progress reporting (“none”, “basic”, “detailed”)

  • return_type – Return format (“polars”, “pandas”)

  • use_arrow – Use Apache Arrow for large datasets (“auto”, “always”, “never”)

  • validate_airr – Enable AIRR schema validation

  • enhanced_mappings – Use comprehensive column mappings

  • verbose – Show detailed progress messages (default: False)

Returns:

  • DataFrame (polars or pandas) if streaming_mode=False

  • LazyFrame (polars) if streaming_mode=True (use .collect() to load)

Examples

>>> # Read all TSV files in a directory
>>> data = read_immunoseq("data/immunoseq/")
>>> # Read specific files
>>> data = read_immunoseq(["sample1.tsv", "sample2.tsv"])
>>> # Read with custom settings
>>> data = read_immunoseq(
...     "data/",
...     parallel=True,
...     threads=4,
...     return_type="pandas"
... )
>>> # For very large datasets (100+ GB), use streaming mode
>>> # This returns a LazyFrame to avoid loading into memory
>>> lazy_data = read_immunoseq(
...     "data/large_dataset/",
...     streaming_mode=True,
...     chunk_size=3,
...     parallel=False,
...     verbose=False
... )
>>> # Work with lazy operations - only loads what you need
>>> result = lazy_data.filter(pl.col("duplicate_count") > 10).collect()
>>> # Or collect specific columns only
>>> counts = lazy_data.select(["repertoire_id", "duplicate_count"]).collect()
>>> # The Arrow file is saved in temp directory and shown in output
>>> # You can also scan it directly later:
>>> lazy_data = pl.scan_ipc("/tmp/lymphoseq_output_abc12345.arrow")
lymphoseq.read_mixcr(path: str | Path | List[str], recursive: bool = False, parallel: bool = True, threads: int | None = None, return_type: str = 'polars', validate_airr: bool = True, enhanced_mappings: bool = True) DataFrame | DataFrame[source]

Read MiXCR pipeline output files.

This function imports tab-separated value (.tsv) files generated by MiXCR and standardizes them to AIRR format.

Parameters:
  • path – Path to directory containing TSV files, single file, or list of files

  • recursive – Search recursively for files in subdirectories

  • parallel – Process files in parallel

  • threads – Number of threads to use

  • return_type – Return format (“polars”, “pandas”)

  • validate_airr – Enable AIRR schema validation

  • enhanced_mappings – Use comprehensive column mappings

Returns:

Parsed and standardized data frame

Examples

>>> # Read MiXCR output directory
>>> data = read_mixcr("data/mixcr_output/")
>>> # Read specific file
>>> data = read_mixcr("clones.tsv")
>>> # Read with pandas backend
>>> data = read_mixcr("data/", return_type="pandas")
lymphoseq.remove_seq(data: DataFrame | DataFrame, sequences: str | List[str], column: str = 'junction_aa', invert: bool = False) DataFrame | DataFrame[source]

Remove (or keep only) specific sequences from the data.

Removes sequences matching the specified criteria, or keeps only those sequences if invert=True.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • sequences – Single sequence or list of sequences to remove/keep

  • column – Column name containing sequences to match (default: “junction_aa”)

  • invert – If True, keep only matching sequences instead of removing them (default: False)

Returns:

DataFrame with sequences removed (or kept if invert=True), maintaining the same type (Polars or Pandas) as the input.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Remove a specific sequence
>>> filtered = ls.remove_seq(data, sequences="CASSLKPNTEAFF")
>>> # Remove multiple sequences
>>> to_remove = ["CASSLKPNTEAFF", "CASSXXXXTEAFF"]
>>> filtered = ls.remove_seq(data, sequences=to_remove)
>>> # Keep only specific sequences
>>> keep_these = ["CASSLKPNTEAFF", "CASSXXXXTEAFF"]
>>> subset = ls.remove_seq(data, sequences=keep_these, invert=True)

Notes

  • Case-sensitive matching

  • Uses exact string matching

See also

  • productive_seq(): Filter productive sequences

  • top_seqs(): Select top sequences

lymphoseq.searchSeq(data: DataFrame | DataFrame, sequence: str, by_column: str = 'junction_aa', exact: bool = True) DataFrame | DataFrame[source]

Search for specific sequences or patterns in the data.

Finds all occurrences of a sequence (exact match) or sequences containing a pattern (substring match) across all repertoires.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • sequence – Sequence string to search for

  • by_column – Column to search in (default: “junction_aa”)

  • exact – If True, exact match; if False, substring match (default: True)

Returns:

DataFrame containing all matching sequences with their frequencies and repertoire information.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Find exact sequence
>>> matches = ls.searchSeq(data, "CASSLKPNTEAFF")
>>> print(f"Found in {matches['repertoire_id'].n_unique()} repertoires")
>>> # Find sequences containing motif
>>> motif_matches = ls.searchSeq(data, "LKPN", exact=False)
>>> print(f"Found {len(motif_matches)} sequences with LKPN motif")

See also

  • remove_seq(): Remove specific sequences

  • common_seqs(): Find shared sequences

lymphoseq.search_db(data: DataFrame | DataFrame, databases: Literal['all'] | List[str] = 'all', chain: Literal['tra', 'trb', 'igh', 'igk', 'igl'] = 'trb', by_column: str = 'junction_aa', include_similarity: bool = False, cache_dir: str | None = None) DataFrame | DataFrame[source]

Search public databases for TCR/BCR sequences with known antigen specificity.

Searches VDJdb, McPAS-TCR, and IEDB databases to find if your sequences match known antigen-specific receptors. Returns annotated data with epitope, antigen, MHC allele, and reference information.

Parameters:
  • data – DataFrame with sequence data

  • databases – List of databases to search or “all” for all databases Options: [“VDJdb”, “McPAS-TCR”, “IEDB”]

  • chain – Receptor chain type - “tra”, “trb”, “igh”, “igk”, or “igl”

  • by_column – Column to search (default: junction_aa)

  • include_similarity – Include similar (not exact) matches (requires online API)

  • cache_dir – Directory to cache database files

Returns:

DataFrame annotated with antigen specificity information

Examples

>>> # Search VDJdb for TRB sequences
>>> annotated = search_db(df, databases=["VDJdb"], chain="trb")
>>> # Find sequences with known epitopes
>>> matches = annotated[annotated["epitope"].notna()]
>>> print(matches[["junction_aa", "epitope", "antigen", "mhc_allele"]])

Notes

lymphoseq.search_published(data: DataFrame | DataFrame, by_column: str = 'junction_aa') DataFrame | DataFrame[source]

Search for sequences in published TCR literature.

Annotates your sequences with information from published studies if they match known TCR sequences in the literature.

Parameters:
  • data – DataFrame with sequence data

  • by_column – Column to search (default: junction_aa)

Returns:

DataFrame annotated with published sequence information

Examples

>>> # Find published sequences in your data
>>> annotated = search_published(df)
>>> published_seqs = annotated[annotated["published"].notna()]

Notes

This is a wrapper around search_db that searches all available databases. For more control, use search_db() directly.

lymphoseq.split_chains(data: DataFrame | DataFrame, separator: str = ':') DataFrame | DataFrame[source]

Split merged junction sequences back into separate alpha and beta chains.

This is the reverse operation of merge_chains(), taking merged sequences and splitting them back into tra_junction_aa and trb_junction_aa columns.

Parameters:
  • data – DataFrame with merged junction_aa column

  • separator – String that was used to join sequences (default: “:”)

Returns:

DataFrame with separated tra_junction_aa and trb_junction_aa columns

Examples

>>> # Merge chains
>>> merged = merge_chains(data_10x)
>>>
>>> # Split them back
>>> split = split_chains(merged)
>>>
>>> # Check the split columns
>>> print(split[["tra_junction_aa", "trb_junction_aa"]].head())
lymphoseq.top_seqs(data: DataFrame | DataFrame, top: int = 1, group_by: str = 'repertoire_id', order_by: str = 'duplicate_frequency') DataFrame | DataFrame[source]

Select top N sequences from each repertoire by frequency.

Creates a DataFrame containing the top productive sequences from each repertoire, ordered by their frequencies.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences. Must contain columns specified in group_by and order_by parameters.

  • top – Number of top sequences to select from each repertoire (default: 1)

  • group_by – Column name to group by (default: “repertoire_id”)

  • order_by – Column name to order by (default: “duplicate_frequency”)

Returns:

DataFrame containing top N sequences from each repertoire, maintaining the same type (Polars or Pandas) as the input.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Get top 10 sequences from each repertoire
>>> top_10 = ls.top_seqs(data, top=10)
>>> print(f"Selected {len(top_10)} sequences")
>>> # Get most abundant clone from each sample
>>> top_clone = ls.top_seqs(data, top=1)
>>> print(top_clone[["repertoire_id", "junction_aa", "duplicate_frequency"]])

See also

  • productive_seq(): Filter for productive sequences

  • unique_seqs(): Get unique sequences

lymphoseq.unique_seqs(data: DataFrame | DataFrame, by_column: str = 'junction_aa', keep: str = 'first') DataFrame | DataFrame[source]

Get unique sequences based on specified column.

Returns unique sequences based on the specified column, keeping either the first or last occurrence of duplicates.

Parameters:
  • data – Input DataFrame with AIRR-formatted sequences

  • by_column – Column name to determine uniqueness (default: “junction_aa”)

  • keep – Which occurrence to keep for duplicates: - “first”: Keep first occurrence (default) - “last”: Keep last occurrence

Returns:

DataFrame with unique sequences, maintaining the same type (Polars or Pandas) as the input.

Examples

>>> import lymphoseq as ls
>>> data = ls.read_immunoseq("data/immunoseq/")
>>> # Get unique amino acid sequences
>>> unique_aa = ls.unique_seqs(data, by_column="junction_aa")
>>> print(f"Unique sequences: {len(unique_aa)}")
>>> # Get unique nucleotide sequences
>>> unique_nt = ls.unique_seqs(data, by_column="junction")

Notes

  • This function does not aggregate counts like productive_seq()

  • Use productive_seq() with aggregate parameter for count aggregation

See also

  • productive_seq(): Filter and aggregate sequences

  • top_seqs(): Get top sequences by frequency