TCRmeta 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tcrmeta-0.1.0/PKG-INFO +154 -0
- tcrmeta-0.1.0/README.md +126 -0
- tcrmeta-0.1.0/pyproject.toml +46 -0
- tcrmeta-0.1.0/setup.cfg +4 -0
- tcrmeta-0.1.0/src/TCRmeta.egg-info/PKG-INFO +154 -0
- tcrmeta-0.1.0/src/TCRmeta.egg-info/SOURCES.txt +22 -0
- tcrmeta-0.1.0/src/TCRmeta.egg-info/dependency_links.txt +1 -0
- tcrmeta-0.1.0/src/TCRmeta.egg-info/requires.txt +15 -0
- tcrmeta-0.1.0/src/TCRmeta.egg-info/top_level.txt +1 -0
- tcrmeta-0.1.0/src/tcrmeta/__init__.py +22 -0
- tcrmeta-0.1.0/src/tcrmeta/_dataset.py +53 -0
- tcrmeta-0.1.0/src/tcrmeta/_model.py +131 -0
- tcrmeta-0.1.0/src/tcrmeta/_tokenizer.py +23 -0
- tcrmeta-0.1.0/src/tcrmeta/_utils.py +185 -0
- tcrmeta-0.1.0/src/tcrmeta/_weights.py +119 -0
- tcrmeta-0.1.0/src/tcrmeta/css.py +230 -0
- tcrmeta-0.1.0/src/tcrmeta/embedding.py +334 -0
- tcrmeta-0.1.0/src/tcrmeta/energy.py +322 -0
- tcrmeta-0.1.0/src/tcrmeta/reference.py +262 -0
- tcrmeta-0.1.0/src/tcrmeta/umap_plot.py +178 -0
- tcrmeta-0.1.0/tests/test_energy.py +100 -0
- tcrmeta-0.1.0/tests/test_reference_and_css.py +151 -0
- tcrmeta-0.1.0/tests/test_umap_plot.py +60 -0
- tcrmeta-0.1.0/tests/test_utils.py +69 -0
tcrmeta-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: TCRmeta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Antigen-aware TCR repertoire embeddings: pretrained ESM2 + contrastive fine-tuning, with downstream CSS scoring, UMAP projection, and energy-distance shift analysis.
|
|
5
|
+
Author: Mingyao Pan
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Mia-yao/TCRmeta
|
|
8
|
+
Keywords: TCR,immunology,repertoire,embedding,ESM2,contrastive learning
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: numpy>=1.23
|
|
15
|
+
Requires-Dist: pandas>=1.5
|
|
16
|
+
Requires-Dist: scipy>=1.9
|
|
17
|
+
Requires-Dist: scikit-learn>=1.2
|
|
18
|
+
Requires-Dist: torch>=1.13
|
|
19
|
+
Requires-Dist: transformers>=4.30
|
|
20
|
+
Requires-Dist: umap-learn>=0.5
|
|
21
|
+
Requires-Dist: joblib>=1.2
|
|
22
|
+
Requires-Dist: tqdm>=4.64
|
|
23
|
+
Requires-Dist: huggingface_hub>=0.20
|
|
24
|
+
Requires-Dist: matplotlib>=3.6
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# TCRmeta
|
|
30
|
+
|
|
31
|
+
Antigen-aware TCR repertoire embeddings and downstream repertoire-level
|
|
32
|
+
analysis, built on a pretrained-ESM2 + contrastively fine-tuned encoder.
|
|
33
|
+
|
|
34
|
+
Given a bulk TCR repertoire, TCRmeta can:
|
|
35
|
+
|
|
36
|
+
1. **Embed** every clone into a TCR embedding (`embed_repertoire`) — pick between a "pretrained" or a "final" embedding, see below.
|
|
37
|
+
2. **Score** a repertoire's clonal shift score (CSS) against a reference cohort (`compute_css`).
|
|
38
|
+
3. **Project** a repertoire onto a reference UMAP map, per V gene (`plot_umap`).
|
|
39
|
+
4. **Compare** two repertoires via a frequency-weighted energy-distance shift (`energy_shift`).
|
|
40
|
+
|
|
41
|
+
Model weights and the default reference map are downloaded once from the
|
|
42
|
+
Hugging Face Hub and cached locally (`~/.cache/tcrmeta` by default).
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install tcrmeta
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
GPU acceleration (embedding + energy-distance computation) is used
|
|
51
|
+
automatically if a CUDA (or MPS) device is available; pass `device="cpu"`
|
|
52
|
+
anywhere to force CPU.
|
|
53
|
+
|
|
54
|
+
## Input format
|
|
55
|
+
|
|
56
|
+
Every function takes a TCR repertoire as a `pandas.DataFrame` with
|
|
57
|
+
**exactly** these columns (no aliases are auto-detected — rename your own
|
|
58
|
+
columns first):
|
|
59
|
+
|
|
60
|
+
| column | meaning |
|
|
61
|
+
|----------|-------------------------------------------|
|
|
62
|
+
| `cdr3aa` | CDR3 amino acid sequence |
|
|
63
|
+
| `v_gene` | V gene (e.g. `TRBV6-4`, allele suffix ok) |
|
|
64
|
+
| `count` | clone count / templates / reads |
|
|
65
|
+
|
|
66
|
+
Currently supports human TRB (beta chain) repertoires only.
|
|
67
|
+
|
|
68
|
+
## Quickstart
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import pandas as pd
|
|
72
|
+
import tcrmeta as tm
|
|
73
|
+
|
|
74
|
+
df = pd.read_csv("my_repertoire.csv") # cdr3aa, v_gene, count
|
|
75
|
+
|
|
76
|
+
# 1. Embed (embedding_type="final" is the default — see "Choosing an
|
|
77
|
+
# embedding type" below for when to use "pretrained" instead)
|
|
78
|
+
embeddings = tm.embed_repertoire(df) # dict {(cdr3aa, v_gene): 64-dim np.ndarray}
|
|
79
|
+
|
|
80
|
+
# 2. CSS against the shipped default reference
|
|
81
|
+
per_gene_css, whole_css = tm.compute_css(df) # v_gene=None -> scores all reference V genes
|
|
82
|
+
|
|
83
|
+
# 3. UMAP projection (v_gene required)
|
|
84
|
+
fig, coords = tm.plot_umap(df, v_gene="TRBV6-4")
|
|
85
|
+
fig.savefig("trbv6-4_umap.png")
|
|
86
|
+
|
|
87
|
+
# 4. Energy-distance shift between two repertoires
|
|
88
|
+
df2 = pd.read_csv("other_repertoire.csv")
|
|
89
|
+
per_gene_energy, weighted_summary = tm.energy_shift(df, df2)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Building your own reference map
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
reference_repertoires = [pd.read_csv(f) for f in my_reference_files]
|
|
96
|
+
reference = tm.build_reference(reference_repertoires)
|
|
97
|
+
tm.save_reference(reference, "my_reference.pkl")
|
|
98
|
+
|
|
99
|
+
per_gene_css, whole_css = tm.compute_css(df, reference="my_reference.pkl")
|
|
100
|
+
fig, coords = tm.plot_umap(df, reference=reference, v_gene="TRBV6-4")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Choosing an embedding type
|
|
104
|
+
|
|
105
|
+
`embed_repertoire` accepts an `embedding_type` argument with two options:
|
|
106
|
+
|
|
107
|
+
| `embedding_type` | Dim | What it is | What it captures |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `"pretrained"` | 480 | The raw CLS embedding straight out of the masked-language-model-pretrained ESM2-style base encoder, before any contrastive fine-tuning. | **Local structure** — this encoder is trained to recover masked residues from local sequence context, so the embedding is most sensitive to motif/sub-sequence-level similarity between TCRs. |
|
|
110
|
+
| `"final"` (default) | 64 | The 480-dim base embedding run through the ensemble of 7 contrastively fine-tuned projection heads, GPA-aligned and mean-fused, L2-normalized. | **Overall structure** — contrastive fine-tuning pulls together TCRs recognizing the same antigen regardless of local sequence differences, so this embedding is most sensitive to antigen-specificity-level, global similarity. |
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Local-structure embedding
|
|
114
|
+
emb_pretrained = tm.embed_repertoire(df, embedding_type="pretrained")
|
|
115
|
+
|
|
116
|
+
# Overall-structure embedding (default; same as tm.embed_repertoire(df))
|
|
117
|
+
emb_final = tm.embed_repertoire(df, embedding_type="final")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This choice is only exposed on `embed_repertoire` itself, for users who
|
|
121
|
+
want the raw embeddings for their own downstream analysis.
|
|
122
|
+
`compute_css`, `plot_umap`, `energy_shift`, and `build_reference` always
|
|
123
|
+
embed internally with `embedding_type="final"` — TCRmeta's own
|
|
124
|
+
repertoire-level statistics (and the shipped reference map) are all
|
|
125
|
+
defined against that 64-dim antigen-aware embedding space, so these
|
|
126
|
+
functions ignore any `embedding_type` passed to them.
|
|
127
|
+
|
|
128
|
+
### Keeping intermediate embeddings
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
tm.embed_repertoire(df, save_path="my_repertoire_embeddings.pkl")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Pass the saved path back in via `embeddings=` (loaded with `pickle.load`)
|
|
135
|
+
to any downstream function to skip re-embedding, or just delete the file
|
|
136
|
+
if you don't need it.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
| Env var | Purpose |
|
|
141
|
+
|------------------------|-------------------------------------------------------|
|
|
142
|
+
| `TCRMETA_HF_REPO` | Hugging Face Hub repo id for weights/reference (placeholder until published) |
|
|
143
|
+
| `TCRMETA_WEIGHTS_DIR` | Load weights from a local directory instead of the Hub |
|
|
144
|
+
| `TCRMETA_CACHE_DIR` | Local cache directory (default `~/.cache/tcrmeta`) |
|
|
145
|
+
|
|
146
|
+
See `scripts/upload_weights.py` for a one-time script to publish your
|
|
147
|
+
model weights and default reference map to the Hugging Face Hub.
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pip install -e ".[dev]"
|
|
153
|
+
pytest
|
|
154
|
+
```
|
tcrmeta-0.1.0/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# TCRmeta
|
|
2
|
+
|
|
3
|
+
Antigen-aware TCR repertoire embeddings and downstream repertoire-level
|
|
4
|
+
analysis, built on a pretrained-ESM2 + contrastively fine-tuned encoder.
|
|
5
|
+
|
|
6
|
+
Given a bulk TCR repertoire, TCRmeta can:
|
|
7
|
+
|
|
8
|
+
1. **Embed** every clone into a TCR embedding (`embed_repertoire`) — pick between a "pretrained" or a "final" embedding, see below.
|
|
9
|
+
2. **Score** a repertoire's clonal shift score (CSS) against a reference cohort (`compute_css`).
|
|
10
|
+
3. **Project** a repertoire onto a reference UMAP map, per V gene (`plot_umap`).
|
|
11
|
+
4. **Compare** two repertoires via a frequency-weighted energy-distance shift (`energy_shift`).
|
|
12
|
+
|
|
13
|
+
Model weights and the default reference map are downloaded once from the
|
|
14
|
+
Hugging Face Hub and cached locally (`~/.cache/tcrmeta` by default).
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install tcrmeta
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
GPU acceleration (embedding + energy-distance computation) is used
|
|
23
|
+
automatically if a CUDA (or MPS) device is available; pass `device="cpu"`
|
|
24
|
+
anywhere to force CPU.
|
|
25
|
+
|
|
26
|
+
## Input format
|
|
27
|
+
|
|
28
|
+
Every function takes a TCR repertoire as a `pandas.DataFrame` with
|
|
29
|
+
**exactly** these columns (no aliases are auto-detected — rename your own
|
|
30
|
+
columns first):
|
|
31
|
+
|
|
32
|
+
| column | meaning |
|
|
33
|
+
|----------|-------------------------------------------|
|
|
34
|
+
| `cdr3aa` | CDR3 amino acid sequence |
|
|
35
|
+
| `v_gene` | V gene (e.g. `TRBV6-4`, allele suffix ok) |
|
|
36
|
+
| `count` | clone count / templates / reads |
|
|
37
|
+
|
|
38
|
+
Currently supports human TRB (beta chain) repertoires only.
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import pandas as pd
|
|
44
|
+
import tcrmeta as tm
|
|
45
|
+
|
|
46
|
+
df = pd.read_csv("my_repertoire.csv") # cdr3aa, v_gene, count
|
|
47
|
+
|
|
48
|
+
# 1. Embed (embedding_type="final" is the default — see "Choosing an
|
|
49
|
+
# embedding type" below for when to use "pretrained" instead)
|
|
50
|
+
embeddings = tm.embed_repertoire(df) # dict {(cdr3aa, v_gene): 64-dim np.ndarray}
|
|
51
|
+
|
|
52
|
+
# 2. CSS against the shipped default reference
|
|
53
|
+
per_gene_css, whole_css = tm.compute_css(df) # v_gene=None -> scores all reference V genes
|
|
54
|
+
|
|
55
|
+
# 3. UMAP projection (v_gene required)
|
|
56
|
+
fig, coords = tm.plot_umap(df, v_gene="TRBV6-4")
|
|
57
|
+
fig.savefig("trbv6-4_umap.png")
|
|
58
|
+
|
|
59
|
+
# 4. Energy-distance shift between two repertoires
|
|
60
|
+
df2 = pd.read_csv("other_repertoire.csv")
|
|
61
|
+
per_gene_energy, weighted_summary = tm.energy_shift(df, df2)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Building your own reference map
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
reference_repertoires = [pd.read_csv(f) for f in my_reference_files]
|
|
68
|
+
reference = tm.build_reference(reference_repertoires)
|
|
69
|
+
tm.save_reference(reference, "my_reference.pkl")
|
|
70
|
+
|
|
71
|
+
per_gene_css, whole_css = tm.compute_css(df, reference="my_reference.pkl")
|
|
72
|
+
fig, coords = tm.plot_umap(df, reference=reference, v_gene="TRBV6-4")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Choosing an embedding type
|
|
76
|
+
|
|
77
|
+
`embed_repertoire` accepts an `embedding_type` argument with two options:
|
|
78
|
+
|
|
79
|
+
| `embedding_type` | Dim | What it is | What it captures |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| `"pretrained"` | 480 | The raw CLS embedding straight out of the masked-language-model-pretrained ESM2-style base encoder, before any contrastive fine-tuning. | **Local structure** — this encoder is trained to recover masked residues from local sequence context, so the embedding is most sensitive to motif/sub-sequence-level similarity between TCRs. |
|
|
82
|
+
| `"final"` (default) | 64 | The 480-dim base embedding run through the ensemble of 7 contrastively fine-tuned projection heads, GPA-aligned and mean-fused, L2-normalized. | **Overall structure** — contrastive fine-tuning pulls together TCRs recognizing the same antigen regardless of local sequence differences, so this embedding is most sensitive to antigen-specificity-level, global similarity. |
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# Local-structure embedding
|
|
86
|
+
emb_pretrained = tm.embed_repertoire(df, embedding_type="pretrained")
|
|
87
|
+
|
|
88
|
+
# Overall-structure embedding (default; same as tm.embed_repertoire(df))
|
|
89
|
+
emb_final = tm.embed_repertoire(df, embedding_type="final")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This choice is only exposed on `embed_repertoire` itself, for users who
|
|
93
|
+
want the raw embeddings for their own downstream analysis.
|
|
94
|
+
`compute_css`, `plot_umap`, `energy_shift`, and `build_reference` always
|
|
95
|
+
embed internally with `embedding_type="final"` — TCRmeta's own
|
|
96
|
+
repertoire-level statistics (and the shipped reference map) are all
|
|
97
|
+
defined against that 64-dim antigen-aware embedding space, so these
|
|
98
|
+
functions ignore any `embedding_type` passed to them.
|
|
99
|
+
|
|
100
|
+
### Keeping intermediate embeddings
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
tm.embed_repertoire(df, save_path="my_repertoire_embeddings.pkl")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Pass the saved path back in via `embeddings=` (loaded with `pickle.load`)
|
|
107
|
+
to any downstream function to skip re-embedding, or just delete the file
|
|
108
|
+
if you don't need it.
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
| Env var | Purpose |
|
|
113
|
+
|------------------------|-------------------------------------------------------|
|
|
114
|
+
| `TCRMETA_HF_REPO` | Hugging Face Hub repo id for weights/reference (placeholder until published) |
|
|
115
|
+
| `TCRMETA_WEIGHTS_DIR` | Load weights from a local directory instead of the Hub |
|
|
116
|
+
| `TCRMETA_CACHE_DIR` | Local cache directory (default `~/.cache/tcrmeta`) |
|
|
117
|
+
|
|
118
|
+
See `scripts/upload_weights.py` for a one-time script to publish your
|
|
119
|
+
model weights and default reference map to the Hugging Face Hub.
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install -e ".[dev]"
|
|
125
|
+
pytest
|
|
126
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "TCRmeta"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Antigen-aware TCR repertoire embeddings: pretrained ESM2 + contrastive fine-tuning, with downstream CSS scoring, UMAP projection, and energy-distance shift analysis."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Mingyao Pan" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["TCR", "immunology", "repertoire", "embedding", "ESM2", "contrastive learning"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"numpy>=1.23",
|
|
24
|
+
"pandas>=1.5",
|
|
25
|
+
"scipy>=1.9",
|
|
26
|
+
"scikit-learn>=1.2",
|
|
27
|
+
"torch>=1.13",
|
|
28
|
+
"transformers>=4.30",
|
|
29
|
+
"umap-learn>=0.5",
|
|
30
|
+
"joblib>=1.2",
|
|
31
|
+
"tqdm>=4.64",
|
|
32
|
+
"huggingface_hub>=0.20",
|
|
33
|
+
"matplotlib>=3.6",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = ["pytest>=7.0", "build>=1.0"]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/Mia-yao/TCRmeta"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.package-data]
|
|
46
|
+
tcrmeta = ["data/*.csv"]
|
tcrmeta-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: TCRmeta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Antigen-aware TCR repertoire embeddings: pretrained ESM2 + contrastive fine-tuning, with downstream CSS scoring, UMAP projection, and energy-distance shift analysis.
|
|
5
|
+
Author: Mingyao Pan
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Mia-yao/TCRmeta
|
|
8
|
+
Keywords: TCR,immunology,repertoire,embedding,ESM2,contrastive learning
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: numpy>=1.23
|
|
15
|
+
Requires-Dist: pandas>=1.5
|
|
16
|
+
Requires-Dist: scipy>=1.9
|
|
17
|
+
Requires-Dist: scikit-learn>=1.2
|
|
18
|
+
Requires-Dist: torch>=1.13
|
|
19
|
+
Requires-Dist: transformers>=4.30
|
|
20
|
+
Requires-Dist: umap-learn>=0.5
|
|
21
|
+
Requires-Dist: joblib>=1.2
|
|
22
|
+
Requires-Dist: tqdm>=4.64
|
|
23
|
+
Requires-Dist: huggingface_hub>=0.20
|
|
24
|
+
Requires-Dist: matplotlib>=3.6
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# TCRmeta
|
|
30
|
+
|
|
31
|
+
Antigen-aware TCR repertoire embeddings and downstream repertoire-level
|
|
32
|
+
analysis, built on a pretrained-ESM2 + contrastively fine-tuned encoder.
|
|
33
|
+
|
|
34
|
+
Given a bulk TCR repertoire, TCRmeta can:
|
|
35
|
+
|
|
36
|
+
1. **Embed** every clone into a TCR embedding (`embed_repertoire`) — pick between a "pretrained" or a "final" embedding, see below.
|
|
37
|
+
2. **Score** a repertoire's clonal shift score (CSS) against a reference cohort (`compute_css`).
|
|
38
|
+
3. **Project** a repertoire onto a reference UMAP map, per V gene (`plot_umap`).
|
|
39
|
+
4. **Compare** two repertoires via a frequency-weighted energy-distance shift (`energy_shift`).
|
|
40
|
+
|
|
41
|
+
Model weights and the default reference map are downloaded once from the
|
|
42
|
+
Hugging Face Hub and cached locally (`~/.cache/tcrmeta` by default).
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install tcrmeta
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
GPU acceleration (embedding + energy-distance computation) is used
|
|
51
|
+
automatically if a CUDA (or MPS) device is available; pass `device="cpu"`
|
|
52
|
+
anywhere to force CPU.
|
|
53
|
+
|
|
54
|
+
## Input format
|
|
55
|
+
|
|
56
|
+
Every function takes a TCR repertoire as a `pandas.DataFrame` with
|
|
57
|
+
**exactly** these columns (no aliases are auto-detected — rename your own
|
|
58
|
+
columns first):
|
|
59
|
+
|
|
60
|
+
| column | meaning |
|
|
61
|
+
|----------|-------------------------------------------|
|
|
62
|
+
| `cdr3aa` | CDR3 amino acid sequence |
|
|
63
|
+
| `v_gene` | V gene (e.g. `TRBV6-4`, allele suffix ok) |
|
|
64
|
+
| `count` | clone count / templates / reads |
|
|
65
|
+
|
|
66
|
+
Currently supports human TRB (beta chain) repertoires only.
|
|
67
|
+
|
|
68
|
+
## Quickstart
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import pandas as pd
|
|
72
|
+
import tcrmeta as tm
|
|
73
|
+
|
|
74
|
+
df = pd.read_csv("my_repertoire.csv") # cdr3aa, v_gene, count
|
|
75
|
+
|
|
76
|
+
# 1. Embed (embedding_type="final" is the default — see "Choosing an
|
|
77
|
+
# embedding type" below for when to use "pretrained" instead)
|
|
78
|
+
embeddings = tm.embed_repertoire(df) # dict {(cdr3aa, v_gene): 64-dim np.ndarray}
|
|
79
|
+
|
|
80
|
+
# 2. CSS against the shipped default reference
|
|
81
|
+
per_gene_css, whole_css = tm.compute_css(df) # v_gene=None -> scores all reference V genes
|
|
82
|
+
|
|
83
|
+
# 3. UMAP projection (v_gene required)
|
|
84
|
+
fig, coords = tm.plot_umap(df, v_gene="TRBV6-4")
|
|
85
|
+
fig.savefig("trbv6-4_umap.png")
|
|
86
|
+
|
|
87
|
+
# 4. Energy-distance shift between two repertoires
|
|
88
|
+
df2 = pd.read_csv("other_repertoire.csv")
|
|
89
|
+
per_gene_energy, weighted_summary = tm.energy_shift(df, df2)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Building your own reference map
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
reference_repertoires = [pd.read_csv(f) for f in my_reference_files]
|
|
96
|
+
reference = tm.build_reference(reference_repertoires)
|
|
97
|
+
tm.save_reference(reference, "my_reference.pkl")
|
|
98
|
+
|
|
99
|
+
per_gene_css, whole_css = tm.compute_css(df, reference="my_reference.pkl")
|
|
100
|
+
fig, coords = tm.plot_umap(df, reference=reference, v_gene="TRBV6-4")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Choosing an embedding type
|
|
104
|
+
|
|
105
|
+
`embed_repertoire` accepts an `embedding_type` argument with two options:
|
|
106
|
+
|
|
107
|
+
| `embedding_type` | Dim | What it is | What it captures |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `"pretrained"` | 480 | The raw CLS embedding straight out of the masked-language-model-pretrained ESM2-style base encoder, before any contrastive fine-tuning. | **Local structure** — this encoder is trained to recover masked residues from local sequence context, so the embedding is most sensitive to motif/sub-sequence-level similarity between TCRs. |
|
|
110
|
+
| `"final"` (default) | 64 | The 480-dim base embedding run through the ensemble of 7 contrastively fine-tuned projection heads, GPA-aligned and mean-fused, L2-normalized. | **Overall structure** — contrastive fine-tuning pulls together TCRs recognizing the same antigen regardless of local sequence differences, so this embedding is most sensitive to antigen-specificity-level, global similarity. |
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Local-structure embedding
|
|
114
|
+
emb_pretrained = tm.embed_repertoire(df, embedding_type="pretrained")
|
|
115
|
+
|
|
116
|
+
# Overall-structure embedding (default; same as tm.embed_repertoire(df))
|
|
117
|
+
emb_final = tm.embed_repertoire(df, embedding_type="final")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This choice is only exposed on `embed_repertoire` itself, for users who
|
|
121
|
+
want the raw embeddings for their own downstream analysis.
|
|
122
|
+
`compute_css`, `plot_umap`, `energy_shift`, and `build_reference` always
|
|
123
|
+
embed internally with `embedding_type="final"` — TCRmeta's own
|
|
124
|
+
repertoire-level statistics (and the shipped reference map) are all
|
|
125
|
+
defined against that 64-dim antigen-aware embedding space, so these
|
|
126
|
+
functions ignore any `embedding_type` passed to them.
|
|
127
|
+
|
|
128
|
+
### Keeping intermediate embeddings
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
tm.embed_repertoire(df, save_path="my_repertoire_embeddings.pkl")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Pass the saved path back in via `embeddings=` (loaded with `pickle.load`)
|
|
135
|
+
to any downstream function to skip re-embedding, or just delete the file
|
|
136
|
+
if you don't need it.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
| Env var | Purpose |
|
|
141
|
+
|------------------------|-------------------------------------------------------|
|
|
142
|
+
| `TCRMETA_HF_REPO` | Hugging Face Hub repo id for weights/reference (placeholder until published) |
|
|
143
|
+
| `TCRMETA_WEIGHTS_DIR` | Load weights from a local directory instead of the Hub |
|
|
144
|
+
| `TCRMETA_CACHE_DIR` | Local cache directory (default `~/.cache/tcrmeta`) |
|
|
145
|
+
|
|
146
|
+
See `scripts/upload_weights.py` for a one-time script to publish your
|
|
147
|
+
model weights and default reference map to the Hugging Face Hub.
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pip install -e ".[dev]"
|
|
153
|
+
pytest
|
|
154
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/TCRmeta.egg-info/PKG-INFO
|
|
4
|
+
src/TCRmeta.egg-info/SOURCES.txt
|
|
5
|
+
src/TCRmeta.egg-info/dependency_links.txt
|
|
6
|
+
src/TCRmeta.egg-info/requires.txt
|
|
7
|
+
src/TCRmeta.egg-info/top_level.txt
|
|
8
|
+
src/tcrmeta/__init__.py
|
|
9
|
+
src/tcrmeta/_dataset.py
|
|
10
|
+
src/tcrmeta/_model.py
|
|
11
|
+
src/tcrmeta/_tokenizer.py
|
|
12
|
+
src/tcrmeta/_utils.py
|
|
13
|
+
src/tcrmeta/_weights.py
|
|
14
|
+
src/tcrmeta/css.py
|
|
15
|
+
src/tcrmeta/embedding.py
|
|
16
|
+
src/tcrmeta/energy.py
|
|
17
|
+
src/tcrmeta/reference.py
|
|
18
|
+
src/tcrmeta/umap_plot.py
|
|
19
|
+
tests/test_energy.py
|
|
20
|
+
tests/test_reference_and_css.py
|
|
21
|
+
tests/test_umap_plot.py
|
|
22
|
+
tests/test_utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tcrmeta
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""TCRmeta: antigen-aware TCR repertoire embeddings and downstream
|
|
2
|
+
repertoire-level analysis (CSS scoring, UMAP projection, energy-distance
|
|
3
|
+
shift), built on a pretrained-ESM2 + contrastive-fine-tuned encoder.
|
|
4
|
+
"""
|
|
5
|
+
from .embedding import embed_repertoire
|
|
6
|
+
from .reference import build_reference, load_reference, save_reference
|
|
7
|
+
from .css import compute_css, load_default_reference
|
|
8
|
+
from .umap_plot import plot_umap
|
|
9
|
+
from .energy import energy_shift
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"embed_repertoire",
|
|
13
|
+
"build_reference",
|
|
14
|
+
"save_reference",
|
|
15
|
+
"load_reference",
|
|
16
|
+
"load_default_reference",
|
|
17
|
+
"compute_css",
|
|
18
|
+
"plot_umap",
|
|
19
|
+
"energy_shift",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Fixed-length tokenization of (CDR1 + CDR2 + CDR2.5 + CDR3) TCR
|
|
2
|
+
sequences for the ESM2 base encoder.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Dict, List, Tuple
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import torch
|
|
10
|
+
from torch.utils.data import Dataset
|
|
11
|
+
|
|
12
|
+
DEFAULT_MAX_LEN = 48
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TCRDatasetFixedLen(Dataset):
|
|
16
|
+
def __init__(self, df: pd.DataFrame, token2idx: Dict[str, int], max_len: int = DEFAULT_MAX_LEN):
|
|
17
|
+
self.df = df.reset_index(drop=True)
|
|
18
|
+
self.t2i = token2idx
|
|
19
|
+
self.max_len = max_len
|
|
20
|
+
self.cls = token2idx["[CLS]"]
|
|
21
|
+
self.pad = token2idx["[PAD]"]
|
|
22
|
+
self.eos = token2idx["[EOS]"]
|
|
23
|
+
self.unk = token2idx["[UNK]"]
|
|
24
|
+
self.gap = token2idx["X"] # separator between CDR segments
|
|
25
|
+
|
|
26
|
+
def _encode_seq(self, s: str) -> List[int]:
|
|
27
|
+
return [self.t2i.get(a, self.unk) for a in s]
|
|
28
|
+
|
|
29
|
+
def __getitem__(self, i) -> Tuple[Tuple[str, str], List[int]]:
|
|
30
|
+
r = self.df.iloc[i]
|
|
31
|
+
v_seq = r.cdr1 + "X" + r.cdr2 + "X" + r.cdr2_5
|
|
32
|
+
tokens = (
|
|
33
|
+
[self.cls]
|
|
34
|
+
+ self._encode_seq(v_seq)
|
|
35
|
+
+ [self.gap]
|
|
36
|
+
+ self._encode_seq(r.cdr3aa)
|
|
37
|
+
+ [self.eos]
|
|
38
|
+
)
|
|
39
|
+
if len(tokens) < self.max_len:
|
|
40
|
+
tokens = tokens + [self.pad] * (self.max_len - len(tokens))
|
|
41
|
+
else:
|
|
42
|
+
tokens = tokens[: self.max_len]
|
|
43
|
+
key = (r.cdr3aa, r.v_gene)
|
|
44
|
+
return key, tokens
|
|
45
|
+
|
|
46
|
+
def __len__(self) -> int:
|
|
47
|
+
return len(self.df)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def fixedlen_collate(batch):
|
|
51
|
+
keys, seqs = zip(*batch)
|
|
52
|
+
input_ids = torch.tensor(seqs, dtype=torch.long)
|
|
53
|
+
return keys, input_ids
|