Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
^cran-comments\.md$
^doc$
^Meta$
^\.claude$
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.Rhistory
/doc/
/Meta/
.claude/
15 changes: 11 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: immLynx
Title: Linking Advanced TCR Python Pipelines and Hugging Face Models in R
Version: 1.1.2
Version: 1.3.1
Authors@R: c(
person(given = "Nick", family = "Borcherding", role = c("aut", "cre"),
email = "ncborch@gmail.com",
Expand All @@ -10,13 +10,16 @@ Description: A comprehensive toolkit that bridges popular Python-based immune
R environment. Provides unified interfaces for TCR distance calculations
(tcrdist3), sequence generation probability (OLGA), selection inference
(soNNia), clustering (clusTCR), protein embeddings (ESM-2), metaclone
discovery (metaclonotypist). Fully compatible with the scRepertoire and immApex ecosystem for single-cell
discovery (metaclonotypist), clonal expansion prediction from gene
expression (scXpand), near-neighbor search by symmetric deletion lookup
(pyrepseq), and deep generative featurization (DeepTCR). Fully compatible
with the scRepertoire and immApex ecosystem for single-cell
immune repertoire analysis.
License: MIT + file LICENSE
Encoding: UTF-8
RoxygenNote: 7.3.3
biocViews: Software, ImmunoOncology, SingleCell, Classification, Annotation,
Sequencing, MotifAnnotation, Clustering, DimensionReduction
Sequencing, MotifAnnotation, Clustering, DimensionReduction,
GeneExpression
Depends:
R (>= 4.5.0)
Imports:
Expand All @@ -28,15 +31,18 @@ Imports:
SingleCellExperiment,
stats,
SummarizedExperiment,
tools,
utils,
zellkonverter
Suggests:
AnnotationDbi,
BiocStyle,
data.table,
ggplot2,
knitr,
markdown,
Matrix,
org.Hs.eg.db,
rmarkdown,
scater,
scran,
Expand All @@ -51,3 +57,4 @@ URL: https://github.com/BorchLab/immLynx/
BugReports: https://github.com/BorchLab/immLynx/issues
StagedInstall: no
Config/testthat/edition: 3
Config/roxygen2/version: 8.0.0
9 changes: 9 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,41 @@ export(exportToScanpy)
export(extractTCRdata)
export(generateOLGA)
export(huggingModel)
export(listScXpandModels)
export(proteinEmbeddings)
export(runClustTCR)
export(runDeepTCR)
export(runEmbeddings)
export(runHLAassociation)
export(runMetaclonotypist)
export(runOLGA)
export(runScXpand)
export(runSoNNia)
export(runSymdelNeighbors)
export(runTCRdist)
export(summarizeTCRrepertoire)
export(tokenizeSequences)
export(validateTCRdata)
exportClasses(TCR_summary)
exportMethods(show)
importFrom(SummarizedExperiment,"colData<-")
importFrom(SummarizedExperiment,assay)
importFrom(SummarizedExperiment,assayNames)
importFrom(SummarizedExperiment,colData)
importFrom(SummarizedExperiment,rowData)
importFrom(immApex,getIR)
importFrom(methods,"slot<-")
importFrom(methods,is)
importFrom(methods,new)
importFrom(methods,setClass)
importFrom(methods,setMethod)
importFrom(methods,show)
importFrom(methods,slot)
importFrom(reticulate,array_reshape)
importFrom(reticulate,import)
importFrom(reticulate,py_to_r)
importFrom(stats,median)
importFrom(stats,sd)
importFrom(stats,setNames)
importFrom(utils,head)
importFrom(utils,write.csv)
53 changes: 53 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
# immLynx 1.3.1

* Added `runScXpand()`, which predicts T-cell clonal expansion from gene
expression alone using scXpand's pretrained pan-cancer models. Unlike the
other wrapped tools it needs no receptor sequences, so it works on
datasets with no paired TCR sequencing. Inference only; training and
hyperparameter optimization stay in Python.
* Added `listScXpandModels()` to enumerate the available pretrained models
without building the Python environment.
* Added a dedicated `scXpandEnv` basilisk environment (Python 3.11, CPU
PyTorch, scxpand 0.4.6). It cannot share the existing environments
because scxpand needs Python 3.11 and torch 2.5. The first call builds
several gigabytes and downloads the selected model from figshare.
* `runScXpand()` resolves gene identifiers to the Ensembl IDs scXpand's
models are indexed by, optionally mapping symbols through `org.Hs.eg.db`.
Ambiguous symbols (`HLA-DRA` alone maps to eight Ensembl IDs) and
collapsed duplicates are counted and reported rather than resolved
silently, because scXpand zero-fills genes it cannot find.
* When scRepertoire clone calls are present, `runScXpand()` derives
`clone_id_size`, `median_clone_size` and `expansion` per sample using
scXpand's 1.5x-median rule, so a gene-expression-only prediction can be
scored against the observed repertoire. Clone sizes are tabulated fresh
rather than read from `clonalFrequency`, which `combineExpression()`
computes under whatever grouping was in effect.
* Pretrained models are cached under `tools::R_user_dir("immLynx", "cache")`.
scXpand's own default would write a `.scxpand_cache` directory into the
current working directory, so `runScXpand()` downloads the model as an
explicit step with an explicit cache location.
* Worked around an upstream download bug: scXpand's registry points at
`figshare.com/ndownloader/articles/...`, which answers HTTP 202 with an
empty body, and the failed download is cached so retries keep failing.
The same archive on `ndownloader.figshare.com` serves correctly, so
`runScXpand()` rewrites the host. The rewrite becomes a no-op once
upstream fixes its URLs.

* Added `runSymdelNeighbors()` for near-neighbor CDR3 search by symmetric
deletion lookup, backed by `pyrepseq.nn.symdel` in the existing
`immLynxEnv`. Returns either a neighbor edge list or a per-cell neighbor
count. This closes the XT-neighbor request (#6) without a GPU dependency:
XT-neighbor is CUDA-only and its own documentation redirects users to the
CPU implementation of the same algorithm.

* Added `runDeepTCR()` for unsupervised VAE featurization of CDR3 sequences
via DeepTCR (#8), writing features to a dimensional reduction. Runs in a new
`deepTCREnv` basilisk environment. The dependency conflict that originally
blocked this was resolved upstream in DeepTCR 2.1.29.

* Fixed `scanpyExportEnv` declaring `anndata>=0.8`. basilisk passes the `pip`
vector through an unquoted shell, so `>=0.8` was parsed as a redirect: the
version floor was silently dropped and a stray file named `=0.8` was written
to the working directory. Now pinned to `anndata==0.11.4`, the version pip
already resolved to.

# immLynx 1.1.2

* Added `exportToScanpy()` to write a `SingleCellExperiment` or `Seurat`
Expand Down
90 changes: 89 additions & 1 deletion R/basilisk.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,97 @@ scanpyExportEnv <- basilisk::BasiliskEnvironment(
"h5py"
),
pip = c(
"anndata>=0.8",
# Pinned exactly rather than ">=0.8": basilisk hands the pip vector to
# system2(), which runs it through an unquoted shell, so ">=0.8" is
# parsed as a redirect to a file named "=0.8". That both dropped the
# version floor and littered the working directory. 0.11.4 is the
# version pip already resolved to under python=3.10.
"anndata==0.11.4",
"scanpy",
"muon",
"scirpy"
)
)

# Separate environment for scXpand (clonal expansion prediction from gene
# expression). Cannot share scanpyExportEnv (python 3.10) or immLynxEnv
# (python 3.9, torch 2.1.2, numpy 1.23): scxpand requires python >= 3.11
# and torch >= 2.5.
#
# pytorch-cpu is pulled from conda-forge rather than letting pip resolve
# `torch`, because the default PyPI torch wheel on linux-x86_64 bundles
# CUDA and runs to several gigabytes. basilisk's `pip` entries are bare
# specifiers, so there is no way to inject
# --index-url https://download.pytorch.org/whl/cpu. Installing the CPU
# build through conda first satisfies scxpand's torch>=2.5 requirement.
#
# Only scxpand itself is listed under `pip`; it resolves the rest of the
# stack (scanpy, anndata, scirpy, lightgbm, optuna, pooch, shap, ...).
# Enumerating those here would only create version-pin drift.
scXpandEnv <- basilisk::BasiliskEnvironment(
envname = "scXpandEnv",
pkgname = "immLynx",
packages = c(
"python=3.11",
"pytorch-cpu>=2.5",
"numpy",
"pandas",
"scipy",
"h5py"
),
pip = c(
"scxpand==0.4.6"
)
)

# Separate environment for DeepTCR. DeepTCR pins its whole scientific stack
# (numpy 1.23.5, pandas 1.5.3, scipy 1.10.1, TensorFlow 2.12) with "==", so it
# cannot share immLynxEnv.
#
# Three constraints drove this layout, each verified by building the
# environment and training a VAE:
#
# 1. python=3.10, not 3.11. DeepTCR pins biopython==1.76, which ships no
# wheel past cp38 and none for macOS arm64, so it compiles from source.
# Its C extension assigns to Py_TYPE(), which CPython 3.11 made a hard
# error. It compiles cleanly on 3.10.
#
# 2. The stack comes from conda rather than pip so that TensorFlow arrives
# without Apple's metal plugin. On macOS, DeepTCR's requirements pull
# tensorflow-metal==0.8.0, which is built against TF 2.11 and aborts the
# process at import under TF 2.12 with "platform is already registered
# with name: METAL". conda-forge's tensorflow has no such plugin.
#
# 3. biopython stays on pip. conda-forge's oldest osx-arm64 build is 1.78,
# which removed Bio.Alphabet, and DeepTCR still imports it.
#
# "--no-deps" therefore applies to the whole pip step: every dependency is
# already satisfied by conda, and it also keeps DeepTCR's jupyterlab and
# notebook requirements out of the environment.
deepTCREnv <- basilisk::BasiliskEnvironment(
envname = "deepTCREnv",
pkgname = "immLynx",
packages = c(
"python=3.10",
"tensorflow=2.12",
"numpy=1.23.5",
"pandas=1.5.3",
"scipy=1.10.1",
"h5py=3.8.0",
"scikit-learn=1.2.2",
"matplotlib-base=3.7.2",
"seaborn=0.12.2",
"umap-learn",
"networkx",
"tqdm",
"psutil"
),
pip = c(
"--no-deps",
"DeepTCR==2.1.29",
"biopython==1.76",
"logomaker==0.8",
"distinctipy==1.2.1",
"python-louvain==0.16"
)
)
Loading
Loading