From 0beef716080ca89e3d3c4b767bf5920573ddc1f6 Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 13:31:24 +0800 Subject: [PATCH 01/13] update readme --- README.md | 135 ++++++++++++++++++++---------------------------------- 1 file changed, 50 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index cdd9321a4..5ffec7e6a 100755 --- a/README.md +++ b/README.md @@ -233,23 +233,15 @@ pip install -e . ## Implemented Algorithms -**P1** not covered in the first release - ### Single Modality Module #### 1)Imputation -| BackBone | Model | Algorithm | Year | CheckIn | -| ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------ | ---- | ------- | -| GNN | GraphSCI | Imputing Single-cell RNA-seq data by combining Graph Convolution and Autoencoder Neural Networks | 2021 | ✅ | -| GNN | scGNN (2020) | SCGNN: scRNA-seq Dropout Imputation via Induced Hierarchical Cell Similarity Graph | 2020 | P1 | -| GNN | scGNN (2021) | scGNN is a novel graph neural network framework for single-cell RNA-Seq analyses | 2021 | ✅ | -| GNN | GNNImpute | An efficient scRNA-seq dropout imputation method using graph attention network | 2021 | P1 | -| Graph Diffusion | MAGIC | MAGIC: A diffusion-based imputation method reveals gene-gene interactions in single-cell RNA-sequencing data | 2018 | P1 | -| Probabilistic Model | scImpute | An accurate and robust imputation method scImpute for single-cell RNA-seq data | 2018 | P1 | -| GAN | scGAIN | scGAIN: Single Cell RNA-seq Data Imputation using Generative Adversarial Networks | 2019 | P1 | -| NN | DeepImpute | DeepImpute: an accurate, fast, and scalable deep neural network method to impute single-cell RNA-seq data | 2019 | ✅ | -| NN + TF | Saver-X | Transfer learning in single-cell transcriptomics improves data denoising and pattern discovery | 2019 | P1 | +| BackBone | Model | Algorithm | +| -------- | ------------ | --------------------------------------------------------------------------------------------------------- | +| GNN | GraphSCI | Imputing Single-cell RNA-seq data by combining Graph Convolution and Autoencoder Neural Networks | +| GNN | scGNN (2021) | scGNN is a novel graph neural network framework for single-cell RNA-Seq analyses | +| NN | DeepImpute | DeepImpute: an accurate, fast, and scalable deep neural network method to impute single-cell RNA-seq data | | Model | Mouse Brain (DANCE 2.0/DANCE1.0/Original) | Mouse Embryo (DANCE 2.0/DANCE1.0/Original) | PBMC (DANCE 2.0/DANCE1.0/Original) | Evaluation Metric | | ---------- | ----------------------------------------- | ------------------------------------------ | ---------------------------------- | ----------------- | @@ -263,15 +255,14 @@ pip install -e . #### 2)Cell Type Annotation -| BackBone | Model | Algorithm | Year | CheckIn | -| ----------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScDeepsort | Single-cell transcriptomics with weighted GNN | 2021 | ✅ | -| Logistic Regression | Celltypist | Cross-tissue immune cell analysis reveals tissue-specific features in humans. | 2021 | ✅ | -| Random Forest | singleCellNet | SingleCellNet: a computational tool to classify single cell RNA-Seq data across platforms and across species. | 2019 | ✅ | -| Neural Network | ACTINN | ACTINN: automated identification of cell types in single cell RNA sequencing. | 2020 | ✅ | -| Hierarchical Clustering | SingleR | Reference-based analysis of lung single-cell sequencing reveals a transitional profibrotic macrophage. | 2019 | P1 | -| SVM | SVM | A comparison of automatic cell identification methods for single-cell RNA sequencing data. | 2018 | ✅ | -| GNN | scHeteroNet | scHeteroNet: A Heterophily-Aware Graph Neural Network for Accurate Cell Type Annotation and Novel Cell Detection | 2025 | ✅ | +| BackBone | Model | Algorithm | +| ------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------- | +| GNN | ScDeepsort | Single-cell transcriptomics with weighted GNN | +| Logistic Regression | Celltypist | Cross-tissue immune cell analysis reveals tissue-specific features in humans. | +| Random Forest | singleCellNet | SingleCellNet: a computational tool to classify single cell RNA-Seq data across platforms and across species. | +| Neural Network | ACTINN | ACTINN: automated identification of cell types in single cell RNA sequencing. | +| SVM | SVM | A comparison of automatic cell identification methods for single-cell RNA sequencing data. | +| GNN | scHeteroNet | scHeteroNet: A Heterophily-Aware Graph Neural Network for Accurate Cell Type Annotation and Novel Cell Detection | | Model | GSE67835 Brain
(DANCE 2.0/DANCE 1.0/Original) | CD8+ TIL atlas
(DANCE 2.0/DANCE 1.0/Original) | GSE123813 Immune
(DANCE 2.0/DANCE 1.0/Original) | CD4+ TIL atlas
(DANCE 2.0/DANCE 1.0/Original) | GSE182320 (Tissue- Spleen)
(DANCE 2.0/DANCE 1.0/Original) | Evaluation Metric | | ------------- | ------------------------------------------------ | ------------------------------------------------ | -------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------ | ----------------- | @@ -286,15 +277,13 @@ pip install -e . #### 3)Clustering -| BackBone | Model | Algorithm | Year | CheckIn | -| ----------- | ------------- | ------------------------------------------------------------------------------------------------------------ | ---- | ------- | -| GNN | graph-sc | GNN-based embedding for clustering scRNA-seq data | 2022 | ✅ | -| GNN | scTAG | ZINB-based Graph Embedding Autoencoder for Single-cell RNA-seq Interpretations | 2022 | ✅ | -| GNN | scDSC | Deep structural clustering for single-cell RNA-seq data jointly through autoencoder and graph neural network | 2022 | ✅ | -| GNN | scGAC | scGAC: a graph attentional architecture for clustering single-cell RNA-seq data | 2022 | P1 | -| AutoEncoder | scDeepCluster | Clustering single-cell RNA-seq data with a model-based deep learning approach | 2019 | ✅ | -| AutoEncoder | scDCC | Model-based deep embedding for constrained clustering analysis of single cell RNA-seq data | 2021 | ✅ | -| AutoEncoder | scziDesk | Deep soft K-means clustering with self-training for single-cell RNA sequence data | 2020 | P1 | +| BackBone | Model | Algorithm | +| ----------- | ------------- | ------------------------------------------------------------------------------------------------------------ | +| GNN | graph-sc | GNN-based embedding for clustering scRNA-seq data | +| GNN | scTAG | ZINB-based Graph Embedding Autoencoder for Single-cell RNA-seq Interpretations | +| GNN | scDSC | Deep structural clustering for single-cell RNA-seq data jointly through autoencoder and graph neural network | +| AutoEncoder | scDeepCluster | Clustering single-cell RNA-seq data with a model-based deep learning approach | +| AutoEncoder | scDCC | Model-based deep embedding for constrained clustering analysis of single cell RNA-seq data | | Model | Worm Neuron (DANCE 2.0/DANCE 1.0/Original) | Mouse Bladder (DANCE 2.0/DANCE 1.0/Original) | 10X PBMC (DANCE 2.0/DANCE 1.0/Original) | Mouse ES (DANCE 2.0/DANCE 1.0/Original) | Evaluation Metric | | ------------- | ------------------------------------------ | -------------------------------------------- | --------------------------------------- | --------------------------------------- | ----------------- | @@ -310,14 +299,12 @@ pip install -e . #### 1)Modality Prediction -| BackBone | Model | Algorithm | Year | CheckIn | -| ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | 2022 | ✅ | -| GNN | ScMoLP | Link Prediction Variant of ScMoGCN | 2022 | P1 | -| GNN | GRAPE | Handling Missing Data with Graph Representation Learning | 2020 | P1 | -| Generative Model | SCMM | SCMM: MIXTURE-OF-EXPERTS MULTIMODAL DEEP GENERATIVE MODEL FOR SINGLE-CELL MULTIOMICS DATA ANALYSIS | 2021 | ✅ | -| Auto-encoder | Cross-modal autoencoders | Multi-domain translation between single-cell imaging and sequencing data using autoencoders | 2021 | ✅ | -| Auto-encoder | BABEL | BABEL enables cross-modality translation between multiomic profiles at single-cell resolution | 2021 | ✅ | +| BackBone | Model | Algorithm | +| ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------- | +| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | +| Generative Model | SCMM | SCMM: MIXTURE-OF-EXPERTS MULTIMODAL DEEP GENERATIVE MODEL FOR SINGLE-CELL MULTIOMICS DATA ANALYSIS | +| Auto-encoder | Cross-modal autoencoders | Multi-domain translation between single-cell imaging and sequencing data using autoencoders | +| Auto-encoder | BABEL | BABEL enables cross-modality translation between multiomic profiles at single-cell resolution | | Model | Evaluation Metric | GEX2ADT (DANCE 1.0/Original) | ADT2GEX (DANCE 1.0/Original) | GEX2ATAC (DANCE 1.0/Original) | ATAC2GEX (DANCE 1.0/Original) | | ------------------------ | ----------------- | ---------------------------- | ---------------------------- | ----------------------------- | ----------------------------- | @@ -328,12 +315,11 @@ pip install -e . #### 2) Modality Matching -| BackBone | Model | Algorithm | Year | CheckIn | -| ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | 2022 | ✅ | -| GNN/Auto-ecnoder | GLUE | Multi-omics single-cell data integration and regulatory inference with graph-linked embedding | 2021 | P1 | -| Generative Model | SCMM | SCMM: MIXTURE-OF-EXPERTS MULTIMODAL DEEP GENERATIVE MODEL FOR SINGLE-CELL MULTIOMICS DATA ANALYSIS | 2021 | ✅ | -| Auto-encoder | Cross-modal autoencoders | Multi-domain translation between single-cell imaging and sequencing data using autoencoders | 2021 | ✅ | +| BackBone | Model | Algorithm | +| ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------- | +| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | +| Generative Model | SCMM | SCMM: MIXTURE-OF-EXPERTS MULTIMODAL DEEP GENERATIVE MODEL FOR SINGLE-CELL MULTIOMICS DATA ANALYSIS | +| Auto-encoder | Cross-modal autoencoders | Multi-domain translation between single-cell imaging and sequencing data using autoencoders | | Model | Evaluation Metric | GEX2ADT (DANCE 1.0/Original) | GEX2ATAC (DANCE 1.0/Original) | | ------------------------ | ----------------- | ---------------------------- | ----------------------------- | @@ -343,13 +329,12 @@ pip install -e . #### 3) Joint Embedding -| BackBone | Model | Algorithm | Year | CheckIn | -| ---------------- | ------- | ----------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | 2022 | ✅ | -| Auto-encoder | scMVAE | Deep-joint-learning analysis model of single cell transcriptome and open chromatin accessibility data | 2020 | ✅ | -| Auto-encoder | scDEC | Simultaneous deep generative modelling and clustering of single-cell genomic data | 2021 | ✅ | -| GNN/Auto-ecnoder | GLUE | Multi-omics single-cell data integration and regulatory inference with graph-linked embedding | 2021 | P1 | -| Auto-encoder | DCCA | Deep cross-omics cycle attention model for joint analysis of single-cell multi-omics data | 2021 | ✅ | +| BackBone | Model | Algorithm | +| ------------ | ------- | ----------------------------------------------------------------------------------------------------- | +| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | +| Auto-encoder | scMVAE | Deep-joint-learning analysis model of single cell transcriptome and open chromatin accessibility data | +| Auto-encoder | scDEC | Simultaneous deep generative modelling and clustering of single-cell genomic data | +| Auto-encoder | DCCA | Deep cross-omics cycle attention model for joint analysis of single-cell multi-omics data | | Model | BRAIN ATAC2GEX (DANCE 2.0/DANCE 1.0/Original) | SKIN ATAC2GEX (DANCE 2.0/DANCE 1.0/Original) | OP 2022 Multi ATAC2GEX (DANCE 2.0/DANCE 1.0/Original) | Evaluation Metric | | ------- | --------------------------------------------- | -------------------------------------------- | ----------------------------------------------------- | ----------------- | @@ -360,38 +345,18 @@ pip install -e . **Note**: Stage 1, 2 and 3 (test dataset as metric for selection) for all methods. -#### 4) Multimodal Imputation - -| BackBone | Model | Algorithm | Year | CheckIn | -| -------- | ------ | -------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScMoLP | Link Prediction Variant of ScMoGCN | 2022 | P1 | -| GNN | scGNN | scGNN is a novel graph neural network framework for single-cell RNA-Seq analyses | 2021 | P1 | -| GNN | GRAPE | Handling Missing Data with Graph Representation Learning | 2020 | P1 | - -#### 5) Multimodal Integration - -| BackBone | Model | Algorithm | Year | CheckIn | -| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | ScMoGCN | Graph Neural Networks for Multimodal Single-Cell Data Integration | 2022 | P1 | -| GNN | scGNN | scGNN is a novel graph neural network framework for single-cell RNA-Seq analyses (GCN on Nearest Neighbor graph) | 2021 | P1 | -| Nearest Neighbor | WNN | Integrated analysis of multimodal single-cell data | 2021 | P1 | -| GAN | MAGAN | MAGAN: Aligning Biological Manifolds | 2018 | P1 | -| Auto-encoder | SCIM | SCIM: universal single-cell matching with unpaired feature sets | 2020 | P1 | -| Auto-encoder | MultiMAP | MultiMAP: Dimensionality Reduction and Integration of Multimodal Data | 2021 | P1 | -| Generative Model | SCMM | SCMM: MIXTURE-OF-EXPERTS MULTIMODAL DEEP GENERATIVE MODEL FOR SINGLE-CELL MULTIOMICS DATA ANALYSIS | 2021 | P1 | - ### Spatial Module #### 1)Spatial Domain | BackBone | Model | Algorithm | Year | CheckIn | | -------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | SpaGCN | SpaGCN: Integrating gene expression, spatial location and histology to identify spatial domains and spatially variable genes by graph convolutional network | 2021 | ✅ | -| GNN | STAGATE | Deciphering spatial domains from spatially resolved transcriptomics with adaptive graph attention auto-encoder | 2021 | ✅ | -| Bayesian | BayesSpace | Spatial transcriptomics at subspot resolution with BayesSpace | 2021 | P1 | -| Pseudo-space-time (PST) Distance | stLearn | stLearn: integrating spatial location, tissue morphology and gene expression to find cell types, cell-cell interactions and spatial trajectories within undissociated tissues | 2020 | ✅ | -| Heuristic | Louvain | Fast unfolding of community hierarchies in large networks | 2008 | ✅ | -| GNN | EfNST | A composite scaling network of EfficientNet for improving spatial domain identification performance | 2024 | ✅ | +| GNN | SpaGCN | SpaGCN: Integrating gene expression, spatial location and histology to identify spatial domains and spatially variable genes by graph convolutional network | | | +| GNN | STAGATE | Deciphering spatial domains from spatially resolved transcriptomics with adaptive graph attention auto-encoder | | | +| Bayesian | BayesSpace | Spatial transcriptomics at subspot resolution with BayesSpace | | | +| Pseudo-space-time (PST) Distance | stLearn | stLearn: integrating spatial location, tissue morphology and gene expression to find cell types, cell-cell interactions and spatial trajectories within undissociated tissues | | | +| Heuristic | Louvain | Fast unfolding of community hierarchies in large networks | | | +| GNN | EfNST | A composite scaling network of EfficientNet for improving spatial domain identification performance | | | | Model | 151676 (DANCE 2.0/DANCE 1.0/Original) | Sub MBA (DANCE 2.0/DANCE 1.0/Original) | Evaluation Metric | | ------- | ------------------------------------- | -------------------------------------- | ----------------- | @@ -405,13 +370,13 @@ pip install -e . #### 2)Cell Type Deconvolution -| BackBone | Model | Algorithm | Year | CheckIn | -| -------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- | ---- | ------- | -| GNN | DSTG | DSTG: deconvoluting spatial transcriptomics data through graph-based artificial intelligence | 2021 | ✅ | -| logNormReg | SpatialDecon | Advances in mixed cell deconvolution enable quantification of cell types in spatial transcriptomic data | 2022 | ✅ | -| NNMFreg | SPOTlight | SPOTlight: seeded NMF regression to deconvolute spatial transcriptomics spots with single-cell transcriptomes | 2021 | ✅ | -| NN Linear + CAR assumption | CARD | Spatially informed cell-type deconvolution for spatial transcriptomics | 2022 | ✅ | -| GNN | STdGCN | STdGCN: spatial transcriptomic cell-type deconvolution using graph convolutional networks | 2024 | ✅ | +| BackBone | Model | Algorithm | +| -------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- | +| GNN | DSTG | DSTG: deconvoluting spatial transcriptomics data through graph-based artificial intelligence | +| logNormReg | SpatialDecon | Advances in mixed cell deconvolution enable quantification of cell types in spatial transcriptomic data | +| NNMFreg | SPOTlight | SPOTlight: seeded NMF regression to deconvolute spatial transcriptomics spots with single-cell transcriptomes | +| NN Linear + CAR assumption | CARD | Spatially informed cell-type deconvolution for spatial transcriptomics | +| GNN | STdGCN | STdGCN: spatial transcriptomic cell-type deconvolution using graph convolutional networks | | Model | CARD Synthetic (DANCE 2.0/DANCE 1.0/Original) | SPOTlight Synthetic (DANCE 2.0/DANCE 1.0/Original) | Evaluation Metric | | ------------ | --------------------------------------------- | -------------------------------------------------- | ----------------- | From 2d778db7aed7287d88d7e75acc9eb408584f0c92 Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 14:53:19 +0800 Subject: [PATCH 02/13] update readme --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 364a812cb..905d47027 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: name: Sort imports args: [--line-width, '120', --profile, black] - repo: https://github.com/PyCQA/docformatter - rev: eb1df34 + rev: master hooks: - id: docformatter name: Format docstring From 1f6b7c61db05bb3151e507f070d61d08c191d688 Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:03:02 +0800 Subject: [PATCH 03/13] update readme --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 905d47027..364a812cb 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: name: Sort imports args: [--line-width, '120', --profile, black] - repo: https://github.com/PyCQA/docformatter - rev: master + rev: eb1df34 hooks: - id: docformatter name: Format docstring From 4c5e9b5bffd9c71cbfb5f353ddbfc123ed51670c Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:11:48 +0800 Subject: [PATCH 04/13] update readme --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 364a812cb..c9193c403 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,7 @@ repos: hooks: - id: docformatter name: Format docstring + language_version: python3.10 additional_dependencies: [tomli] args: [--config, pyproject.toml] - repo: https://github.com/executablebooks/mdformat From fbf5e71255d32bfa10f6fedc3135a06a7a2c293d Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:22:46 +0800 Subject: [PATCH 05/13] update readme --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c9193c403..7d30ff7b7 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,11 +23,13 @@ repos: rev: v3.16.0 hooks: - id: pyupgrade + language_version: python3.10 args: [--py3-plus] - repo: https://github.com/google/yapf rev: v0.40.2 hooks: - id: yapf + language_version: python3.10 name: Format code additional_dependencies: [toml] - repo: https://github.com/pycqa/isort From 742a1002da39b56a0e88370aabe43e8a9342e0c9 Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:33:43 +0800 Subject: [PATCH 06/13] update readme --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index c1dd8cd4a..69e414c6e 100755 --- a/setup.cfg +++ b/setup.cfg @@ -65,6 +65,7 @@ packages = find: [options.extras_require] dev = + setuptools pre-commit==3.6.1 pytest-subtests==0.11.0 pytest-xdist==3.6.1 From 0b6842ccb06dc5c2d057672d0020feb3d22d85fa Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:40:42 +0800 Subject: [PATCH 07/13] update readme --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index b8977156a..bb9c72b85 100755 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,7 @@ extras = dev commands = pip install -U pip + pip install setuptools # Install torch related dependencies with CUDA 11.8 gpu: pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 From d0f3b5c62d6b29ef795a41cf1ed585978ff57789 Mon Sep 17 00:00:00 2001 From: xzy Date: Thu, 19 Mar 2026 15:47:02 +0800 Subject: [PATCH 08/13] update readme --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c85cef489..3433e1c58 100755 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ tifffile==2024.2.12 torchnmf==0.3.5 tqdm==4.66.2 anndata==0.10.8 -wandb==0.16.3 +wandb>=0.17.0 uvicorn== 0.34.3 fastapi == 0.115.12 python-multipart==0.0.20 From 633b3e4d923ada73eeba13fd769ddabfc60af6be Mon Sep 17 00:00:00 2001 From: xzy Date: Fri, 17 Jul 2026 13:33:33 +0800 Subject: [PATCH 09/13] minor --- .../cta_scdeepsort/main.py | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py diff --git a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py new file mode 100644 index 000000000..80b08a4e7 --- /dev/null +++ b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py @@ -0,0 +1,262 @@ +import argparse +import gc +import os +import pickle +import pprint +import sys +from math import e +from pathlib import Path +from typing import get_args + +import numpy as np +import torch +from sympy import elliptic_k + +import wandb +from dance import logger +from dance.datasets.singlemodality import CellTypeAnnotationDataset +from dance.modules.single_modality.cell_type_annotation.scdeepsort import ScDeepSort +from dance.pipeline import PipelinePlaner, get_step3_yaml, run_step3, save_summary_data +from dance.transforms import FeatureCellPlaceHolder, FilterGenesTopK, WeightedFeatureSVD +from dance.transforms.cell_feature import WeightedFeaturePCA +from dance.transforms.filter import ( + FilterGenesNumberPlaceHolder, + FilterGenesPlaceHolder, + HighlyVariableGenesLogarithmizedByTopGenes, + HighlyVariableGenesRawCount, +) +from dance.transforms.graph.cell_feature_graph import CellFeatureGraph +from dance.transforms.misc import Compose, SetConfig +from dance.transforms.normalize import ( + ColumnSumNormalize, + Log1P, + NormalizePlaceHolder, + NormalizeTotal, + NormalizeTotalLog1P, + ScTransform, +) +from dance.typing import LogLevel +from dance.utils import set_seed + + +def build_transform_pipeline(pipeline_name, num_genes=2208): + transforms = [] + pipeline_parts = pipeline_name.split("_") + for i, pipline_part in enumerate(pipeline_parts): + if pipline_part in {"FGPH", "FPGH"}: + transforms.append(FilterGenesPlaceHolder()) + elif pipline_part == "NP": + transforms.append(NormalizePlaceHolder()) + elif pipline_part == "Log": + transforms.append(Log1P()) + elif pipline_part == "ST": + transforms.append(ScTransform(processes_num=8)) + elif pipline_part == "NT": + transforms.append(NormalizeTotal()) + elif pipline_part == "FK": + transforms.append(FilterGenesTopK(num_genes=num_genes)) + elif pipline_part == "SF": + transforms.append(ColumnSumNormalize()) + elif pipline_part == "FGNP": + transforms.append(FilterGenesNumberPlaceHolder()) + elif pipline_part == "HvgRC": + transforms.append(HighlyVariableGenesRawCount(n_top_genes=num_genes)) + elif pipline_part == "HvgLogTop": + transforms.append(HighlyVariableGenesLogarithmizedByTopGenes(n_top_genes=num_genes)) + elif pipline_part == "NTLP": + transforms.append(NormalizeTotalLog1P()) + elif pipline_part == "WPCA": + transforms.append( + WeightedFeaturePCA(out="feature.cell", log_level="INFO", save_info=True, split_name="train")) + elif pipline_part == "WSVD": + transforms.append( + WeightedFeatureSVD(out="feature.cell", log_level="INFO", save_info=True, split_name="train")) + elif pipline_part == "FP": + transforms.append(FeatureCellPlaceHolder(out="feature.cell")) + transforms.append(CellFeatureGraph(cell_feature_channel="feature.cell")) + + transforms.append(SetConfig({"label_channel": "cell_type"})) + return transforms + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--batch_size", type=int, default=500) + parser.add_argument("--cache", action="store_true", help="Cache processed data.") + # parser.add_argument("--dense_dim", type=int, default=400, help="number of hidden gcn units") + parser.add_argument("--device", type=str, default="cpu", help="Computation device") + parser.add_argument("--dropout", type=float, default=0.1, help="dropout probability") + parser.add_argument("--hidden_dim", type=int, default=200, help="number of hidden gcn units") + parser.add_argument("--log_level", type=str, default="INFO", choices=get_args(LogLevel)) + parser.add_argument("--lr", type=float, default=1e-3, help="learning rate") + parser.add_argument("--n_epochs", type=int, default=100, help="number of training epochs") + parser.add_argument("--n_layers", type=int, default=1, help="number of hidden gcn layers") + parser.add_argument("--species", default="mouse", type=str) + parser.add_argument("--test_dataset", nargs="+", type=int, default=[], help="Testing dataset IDs") + parser.add_argument("--test_rate", type=float, default=0.2) + parser.add_argument("--tissue", default="Spleen", type=str) + parser.add_argument("--train_dataset", nargs="+", default=[1970], help="List of training dataset ids.") + parser.add_argument("--valid_dataset", nargs="+", default=None, help="List of valid dataset ids.") + parser.add_argument("--val_size", type=float, default=0.2, help="Validation split size in data loader.") + parser.add_argument("--weight_decay", type=float, default=5e-4, help="Weight for L2 loss") + parser.add_argument("--seed", type=int, default=42) + + parser.add_argument("--exp_num", type=int, default=1000) + parser.add_argument("--root_path", default="/home/common/zyxing/dance/result/cta_scdeepsort", type=str) + parser.add_argument("--filetype", default="csv") + parser.add_argument("--pipeline_name", type=str, default="") + parser.add_argument("--num_genes", type=int, default=2208) + args = parser.parse_args() + logger.setLevel(args.log_level) + os.environ["WANDB_AGENT_MAX_INITIAL_FAILURES"] = "2000" + logger.info(f"Running ScDeepSort with the following parameters:\n{pprint.pformat(vars(args))}") + file_root_path = Path( + args.root_path, "_".join([ + "-".join([str(num) for num in dataset]) + for dataset in [args.train_dataset, args.valid_dataset, args.test_dataset] + if (dataset is not None and dataset != []) + ])).resolve() + logger.info(f"\n files is saved in {file_root_path}") + + set_seed(args.seed) + + # Load data and perform necessary preprocessing + data = CellTypeAnnotationDataset(species=args.species, tissue=args.tissue, test_dataset=args.test_dataset, + train_dataset=args.train_dataset, valid_dataset=args.valid_dataset, + data_dir="../temp_data", val_size=args.val_size, + filetype=args.filetype).load_data() + if args.pipeline_name == "origin": + preprocessing_pipeline = ScDeepSort.preprocessing_pipeline(normalize=True) + else: + # Prepare preprocessing pipeline and apply it to data + transforms = build_transform_pipeline(args.pipeline_name, num_genes=args.num_genes) + preprocessing_pipeline = Compose(*transforms, log_level="INFO") + history = preprocessing_pipeline.transform_with_history(data) + + # Obtain training and testing data + y_train = data.get_y(split_name="train", return_type="torch") + y_valid = data.get_y(split_name="val", return_type="torch") if data.val_idx is not None else None + y_test = data.get_y(split_name="test", return_type="torch") + + # Get cell feature graph for scDeepSort + # TODO: make api for the following block? + g = data.data.uns["CellFeatureGraph"] + num_genes = data.shape[1] + # Initialize model and get model specific preprocessing pipeline + dense_dim = g.ndata["features"].shape[1] + model = ScDeepSort(dense_dim, args.hidden_dim, args.n_layers, args.species, args.tissue, dropout=args.dropout, + batch_size=args.batch_size, device=args.device) + + gene_ids = torch.arange(num_genes) + train_cell_ids = torch.LongTensor(data.train_idx) + num_genes + test_cell_ids = torch.LongTensor(data.test_idx) + num_genes + g_train = g.subgraph(torch.concat((gene_ids, train_cell_ids))) + if data.val_idx is not None: + valid_cell_ids = torch.LongTensor(data.val_idx) + num_genes + g_valid = g.subgraph(torch.concat((gene_ids, valid_cell_ids))) + else: + g_valid = None + g_test = g.subgraph(torch.concat((gene_ids, test_cell_ids))) + + # Train and evaluate models for several rounds + def print_distribution(y_input, class_names=None): + """通用分布打印函数 支持: + + 1. One-hot格式 (Shape: [N, C]) -> 使用 sum(dim=0) + 2. Index格式 (Shape: [N]) -> 使用 bincount + + """ + # 0. 预处理:确保在 CPU 上并转为 long 类型 (bincount 需要 long) + if y_input.is_cuda: + y_input = y_input.cpu() + + # 1. 核心逻辑:自动判断输入类型并统计 + if y_input.ndim == 2: + # One-hot 矩阵: 直接按列求和 + counts = y_input.sum(dim=0).long() + else: + # Index 向量: 使用 bincount 统计每个数字出现的次数 + # minlength 确保即使某些类别没出现,长度也能和 class_names 对齐 + num_classes = len(class_names) if class_names else (y_input.max() + 1).item() + counts = torch.bincount(y_input.long(), minlength=num_classes) + + # 计算总数 + total = counts.sum().item() + + # 2. 打印表头 + print(f"{'ID':<5} {'Name':<20} {'Count':<8} {'Percentage'}") + print("-" * 50) + + # 3. 循环输出 + for i, count in enumerate(counts): + # 如果提供了名字列表,使用名字;否则用默认 ID + # 防止名字列表比实际类别少导致越界 + if class_names and i < len(class_names): + name = class_names[i] + else: + name = f"Class {i}" + + pct = count.item() / total if total > 0 else 0 + + # 只打印存在或者列表里包含的类别 + # (如果你想隐藏数量为0的类别,可以在这里加 if count > 0: ) + print(f"{i:<5} {name:<20} {count.item():<8} {pct:.2%}") + + # Train and evaluate the model + model.fit(g_train, y_train.argmax(1), epochs=args.n_epochs, lr=args.lr, weight_decay=args.weight_decay, + val_ratio=args.test_rate) + train_score = model.score(g_train, y_train) + score = model.score(g_valid, y_valid) if g_valid is not None else None + test_score = model.score(g_test, y_test) + # with open(f"{file_root_path}/data{'_'+args.pipeline_name}.pkl", "wb") as f: #base value is equal to expected value + # pickle.dump(history, f) + pred_prob = model.predict_proba(g_test) + pred_test = pred_prob.argmax(1) + true_test = y_test.argmax(1).detach().cpu().numpy() + label_names = np.asarray(list(data.data.obsm["cell_type"].columns), dtype=object) + test_obs_names = np.asarray(data.data.obs_names[data.test_idx], dtype=object) + output_prefix = f"{args.pipeline_name}_{args.tissue.lower()}".strip("_") + np.savez_compressed( + Path(__file__).resolve().parent / f"{output_prefix}_test_predictions.npz", + predictions=pred_test, + probabilities=pred_prob, + true_labels=true_test, + pred_label_names=label_names[pred_test], + true_label_names=label_names[true_test], + label_names=label_names, + obs_names=test_obs_names, + ) + print_distribution(torch.tensor(pred_test)) + print(f"Train accuracy: {train_score:.4f}") + if score is not None: + print(f"Validation accuracy: {score:.4f}") + print(f"Test accuracy: {test_score:.4f}") +"""To reproduce the benchmarking results, please run the following command: + +Mouse Brain +$ python scdeepsort.py --species mouse --tissue Brain --train_dataset 753 3285 --test_dataset 2695 + +Mouse Spleen +$ python scdeepsort.py --species mouse --tissue Spleen --train_dataset 1970 --test_dataset 1759 + +Mouse Kidney +$ python scdeepsort.py --species mouse --tissue Kidney --train_dataset 4682 --test_dataset 203 + +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:0 --pipeline_name FGPH_NP_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:1 --pipeline_name FGPH_NP_HvgRC_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:0 --pipeline_name FGPH_NP_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:2 --pipeline_name FGPH_Log_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:3 --pipeline_name FGPH_ST_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:4 --pipeline_name FGPH_NT_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:5 --pipeline_name FGPH_SF_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:5 --pipeline_name FGPH_NTLP_FGNP_WPCA +python main.py --species human --tissue Brain --train_dataset 328 --test_dataset 138 --device cuda:5 --pipeline_name FGPH_NTLP_HvgLogTop_WPCA + +python main.py --species human --tissue CD8 --train_dataset 1027 1357 1641 517 706 777 850 972 --test_dataset 245 332 377 398 405 455 470 492 --pipeline_name Log_WPCA --device cuda:0 +python main.py --species human --tissue CD8 --train_dataset 1027 1357 1641 517 706 777 850 972 --test_dataset 245 332 377 398 405 455 470 492 --pipeline_name FPGH_Log_HvgRC_WSVD --device cuda:0 + + +python main.py --species human --tissue CD4 --train_dataset 1013 1247 598 732 767 768 770 784 845 864 --test_dataset 315 340 376 381 390 404 437 490 551 559 --pipeline_name WPCA --device cuda:1 +python main.py --species human --tissue CD4 --train_dataset 1013 1247 598 732 767 768 770 784 845 864 --test_dataset 315 340 376 381 390 404 437 490 551 559 --pipeline_name WSVD --device cuda:1 +""" +"""效果比较好的这些结果流程基本都会有所欠缺,其实人为构造一下,原理也是一样的.""" From 2aa57c7aa2a9e009aa01b7dbb112f8e357e735e8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 05:35:51 +0000 Subject: [PATCH 10/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../result_analysis/celltype_annotation/cta_scdeepsort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py index 80b08a4e7..1baeac3b4 100644 --- a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py +++ b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py @@ -10,9 +10,9 @@ import numpy as np import torch +import wandb from sympy import elliptic_k -import wandb from dance import logger from dance.datasets.singlemodality import CellTypeAnnotationDataset from dance.modules.single_modality.cell_type_annotation.scdeepsort import ScDeepSort From 14e50ed023e96d1dd3ef5e421871961d7b0cf928 Mon Sep 17 00:00:00 2001 From: xzy Date: Fri, 17 Jul 2026 15:51:04 +0800 Subject: [PATCH 11/13] minor --- .../cell_type_annotation/scdeepsort.py | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/dance/modules/single_modality/cell_type_annotation/scdeepsort.py b/dance/modules/single_modality/cell_type_annotation/scdeepsort.py index fa1c3bdc1..0aa18d665 100755 --- a/dance/modules/single_modality/cell_type_annotation/scdeepsort.py +++ b/dance/modules/single_modality/cell_type_annotation/scdeepsort.py @@ -12,13 +12,14 @@ from pathlib import Path import dgl +import scanpy as sc import torch import torch.nn as nn from dgl.dataloading import DataLoader, NeighborSampler from dance.models.nn import AdaptiveSAGE from dance.modules.base import BaseClassificationMethod -from dance.transforms import Compose, SetConfig +from dance.transforms import AnnDataTransform, Compose, SetConfig from dance.transforms.graph import PCACellFeatureGraph from dance.typing import LogLevel, Optional @@ -132,12 +133,31 @@ def __init__(self, dim_in: int, dim_hid: int, num_layers: int, species: str, tis self.save_path.mkdir(parents=True) @staticmethod - def preprocessing_pipeline(n_components: int = 400, log_level: LogLevel = "INFO"): - return Compose( + def preprocessing_pipeline(n_components: int = 400, normalize: bool = False, log_level: LogLevel = "INFO"): + """Create the preprocessing pipeline for scDeepSort. + + Parameters + ---------- + n_components + Number of PCA components used to initialize cell and gene features. + normalize + Whether to apply the Scanpy equivalent of Seurat ``NormalizeData`` before graph construction. + log_level + Logging level for the composed preprocessing pipeline. + + """ + transforms = [] + + if normalize: + transforms.append(AnnDataTransform(sc.pp.normalize_total, target_sum=1e4)) + transforms.append(AnnDataTransform(sc.pp.log1p)) + + transforms.extend([ PCACellFeatureGraph(n_components=n_components, split_name="train"), SetConfig({"label_channel": "cell_type"}), - log_level=log_level, - ) + ]) + + return Compose(*transforms, log_level=log_level) def fit(self, graph: dgl.DGLGraph, labels: torch.Tensor, epochs: int = 300, lr: float = 1e-3, weight_decay: float = 0, val_ratio: float = 0.2): From 78bcd4bc22ef92bc098674e7e4e1e72313608908 Mon Sep 17 00:00:00 2001 From: xzy Date: Fri, 24 Jul 2026 16:31:01 +0800 Subject: [PATCH 12/13] minor --- .gitignore | 4 + dance/datasets/multimodality.py | 236 ++++++++++++++++++ .../cell_type_annotation/celltypist.py | 24 +- dance/transforms/graph/cell_feature_graph.py | 2 +- .../cta_scdeepsort/main.py | 28 ++- .../cell_type_annotation/singlecellnet.py | 3 +- 6 files changed, 290 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index e45459b4a..5a3f715b7 100755 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,7 @@ examples/multi_modality/joint_embedding/models/*.pth !examples/result_analysis/get_important_pattern.py !examples/result_analysis/get_num.py + # ============================================================================= # Model files # ============================================================================= @@ -159,6 +160,9 @@ models/model_joint_embedding_1.pth examples/tuning/joint_embedding_scmogcn/models/model_joint_embedding_1.pth examples/multi_modality/joint_embedding/models/*.pth + +!examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py + # ============================================================================= # Others # ============================================================================= diff --git a/dance/datasets/multimodality.py b/dance/datasets/multimodality.py index 87c8689cc..ffd3ff09a 100755 --- a/dance/datasets/multimodality.py +++ b/dance/datasets/multimodality.py @@ -16,6 +16,27 @@ from dance.data import Data from dance.datasets.base import BaseDataset from dance.registry import register_dataset +from dance.transforms import ( + AlignMod, + ColumnSumNormalize, + Compose, + FeatureCellPlaceHolder, + FilterCellsCommonMod, + FilterCellsPlaceHolder, + FilterCellsScanpyOrder, + FilterGenesPercentile, + FilterGenesPlaceHolder, + FilterGenesRegression, + FilterGenesScanpyOrder, + FilterGenesTopK, + GaussRandProjFeature, + HighlyVariableGenesRawCount, + NormalizePlaceHolder, + NormalizeTotal, + NormalizeTotalLog1P, + SetConfig, + tfidfTransform, +) from dance.transforms.preprocess import lsiTransformer from dance.typing import List from dance.utils import is_numeric @@ -776,6 +797,221 @@ def _maybe_preprocess(self, raw_data): meta_common_cells = list(set(meta1.obs.index) & set(meta2.obs.index)) meta1 = meta1[meta_common_cells, :] meta2 = meta2[meta_common_cells, :] + elif self.preprocess == "dcca_optim": + data = Data(md.MuData({ + "mod1": mod1, + "mod2": mod2, + "meta1": meta1, + "meta2": meta2, + "test_sol": test_sol, + }), train_size=train_size) + + preprocessing_pipeline = Compose( + # pipeline.0.filter.gene: mod1 + FilterGenesPlaceHolder(mod="mod1"), + + # pipeline.1.filter.gene: mod2 + FilterGenesPercentile(mod="mod2"), + + # pipeline.2.misc + AlignMod(), + + # pipeline.3.normalize: mod1, candidate group [NormalizePlaceHolder, tfidfTransform] + NormalizePlaceHolder(mod="mod1"), + + # pipeline.4.normalize: mod2, candidate group [NormalizePlaceHolder, tfidfTransform] + NormalizePlaceHolder(mod="mod2"), + + # pipeline.5.normalize: mod1 + NormalizePlaceHolder(mod="mod1"), + + # pipeline.6.normalize: mod2 + NormalizeTotalLog1P(mod="mod2"), + + # pipeline.7.filter.gene: mod1, channel=counts/layers + FilterGenesRegression( + mod="mod1", + channel="counts", + channel_type="layers", + num_genes=self.selection_threshold, + ), + + # pipeline.8.filter.gene: mod2, channel=counts/layers + FilterGenesRegression( + mod="mod2", + channel="counts", + channel_type="layers", + num_genes=self.selection_threshold, + ), + + # pipeline.9.filter.cell: mod1 + FilterCellsPlaceHolder(mod="mod1"), + + # # pipeline.10.filter.cell: mod2 + FilterCellsPlaceHolder(mod="mod2"), + # FilterCellsScanpyOrder( + # mod="mod2", + # order=["min_counts", "min_genes", "max_counts", "max_genes"], + # min_counts=0.01, + # max_counts=0.99, + # min_genes=0.01, + # max_genes=0.99, + # ), + + # pipeline.11.filter.cell + FilterCellsCommonMod(mod1="mod1", mod2="mod2", sol="test_sol"), + + # pipeline.12.filter.cell: meta1 + FilterCellsPlaceHolder(mod="meta1"), + + # pipeline.13.filter.cell: meta2 + FilterCellsPlaceHolder(mod="meta2"), + + # pipeline.14.filter.cell + FilterCellsCommonMod(mod1="meta1", mod2="meta2"), + + # pipeline.15.feature.cell: mod1 + FeatureCellPlaceHolder(out="feature.cell", log_level="INFO", mod="mod1"), + + # pipeline.16.feature.cell: mod2 + FeatureCellPlaceHolder(out="feature.cell", log_level="INFO", mod="mod2"), + + # pipeline.17.misc + SetConfig({ + "feature_mod": ["mod1", "mod2", "mod1", "mod2", "mod1", "mod2"], + "label_mod": + "mod1", + "feature_channel_type": ["obsm", "obsm", "layers", "layers", "obsm", "obsm"], + "feature_channel": + ["feature.cell", "feature.cell", "counts", "counts", "size_factors", "size_factors"], + "label_channel": + "labels", + }), + ) + preprocessing_pipeline(data) + + mod1 = data.data.mod["mod1"] + mod2 = data.data.mod["mod2"] + meta1 = data.data.mod["meta1"] + meta2 = data.data.mod["meta2"] + test_sol = data.data.mod["test_sol"] + elif self.preprocess == "dcca_optim_v2": + data = Data(md.MuData({ + "mod1": mod1, + "mod2": mod2, + "meta1": meta1, + "meta2": meta2, + "test_sol": test_sol, + }), train_size=train_size) + + preprocessing_pipeline = Compose( + # pipeline.0.filter.gene: mod1 + FilterGenesScanpyOrder( + mod="mod1", + order=["min_counts", "max_counts", "min_cells", "max_cells"], + min_counts=0.01, + max_counts=0.99, + min_cells=0.01, + max_cells=0.99, + ), + + # pipeline.1.filter.gene: mod2 + FilterGenesScanpyOrder( + mod="mod2", + order=["min_counts", "max_counts", "min_cells", "max_cells"], + min_counts=0.01, + max_counts=0.99, + min_cells=0.01, + max_cells=0.99, + ), + + # pipeline.2.misc + AlignMod(), + + # pipeline.3.normalize: mod1 + tfidfTransform(mod="mod1"), + + # pipeline.4.normalize: mod2 + NormalizePlaceHolder(mod="mod2"), + + # pipeline.5.normalize: mod1 + ColumnSumNormalize(mod="mod1"), # pyright: ignore[reportUndefinedVariable] + + # pipeline.6.normalize: mod2 + NormalizeTotal(mod="mod2"), + + # pipeline.7.filter.gene: mod1 + FilterGenesTopK( + mod="mod1", + channel="counts", + channel_type="layers", + num_genes=self.selection_threshold, + ), + + # pipeline.8.filter.gene: mod2 + HighlyVariableGenesRawCount( + mod="mod2", + channel="counts", + channel_type="layers", + n_top_genes=self.selection_threshold, + ), + + # pipeline.9.filter.cell: mod1 + FilterCellsPlaceHolder(mod="mod1"), + + # pipeline.10.filter.cell: mod2 + FilterCellsPlaceHolder(mod="mod2"), + + # pipeline.11.filter.cell + FilterCellsCommonMod(mod1="mod1", mod2="mod2", sol="test_sol"), + + # pipeline.12.filter.cell: meta1 + FilterCellsPlaceHolder(mod="meta1"), + + # pipeline.13.filter.cell: meta2 + FilterCellsScanpyOrder( + mod="meta2", + order=["min_counts", "min_genes", "max_counts", "max_genes"], + min_counts=0.01, + max_counts=0.99, + min_genes=0.01, + max_genes=0.99, + ), + + # pipeline.14.filter.cell + FilterCellsCommonMod(mod1="meta1", mod2="meta2"), + + # pipeline.15.feature.cell: mod1 + GaussRandProjFeature( + mod="mod1", + out="feature.cell", + log_level="INFO", + n_components=400, + ), + + # pipeline.16.feature.cell: mod2 + FeatureCellPlaceHolder(out="feature.cell", log_level="INFO", mod="mod2"), + + # pipeline.17.misc + SetConfig({ + "feature_mod": ["mod1", "mod2", "mod1", "mod2", "mod1", "mod2"], + "label_mod": + "mod1", + "feature_channel_type": ["obsm", "obsm", "layers", "layers", "obsm", "obsm"], + "feature_channel": + ["feature.cell", "feature.cell", "counts", "counts", "size_factors", "size_factors"], + "label_channel": + "labels", + }), + ) + preprocessing_pipeline(data) + + mod1 = data.data.mod["mod1"] + mod2 = data.data.mod["mod2"] + meta1 = data.data.mod["meta1"] + meta2 = data.data.mod["meta2"] + test_sol = data.data.mod["test_sol"] + else: logger.info(f"Preprocessing method {self.preprocess!r} not supported.") diff --git a/dance/modules/single_modality/cell_type_annotation/celltypist.py b/dance/modules/single_modality/cell_type_annotation/celltypist.py index 4005ac41b..78fe6fbf1 100755 --- a/dance/modules/single_modality/cell_type_annotation/celltypist.py +++ b/dance/modules/single_modality/cell_type_annotation/celltypist.py @@ -11,7 +11,7 @@ from dance import logger from dance.modules.base import BaseClassificationMethod -from dance.transforms import SetConfig +from dance.transforms import AnnDataTransform, Compose, SetConfig from dance.typing import LogLevel, Optional, Union @@ -549,8 +549,26 @@ def __init__(self, majority_voting: bool = False, clf=None, scaler=None, descrip self.description = description @staticmethod - def preprocessing_pipeline(log_level: LogLevel = "INFO"): - return SetConfig({"label_channel": "cell_type"}, log_level=log_level) + def preprocessing_pipeline(normalize: bool = False, log_level: LogLevel = "INFO"): + """Create the preprocessing pipeline for CellTypist. + + Parameters + ---------- + normalize + Whether to transform ``.X`` to log1p-normalized expression at 10,000 counts per cell. + log_level + Logging level for the composed preprocessing pipeline. + + """ + transforms = [] + + if normalize: + transforms.append(AnnDataTransform(sc.pp.normalize_total, target_sum=1e4)) + transforms.append(AnnDataTransform(sc.pp.log1p)) + + transforms.append(SetConfig({"label_channel": "cell_type"})) + + return Compose(*transforms, log_level=log_level) def fit(self, indata: np.array, labels: Optional[Union[str, list, tuple, np.ndarray, pd.Series, pd.Index]] = None, C: float = 1.0, solver: Optional[str] = None, max_iter: int = 1000, n_jobs: Optional[int] = None, diff --git a/dance/transforms/graph/cell_feature_graph.py b/dance/transforms/graph/cell_feature_graph.py index f6a3343e0..2cb2f4c75 100755 --- a/dance/transforms/graph/cell_feature_graph.py +++ b/dance/transforms/graph/cell_feature_graph.py @@ -50,7 +50,7 @@ def __call__(self, data): edata = torch.FloatTensor(edata) # Initialize cell-gene graph - g = dgl.graph((row, col)) + g = dgl.graph((row, col), num_nodes=num_feats + num_cells) g.edata["weight"] = edata # FIX: change to feat_id g.ndata["cell_id"] = torch.concat((torch.arange(num_feats, dtype=torch.int32), diff --git a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py index 1baeac3b4..e14b3f8fe 100644 --- a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py +++ b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py @@ -10,9 +10,9 @@ import numpy as np import torch -import wandb from sympy import elliptic_k +import wandb from dance import logger from dance.datasets.singlemodality import CellTypeAnnotationDataset from dance.modules.single_modality.cell_type_annotation.scdeepsort import ScDeepSort @@ -22,6 +22,7 @@ from dance.transforms.filter import ( FilterGenesNumberPlaceHolder, FilterGenesPlaceHolder, + FilterGenesScanpyOrder, HighlyVariableGenesLogarithmizedByTopGenes, HighlyVariableGenesRawCount, ) @@ -41,6 +42,28 @@ def build_transform_pipeline(pipeline_name, num_genes=2208): transforms = [] + if pipeline_name == "atlas_scdeepsort_pancreas_53d208b0_scale_rawcount_svd": + transforms.append( + FilterGenesScanpyOrder( + order=["min_counts", "max_counts", "max_cells", "min_cells"], + min_counts=191, + min_cells=0.03798547080750556, + max_counts=0.9377398883656286, + max_cells=0.964294533892298, + )) + transforms.append(ColumnSumNormalize(mode="l2", eps=0.7)) + transforms.append(HighlyVariableGenesRawCount(n_top_genes=5718, span=0.39461811670516034)) + transforms.append( + WeightedFeatureSVD( + n_components=591, + out="feature.cell", + log_level="INFO", + feat_norm_mode="l2", + )) + transforms.append(CellFeatureGraph(cell_feature_channel="feature.cell")) + transforms.append(SetConfig({"label_channel": "cell_type"})) + return transforms + pipeline_parts = pipeline_name.split("_") for i, pipline_part in enumerate(pipeline_parts): if pipline_part in {"FGPH", "FPGH"}: @@ -103,6 +126,7 @@ def build_transform_pipeline(pipeline_name, num_genes=2208): parser.add_argument("--exp_num", type=int, default=1000) parser.add_argument("--root_path", default="/home/common/zyxing/dance/result/cta_scdeepsort", type=str) + parser.add_argument("--data_dir", default="../temp_data", type=str) parser.add_argument("--filetype", default="csv") parser.add_argument("--pipeline_name", type=str, default="") parser.add_argument("--num_genes", type=int, default=2208) @@ -123,7 +147,7 @@ def build_transform_pipeline(pipeline_name, num_genes=2208): # Load data and perform necessary preprocessing data = CellTypeAnnotationDataset(species=args.species, tissue=args.tissue, test_dataset=args.test_dataset, train_dataset=args.train_dataset, valid_dataset=args.valid_dataset, - data_dir="../temp_data", val_size=args.val_size, + data_dir=args.data_dir, val_size=args.val_size, filetype=args.filetype).load_data() if args.pipeline_name == "origin": preprocessing_pipeline = ScDeepSort.preprocessing_pipeline(normalize=True) diff --git a/examples/single_modality/cell_type_annotation/singlecellnet.py b/examples/single_modality/cell_type_annotation/singlecellnet.py index 9e1083f20..d2d42f87c 100755 --- a/examples/single_modality/cell_type_annotation/singlecellnet.py +++ b/examples/single_modality/cell_type_annotation/singlecellnet.py @@ -46,7 +46,8 @@ # Load data and perform necessary preprocessing dataloader = CellTypeAnnotationDataset(train_dataset=args.train_dataset, test_dataset=args.test_dataset, - species=args.species, tissue=args.tissue, val_size=args.val_size) + species=args.species, tissue=args.tissue, val_size=args.val_size, + data_dir="/mnt/nfs/zyxing/tempdata") data = dataloader.load_data(transform=preprocessing_pipeline, cache=args.cache) # Obtain training and testing data From 3905c7e49390d0f51a726fc45876813a37cff83c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:39:15 +0000 Subject: [PATCH 13/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../result_analysis/celltype_annotation/cta_scdeepsort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py index e14b3f8fe..54e6f0bbf 100644 --- a/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py +++ b/examples/result_analysis/celltype_annotation/cta_scdeepsort/main.py @@ -10,9 +10,9 @@ import numpy as np import torch +import wandb from sympy import elliptic_k -import wandb from dance import logger from dance.datasets.singlemodality import CellTypeAnnotationDataset from dance.modules.single_modality.cell_type_annotation.scdeepsort import ScDeepSort