cytocommunity2 0.1.1__tar.gz → 0.1.2__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.
Files changed (45) hide show
  1. cytocommunity2-0.1.2/LICENSE +21 -0
  2. cytocommunity2-0.1.2/PKG-INFO +167 -0
  3. cytocommunity2-0.1.2/README.md +146 -0
  4. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/pyproject.toml +5 -6
  5. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/cca.py +6 -13
  6. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/api.py +39 -42
  7. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/dataset.py +3 -4
  8. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/selection.py +106 -107
  9. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/training.py +17 -8
  10. cytocommunity2-0.1.2/src/cytocommunity2.egg-info/PKG-INFO +167 -0
  11. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/SOURCES.txt +1 -0
  12. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/requires.txt +0 -1
  13. cytocommunity2-0.1.1/PKG-INFO +0 -27
  14. cytocommunity2-0.1.1/README.md +0 -7
  15. cytocommunity2-0.1.1/src/cytocommunity2.egg-info/PKG-INFO +0 -27
  16. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/setup.cfg +0 -0
  17. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/__init__.py +0 -0
  18. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/colors.py +0 -0
  19. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/config.py +0 -0
  20. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/__init__.py +0 -0
  21. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/coherence.py +0 -0
  22. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/__init__.py +0 -0
  23. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/between.py +0 -0
  24. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/utils.py +0 -0
  25. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/within.py +0 -0
  26. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/composition.py +0 -0
  27. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/moran.py +0 -0
  28. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/__init__.py +0 -0
  29. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/cn_selection.py +0 -0
  30. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/coherence.py +0 -0
  31. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/common.py +0 -0
  32. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/communication.py +0 -0
  33. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/composition.py +0 -0
  34. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dominant.py +0 -0
  35. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dotplots.py +0 -0
  36. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/recurrence.py +0 -0
  37. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/runtime.py +0 -0
  38. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/ensemble.py +0 -0
  39. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/__init__.py +0 -0
  40. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/model.py +0 -0
  41. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/paths.py +0 -0
  42. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/visualization/__init__.py +0 -0
  43. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/visualization/spatial.py +0 -0
  44. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/dependency_links.txt +0 -0
  45. {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 huBioinfo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: cytocommunity2
3
+ Version: 0.1.2
4
+ Summary: Weakly supervised cellular-neighborhood learning for spatial omics
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: matplotlib>=3.6
9
+ Requires-Dist: numpy<2,>=1.23
10
+ Requires-Dist: pandas<3,>=1.5
11
+ Requires-Dist: sci-palettes
12
+ Requires-Dist: scikit-learn>=1.1
13
+ Requires-Dist: scipy>=1.9
14
+ Requires-Dist: seaborn>=0.12
15
+ Requires-Dist: torch-geometric>=2.0
16
+ Provides-Extra: downstream
17
+ Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
18
+ Requires-Dist: jupyter; extra == "downstream"
19
+ Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
20
+ Dynamic: license-file
21
+
22
+ # CytoCommunity2
23
+
24
+ ## Contents
25
+
26
+ - [Overview](#overview)
27
+ - [Installation](#installation)
28
+ - [Usage](#usage)
29
+ - [Maintainers](#maintainers)
30
+ - [Citation](#citation)
31
+
32
+ ## Overview
33
+
34
+ To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
35
+ present CytoCommunity2, **a unified weakly-supervised framework** for
36
+ identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
37
+ **large-scale spatial omics samples with single or multiple biological
38
+ conditions**.
39
+
40
+ Inspired by histopathology workflows, CytoCommunity2 first hierarchically
41
+ partitions the large single-cell spatial map into small patches, performs graph
42
+ construction and weakly supervised TCN learning for each patch, and finally
43
+ merges results through KNN-based TCN reassignment at segmentation boundaries to
44
+ ensure TCN spatial continuity. This strategy divides the original sample into
45
+ patches for TCN learning, achieving **memory efficiency (typical 24G graphics
46
+ memory is enough)** and also increased sample throughput. These optimizations
47
+ significantly **enhance the robustness of TCNs and cross-sample alignment
48
+ performance**.
49
+
50
+ Furthermore, to make CytoCommunity2 a unified framework that is also
51
+ **applicable for single-condition spatial omics datasets**, pseudo-samples with
52
+ artificial labels are generated, enabling automatic TCN alignment across real
53
+ samples via **contrastive learning**.
54
+
55
+ **_In summary, the advantages of CytoCommunity2 include:_**
56
+
57
+ **_(1) Using significantly less memory for large-scale spatial omics samples
58
+ with millions of cells._**
59
+
60
+ **_(2) A unified weakly-supervised model applicable for both multi-condition
61
+ and single-condition datasets._**
62
+
63
+ **_(3) High TCN alignment performance makes it well-suited for comparative
64
+ analysis in large cohort studies._**
65
+
66
+ ## Installation
67
+
68
+ ### Hardware requirement
69
+
70
+ Graphics memory: 24G
71
+
72
+ Storage: 10GB or more
73
+
74
+ ### Install CytoCommunity2
75
+
76
+ CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
77
+ activating a Conda environment first:
78
+
79
+ ```bash
80
+ conda create -n cytocommunity2 python=3.10
81
+ conda activate cytocommunity2
82
+ ```
83
+
84
+ Install PyTorch for your compute platform before installing CytoCommunity2.
85
+ For an NVIDIA GPU on Windows or Linux, first install the appropriate
86
+ CUDA-enabled PyTorch build from the
87
+ [official PyTorch installation page](https://pytorch.org/get-started/locally/).
88
+
89
+ Install the package:
90
+
91
+ ```bash
92
+ pip install cytocommunity2
93
+ ```
94
+
95
+ To run the downstream analyses, install the
96
+ optional downstream dependencies with:
97
+
98
+ ```bash
99
+ pip install "cytocommunity2[downstream]"
100
+ ```
101
+
102
+ This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
103
+ by the basic command above.
104
+
105
+ ## Usage
106
+
107
+ ### 1. Prepare input data
108
+
109
+ The input data to CytoCommunity2 includes four types of files :
110
+
111
+ (1) An image (sample) name list file, named as "ImageNameList.txt".
112
+
113
+ (2) A cell type label file for each image (sample), named as "[image
114
+ name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
115
+ customized image names listed in the "ImageNameList.txt". This file lists cell
116
+ type names of all cells in an image (sample).
117
+
118
+ (3) A cell spatial coordinate file for each image (sample), named as "[image
119
+ name]_Coordinates.txt". Note that [image_name] should be consistent with your
120
+ customized image names listed in the "ImageNameList.txt". This file lists cell
121
+ coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
122
+ orders should be exactly the same with "[image name]_CellTypeLabel.txt".
123
+
124
+ (4) A graph label file for each image (sample), named as "[image
125
+ name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
126
+ for every image (sample) and contain one integer indicating its biological
127
+ condition. For single-condition datasets, graph label
128
+ files can be omitted for all images (samples), and CytoCommunity2 will generate
129
+ pseudo-samples for weakly-supervised learning.
130
+
131
+ ### 2. Configure CytoCommunity2
132
+
133
+ Before running CytoCommunity2, set `input_dir` and review all
134
+ model and downstream parameters in
135
+ [`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
136
+
137
+ ### 3. Run CytoCommunity2
138
+
139
+ The tutorial uses a TNBC dataset as a worked example and demonstrates the
140
+ entire CytoCommunity2 workflow, from model training and CN identification to
141
+ downstream analysis:
142
+
143
+ ```bash
144
+ jupyter notebook tutorial/TNBC_workflow.ipynb
145
+ ```
146
+
147
+ ### 4. Output files
148
+
149
+ The main output directories are:
150
+
151
+ - `CNLearning_Output/`: CN-number screening, trained models and CN labels.
152
+ - `CNVisualization_Output/`: spatial plots and result tables.
153
+ - `data/`: downstream analysis results and intermediate data tables.
154
+ - `plot/`: figures generated by downstream analyses.
155
+
156
+ ## Maintainers
157
+
158
+ - Liukang Wu (yetong@stu.xidian.edu.cn)
159
+ - Yafei Xu (22031212416@stu.xidian.edu.cn)
160
+ - Yuxuan Hu (huyuxuan@xidian.edu.cn)
161
+
162
+ ## Citation
163
+
164
+ Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
165
+ Kai Tan. “Unsupervised and supervised discovery of tissue cellular
166
+ neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
167
+ https://doi.org/10.1038/s41592-023-02124-2
@@ -0,0 +1,146 @@
1
+ # CytoCommunity2
2
+
3
+ ## Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Installation](#installation)
7
+ - [Usage](#usage)
8
+ - [Maintainers](#maintainers)
9
+ - [Citation](#citation)
10
+
11
+ ## Overview
12
+
13
+ To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
14
+ present CytoCommunity2, **a unified weakly-supervised framework** for
15
+ identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
16
+ **large-scale spatial omics samples with single or multiple biological
17
+ conditions**.
18
+
19
+ Inspired by histopathology workflows, CytoCommunity2 first hierarchically
20
+ partitions the large single-cell spatial map into small patches, performs graph
21
+ construction and weakly supervised TCN learning for each patch, and finally
22
+ merges results through KNN-based TCN reassignment at segmentation boundaries to
23
+ ensure TCN spatial continuity. This strategy divides the original sample into
24
+ patches for TCN learning, achieving **memory efficiency (typical 24G graphics
25
+ memory is enough)** and also increased sample throughput. These optimizations
26
+ significantly **enhance the robustness of TCNs and cross-sample alignment
27
+ performance**.
28
+
29
+ Furthermore, to make CytoCommunity2 a unified framework that is also
30
+ **applicable for single-condition spatial omics datasets**, pseudo-samples with
31
+ artificial labels are generated, enabling automatic TCN alignment across real
32
+ samples via **contrastive learning**.
33
+
34
+ **_In summary, the advantages of CytoCommunity2 include:_**
35
+
36
+ **_(1) Using significantly less memory for large-scale spatial omics samples
37
+ with millions of cells._**
38
+
39
+ **_(2) A unified weakly-supervised model applicable for both multi-condition
40
+ and single-condition datasets._**
41
+
42
+ **_(3) High TCN alignment performance makes it well-suited for comparative
43
+ analysis in large cohort studies._**
44
+
45
+ ## Installation
46
+
47
+ ### Hardware requirement
48
+
49
+ Graphics memory: 24G
50
+
51
+ Storage: 10GB or more
52
+
53
+ ### Install CytoCommunity2
54
+
55
+ CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
56
+ activating a Conda environment first:
57
+
58
+ ```bash
59
+ conda create -n cytocommunity2 python=3.10
60
+ conda activate cytocommunity2
61
+ ```
62
+
63
+ Install PyTorch for your compute platform before installing CytoCommunity2.
64
+ For an NVIDIA GPU on Windows or Linux, first install the appropriate
65
+ CUDA-enabled PyTorch build from the
66
+ [official PyTorch installation page](https://pytorch.org/get-started/locally/).
67
+
68
+ Install the package:
69
+
70
+ ```bash
71
+ pip install cytocommunity2
72
+ ```
73
+
74
+ To run the downstream analyses, install the
75
+ optional downstream dependencies with:
76
+
77
+ ```bash
78
+ pip install "cytocommunity2[downstream]"
79
+ ```
80
+
81
+ This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
82
+ by the basic command above.
83
+
84
+ ## Usage
85
+
86
+ ### 1. Prepare input data
87
+
88
+ The input data to CytoCommunity2 includes four types of files :
89
+
90
+ (1) An image (sample) name list file, named as "ImageNameList.txt".
91
+
92
+ (2) A cell type label file for each image (sample), named as "[image
93
+ name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
94
+ customized image names listed in the "ImageNameList.txt". This file lists cell
95
+ type names of all cells in an image (sample).
96
+
97
+ (3) A cell spatial coordinate file for each image (sample), named as "[image
98
+ name]_Coordinates.txt". Note that [image_name] should be consistent with your
99
+ customized image names listed in the "ImageNameList.txt". This file lists cell
100
+ coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
101
+ orders should be exactly the same with "[image name]_CellTypeLabel.txt".
102
+
103
+ (4) A graph label file for each image (sample), named as "[image
104
+ name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
105
+ for every image (sample) and contain one integer indicating its biological
106
+ condition. For single-condition datasets, graph label
107
+ files can be omitted for all images (samples), and CytoCommunity2 will generate
108
+ pseudo-samples for weakly-supervised learning.
109
+
110
+ ### 2. Configure CytoCommunity2
111
+
112
+ Before running CytoCommunity2, set `input_dir` and review all
113
+ model and downstream parameters in
114
+ [`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
115
+
116
+ ### 3. Run CytoCommunity2
117
+
118
+ The tutorial uses a TNBC dataset as a worked example and demonstrates the
119
+ entire CytoCommunity2 workflow, from model training and CN identification to
120
+ downstream analysis:
121
+
122
+ ```bash
123
+ jupyter notebook tutorial/TNBC_workflow.ipynb
124
+ ```
125
+
126
+ ### 4. Output files
127
+
128
+ The main output directories are:
129
+
130
+ - `CNLearning_Output/`: CN-number screening, trained models and CN labels.
131
+ - `CNVisualization_Output/`: spatial plots and result tables.
132
+ - `data/`: downstream analysis results and intermediate data tables.
133
+ - `plot/`: figures generated by downstream analyses.
134
+
135
+ ## Maintainers
136
+
137
+ - Liukang Wu (yetong@stu.xidian.edu.cn)
138
+ - Yafei Xu (22031212416@stu.xidian.edu.cn)
139
+ - Yuxuan Hu (huyuxuan@xidian.edu.cn)
140
+
141
+ ## Citation
142
+
143
+ Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
144
+ Kai Tan. “Unsupervised and supervised discovery of tissue cellular
145
+ neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
146
+ https://doi.org/10.1038/s41592-023-02124-2
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cytocommunity2"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Weakly supervised cellular-neighborhood learning for spatial omics"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
- dependencies = [
11
+ dependencies = [
12
12
  "matplotlib>=3.6",
13
13
  "numpy>=1.23,<2",
14
14
  "pandas>=1.5,<3",
@@ -16,7 +16,6 @@ dependencies = [
16
16
  "scikit-learn>=1.1",
17
17
  "scipy>=1.9",
18
18
  "seaborn>=0.12",
19
- "torch>=1.11",
20
19
  "torch-geometric>=2.0",
21
20
  ]
22
21
 
@@ -27,6 +26,6 @@ downstream = [
27
26
  "libpysal>=4.9,<4.10",
28
27
  ]
29
28
 
30
- [tool.setuptools.packages.find]
31
- where = ["src"]
32
- include = ["cytocommunity2*"]
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
31
+ include = ["cytocommunity2*"]
@@ -169,14 +169,7 @@ def run_cca_for_scores(
169
169
  "Y_mat": y_matrix,
170
170
  }
171
171
 
172
- columns = ["CN_A", "CN_B", "n_samples"]
173
- if condition is not None:
174
- columns.append("Condition")
175
- for component in range(communication_params["cca_canonical_components"]):
176
- columns.extend(
177
- [f"rho{component + 1}", f"pval{component + 1}", f"invalid_perm{component + 1}"]
178
- )
179
- results = pd.DataFrame(result_rows, columns=columns)
172
+ results = pd.DataFrame(result_rows)
180
173
  if not results.empty:
181
174
  results = results.sort_values(
182
175
  "rho1", key=lambda values: values.abs(), ascending=False, na_position="last"
@@ -303,11 +296,11 @@ def run_cca_analysis(config, *, input_file=None, output_dir=None):
303
296
  condition,
304
297
  )
305
298
  condition_results.append(result["results"])
306
- by_condition = (
307
- pd.concat(condition_results, ignore_index=True)
308
- if condition_results
309
- else pd.DataFrame(columns=["Condition", *pooled["results"].columns])
310
- )
299
+ by_condition = (
300
+ pd.concat(condition_results, ignore_index=True)
301
+ if condition_results
302
+ else pd.DataFrame()
303
+ )
311
304
  if not by_condition.empty:
312
305
  by_condition["Condition"] = pd.Categorical(
313
306
  by_condition["Condition"], categories=conditions, ordered=True
@@ -8,12 +8,11 @@ import shutil
8
8
  import pandas as pd
9
9
 
10
10
  from ..config import CytoCommunityConfig
11
- from ..ensemble import ensemble_cn
12
- from ..paths import build_paths
11
+ from ..ensemble import ensemble_cn
13
12
  from .dataset import SpatialGraphDataset
14
13
  from .model import TrainingConfig
15
14
  from .selection import evaluate_cn_num, select_cn_num
16
- from .training import train_one_run
15
+ from .training import train_one_run
17
16
 
18
17
 
19
18
  @dataclass(frozen=True)
@@ -34,9 +33,11 @@ class LearningResult:
34
33
  def load_learning_result(
35
34
  dataset: SpatialGraphDataset,
36
35
  config: CytoCommunityConfig,
36
+ *,
37
+ output_dir,
37
38
  ) -> LearningResult:
38
39
  """Restore a :class:`LearningResult` from a completed learning run."""
39
- output_dir = build_paths(config).learning_output
40
+ output_dir = Path(output_dir).expanduser().resolve()
40
41
  paths = {
41
42
  "selected CN number": output_dir / "SelectedCNNum.txt",
42
43
  "cell-type list": output_dir / "UniqueCellTypeList.txt",
@@ -85,11 +86,13 @@ def _training_config(config, num_cn, num_class):
85
86
 
86
87
 
87
88
  def run_learning(
88
- dataset: SpatialGraphDataset,
89
- config: CytoCommunityConfig,
89
+ dataset: SpatialGraphDataset,
90
+ config: CytoCommunityConfig,
91
+ *,
92
+ output_dir,
90
93
  ) -> LearningResult:
91
- """Select the CN number, train final runs, and export consensus labels."""
92
- output_dir = build_paths(config).learning_output
94
+ """Select the CN number, train final runs, and export consensus labels."""
95
+ output_dir = Path(output_dir).expanduser().resolve()
93
96
  input_dir = config.input_dir
94
97
  unique_celltypes_file = output_dir / "UniqueCellTypeList.txt"
95
98
  selected_model_dir = output_dir / "SelectedModel"
@@ -147,44 +150,38 @@ def run_learning(
147
150
  )
148
151
  del hard_assignments
149
152
  selection_results.append(result)
150
- cn_pvalues = cn_pvalues[
151
- [
152
- "CN",
153
- "n_real",
154
- "n_background",
155
- "real_mean",
156
- "background_mean",
157
- "delta_mean",
158
- "KSStatistic",
159
- "KSLocation",
160
- "p_value_one_sided",
161
- "p_adjusted_bh",
162
- "RecurrenceScore",
153
+ cn_pvalues = cn_pvalues[
154
+ [
155
+ "CN",
156
+ "p_value_one_sided",
157
+ "p_adjusted_bh",
158
+ "RecurrenceScore",
163
159
  "SignificantRecurrence",
164
160
  ]
165
161
  ].copy()
166
162
  cn_pvalues.insert(0, "NumCN", num_cn)
167
- cn_pvalue_tables.append(cn_pvalues)
168
- print(
169
- f"K={num_cn}: Ksig={result['Ksig']}, "
170
- f"significantFraction={result['SignificantFraction']:.6f}, "
171
- f"MeanRS={result['MeanRS']:.6f}, CRI={result['CRI']:.6f}"
172
- )
173
-
174
- pd.concat(cn_pvalue_tables, ignore_index=True).to_csv(
175
- cn_pvalues_file, index=False
176
- )
177
- selected = select_cn_num(selection_results)
178
- selected_cn = int(selected["NumCN"])
179
- selection_summary = pd.DataFrame(selection_results).sort_values("NumCN")
180
- selection_summary.to_csv(selection_summary_file, index=False)
181
- (output_dir / "SelectedCNNum.txt").write_text(
182
- f"{selected_cn}\n", encoding="utf-8"
183
- )
184
- print(
185
- f"\nBest CRI: K={selected_cn} ({selected['CRI']:.6f}). "
186
- f"Training a fresh set of Runs 1-{config.main.final_num_runs}."
187
- )
163
+ cn_pvalue_tables.append(cn_pvalues)
164
+ print(
165
+ f"K={num_cn}: significantCN={result['SignificantCN']}, "
166
+ f"significantFraction={result['SignificantFraction']:.6f}, "
167
+ f"meanRS={result['MeanRS']:.6f}, score={result['Score']:.6f}"
168
+ )
169
+
170
+ pd.DataFrame(selection_results).sort_values("NumCN").to_csv(
171
+ selection_summary_file, index=False
172
+ )
173
+ pd.concat(cn_pvalue_tables, ignore_index=True).to_csv(
174
+ cn_pvalues_file, index=False
175
+ )
176
+ selected = select_cn_num(selection_results)
177
+ selected_cn = int(selected["NumCN"])
178
+ (output_dir / "SelectedCNNum.txt").write_text(
179
+ f"{selected_cn}\n", encoding="utf-8"
180
+ )
181
+ print(
182
+ f"\nSelected K={selected_cn} with score={selected['Score']:.6f}. "
183
+ f"Training a fresh set of Runs 1-{config.main.final_num_runs}."
184
+ )
188
185
 
189
186
  selected_config = _training_config(
190
187
  config, num_cn=selected_cn, num_class=dataset.num_classes
@@ -64,10 +64,9 @@ def _build_knn_edge_index(coordinates, knn_k):
64
64
  ).contiguous()
65
65
 
66
66
 
67
- def build_spatial_graphs(config):
68
- """Read the configured samples and construct their KNN graphs once."""
69
- input_folder = config.input_dir
70
- knn_k = config.main.knn_k
67
+ def build_spatial_graphs(input_dir, *, knn_k):
68
+ """Read real samples and construct their coordinate-based KNN graphs once."""
69
+ input_folder = Path(input_dir)
71
70
  sample_names = _read_lines(input_folder / "ImageNameList.txt")
72
71
  labels_by_sample = {
73
72
  sample: _read_lines(input_folder / f"{sample}_CellTypeLabel.txt")
@@ -3,9 +3,12 @@
3
3
  from itertools import combinations
4
4
  from pathlib import Path
5
5
 
6
- import numpy as np
7
- import pandas as pd
8
- from scipy.stats import hypergeom, ks_2samp
6
+ import numpy as np
7
+ import pandas as pd
8
+ from scipy.stats import hypergeom, ttest_ind
9
+
10
+
11
+ MIN_FLOAT_P = np.finfo(np.float64).tiny
9
12
 
10
13
 
11
14
  def read_nonempty_lines(path):
@@ -218,7 +221,7 @@ def overlap_coefficient(first, second):
218
221
  return len(first & second) / smaller_size if smaller_size else 0.0
219
222
 
220
223
 
221
- def _load_dominant_sets(samples, score_dir, score_threshold):
224
+ def _load_dominant_sets(samples, score_dir, score_threshold):
222
225
  celltypes = (
223
226
  pd.read_csv(Path(score_dir) / "CellTypeVec_List.csv")
224
227
  .iloc[:, 0]
@@ -241,39 +244,42 @@ def _load_dominant_sets(samples, score_dir, score_threshold):
241
244
  set(celltypes[matrix[:, cn] > score_threshold])
242
245
  for cn in range(matrix.shape[1])
243
246
  ]
244
- return dominant_sets, num_cn
245
-
246
-
247
- def _summarize_ks(cn, real_values, background_values):
248
- real_values = np.asarray(real_values, dtype=float)
249
- background_values = np.asarray(background_values, dtype=float)
250
- if len(real_values) >= 1 and len(background_values) >= 1:
251
- result = ks_2samp(
252
- real_values,
253
- background_values,
254
- alternative="less",
255
- method="asymp",
256
- )
257
- ks_statistic = float(result.statistic)
258
- p_value = float(result.pvalue)
259
- ks_location = float(result.statistic_location)
260
- else:
261
- ks_statistic, p_value, ks_location = np.nan, np.nan, np.nan
262
- real_mean = np.mean(real_values) if len(real_values) else np.nan
263
- background_mean = (
264
- np.mean(background_values) if len(background_values) else np.nan
247
+ return dominant_sets, num_cn
248
+
249
+
250
+ def _summarize_ttest(cn, real_values, background_values):
251
+ real_values = np.asarray(real_values, dtype=float)
252
+ background_values = np.asarray(background_values, dtype=float)
253
+ if len(real_values) >= 2 and len(background_values) >= 2:
254
+ result = ttest_ind(
255
+ real_values,
256
+ background_values,
257
+ equal_var=False,
258
+ alternative="greater",
259
+ )
260
+ t_statistic, p_value = float(result.statistic), float(result.pvalue)
261
+ else:
262
+ t_statistic, p_value = np.nan, np.nan
263
+ real_mean = np.mean(real_values) if len(real_values) else np.nan
264
+ background_mean = (
265
+ np.mean(background_values) if len(background_values) else np.nan
265
266
  )
266
267
  return {
267
- "CN": cn,
268
- "n_real": len(real_values),
269
- "n_background": len(background_values),
270
- "real_mean": real_mean,
271
- "background_mean": background_mean,
272
- "delta_mean": real_mean - background_mean,
273
- "KSStatistic": ks_statistic,
274
- "KSLocation": ks_location,
275
- "p_value_one_sided": p_value,
276
- }
268
+ "CN": cn,
269
+ "n_real": len(real_values),
270
+ "n_background": len(background_values),
271
+ "real_mean": real_mean,
272
+ "real_std": np.std(real_values, ddof=1) if len(real_values) > 1 else np.nan,
273
+ "background_mean": background_mean,
274
+ "background_std": (
275
+ np.std(background_values, ddof=1)
276
+ if len(background_values) > 1
277
+ else np.nan
278
+ ),
279
+ "delta_mean": real_mean - background_mean,
280
+ "t_statistic": t_statistic,
281
+ "p_value_one_sided": p_value,
282
+ }
277
283
 
278
284
 
279
285
  def calculate_recurrence(
@@ -316,21 +322,36 @@ def calculate_recurrence(
316
322
  )
317
323
  background_df = pd.DataFrame({"OverlapCoefficient": background_values})
318
324
 
319
- ks_rows = []
320
- for cn in [f"CN{index + 1}" for index in range(num_cn)]:
321
- values = real_df.loc[
322
- real_df["CN"] == cn, "OverlapCoefficient"
323
- ].to_numpy()
324
- ks_rows.append(_summarize_ks(cn, values, background_values))
325
- ks_df = pd.DataFrame(ks_rows)
326
- ks_df["p_adjusted_bh"] = bh_adjust(
327
- ks_df["p_value_one_sided"].to_numpy()
328
- )
329
- ks_df["RecurrenceScore"] = ks_df["KSStatistic"] * 100.0
330
- ks_df["SignificantRecurrence"] = (
331
- (ks_df["p_adjusted_bh"] < recurrence_p_threshold)
332
- & (ks_df["delta_mean"] > 0)
333
- )
325
+ ttest_rows = []
326
+ for cn in [f"CN{index + 1}" for index in range(num_cn)]:
327
+ values = real_df.loc[
328
+ real_df["CN"] == cn, "OverlapCoefficient"
329
+ ].to_numpy()
330
+ ttest_rows.append(_summarize_ttest(cn, values, background_values))
331
+ ttest_rows.append(
332
+ _summarize_ttest(
333
+ "Overall", real_df["OverlapCoefficient"].to_numpy(), background_values
334
+ )
335
+ )
336
+ ttest_df = pd.DataFrame(ttest_rows)
337
+ cn_mask = ttest_df["CN"] != "Overall"
338
+ adjusted_pvalues = bh_adjust(
339
+ ttest_df.loc[cn_mask, "p_value_one_sided"].to_numpy()
340
+ )
341
+ ttest_df["p_adjusted_bh"] = np.nan
342
+ ttest_df["RecurrenceScore"] = np.nan
343
+ ttest_df["SignificantRecurrence"] = False
344
+ ttest_df.loc[cn_mask, "p_adjusted_bh"] = adjusted_pvalues
345
+ ttest_df.loc[cn_mask, "RecurrenceScore"] = -np.log10(
346
+ np.maximum(adjusted_pvalues, MIN_FLOAT_P)
347
+ )
348
+ ttest_df.loc[cn_mask, "SignificantRecurrence"] = (
349
+ (
350
+ ttest_df.loc[cn_mask, "p_adjusted_bh"]
351
+ < recurrence_p_threshold
352
+ )
353
+ & (ttest_df.loc[cn_mask, "delta_mean"] > 0)
354
+ )
334
355
 
335
356
  output_dir = Path(output_dir)
336
357
  output_dir.mkdir(parents=True, exist_ok=True)
@@ -338,11 +359,11 @@ def calculate_recurrence(
338
359
  background_df.to_csv(
339
360
  output_dir / "BackgroundDistribution_randomCN.csv", index=False
340
361
  )
341
- ks_df.to_csv(output_dir / "KSTest_Results.csv", index=False)
342
- return real_df, background_df, ks_df
362
+ ttest_df.to_csv(output_dir / "TTest_Results.csv", index=False)
363
+ return real_df, background_df, ttest_df
343
364
 
344
365
 
345
- def evaluate_cn_num(
366
+ def evaluate_cn_num(
346
367
  num_cn,
347
368
  dataset,
348
369
  celltype_file,
@@ -368,60 +389,38 @@ def evaluate_cn_num(
368
389
  cn_label_dir=cn_label_dir,
369
390
  hard_assignments=hard_assignments,
370
391
  )
371
- real_df, _, per_cn = calculate_recurrence(
372
- enrichment_dir,
373
- Path(dataset) / "ImageNameList.txt",
374
- overlap_dir,
392
+ real_df, _, ttest_df = calculate_recurrence(
393
+ enrichment_dir,
394
+ Path(dataset) / "ImageNameList.txt",
395
+ overlap_dir,
375
396
  score_threshold=-np.log10(enrichment_p_threshold),
376
397
  recurrence_p_threshold=recurrence_p_threshold,
377
- n_random=n_random,
378
- seed=seed,
379
- )
380
- significant_mask = per_cn["SignificantRecurrence"].astype(bool)
381
- significant_cn = int(significant_mask.sum())
382
- significant_fraction = significant_cn / num_cn
383
- mean_oc = float(real_df["OverlapCoefficient"].mean())
384
- mean_rs = float(per_cn["RecurrenceScore"].mean())
385
- cri = significant_fraction**2 * mean_rs
386
- result = {
387
- "NumCN": int(num_cn),
388
- "Ksig": significant_cn,
389
- "SignificantFraction": significant_fraction,
390
- "MeanOC": mean_oc,
391
- "MeanRS": mean_rs,
392
- "CRI": cri,
393
- }
398
+ n_random=n_random,
399
+ seed=seed,
400
+ )
401
+ per_cn = ttest_df[ttest_df["CN"] != "Overall"]
402
+ significant_mask = per_cn["SignificantRecurrence"].astype(bool)
403
+ significant_cn = int(significant_mask.sum())
404
+ significant_fraction = significant_cn / num_cn
405
+ mean_rs = float(per_cn["RecurrenceScore"].mean())
406
+ mean_oc = float(real_df["OverlapCoefficient"].mean())
407
+ score = float(
408
+ significant_fraction**2 * significant_cn * mean_rs
409
+ )
410
+ result = {
411
+ "NumCN": int(num_cn),
412
+ "SignificantCN": significant_cn,
413
+ "SignificantFraction": significant_fraction,
414
+ "MeanRS": mean_rs,
415
+ "MeanOC": mean_oc,
416
+ "Score": score,
417
+ }
394
418
  pd.DataFrame([result]).to_csv(output_dir / "CNNumScore.csv", index=False)
395
- return result, per_cn.copy()
396
-
397
-
398
- def evaluate_selected_cn(result, config):
399
- """Evaluate the selected CN labels and export the communication input."""
400
- from ..paths import build_paths
401
-
402
- paths = build_paths(config)
403
- metrics = evaluate_cn_num(
404
- num_cn=result.selected_cn,
405
- dataset=paths.input_dir,
406
- cn_label_dir=result.hard_assignment_dir,
407
- celltype_file=result.unique_celltypes_file,
408
- output_dir=paths.downstream_data,
409
- enrichment_p_threshold=config.enrichment.p_value_threshold,
410
- minimum_adjusted_p=config.enrichment.minimum_adjusted_p,
411
- recurrence_p_threshold=config.recurrence.recurrence_p_threshold,
412
- n_random=config.recurrence.null_draws,
413
- seed=config.random_seed,
414
- )
415
- export_enrichment_long(
416
- dataset=paths.input_dir,
417
- enrichment_dir=paths.enrichment,
418
- output_file=paths.communication_config / "EnrichScoreMatrix_long.csv",
419
- )
420
- return metrics
421
-
422
-
423
- def select_cn_num(results):
424
- """Select the K with the maximum CRI, preferring smaller K on ties."""
425
- if not results:
426
- raise ValueError("No CN-number results were supplied")
427
- return min(results, key=lambda row: (-row["CRI"], row["NumCN"])).copy()
419
+ return result, per_cn.copy()
420
+
421
+
422
+ def select_cn_num(results):
423
+ """Select maximum score, using smaller K as the deterministic tie-break."""
424
+ if not results:
425
+ raise ValueError("No CN-number results were supplied")
426
+ return min(results, key=lambda row: (-row["Score"], row["NumCN"]))
@@ -14,16 +14,19 @@ import torch
14
14
  import torch.nn.functional as F
15
15
  from torch_geometric.loader import DataLoader
16
16
 
17
- from .model import CNModel
18
-
19
-
20
- def set_reproducible_seed(seed):
17
+ from .model import CNModel
18
+
19
+
20
+ def set_reproducible_seed(seed):
21
21
  random.seed(seed)
22
22
  np.random.seed(seed)
23
23
  torch.manual_seed(seed)
24
24
  torch.cuda.manual_seed_all(seed)
25
25
  torch.backends.cudnn.benchmark = False
26
26
  torch.backends.cudnn.deterministic = True
27
+ if hasattr(torch.backends, "cuda") and hasattr(torch.backends.cuda, "matmul"):
28
+ torch.backends.cuda.matmul.allow_tf32 = False
29
+ torch.backends.cudnn.allow_tf32 = False
27
30
  torch.use_deterministic_algorithms(True, warn_only=True)
28
31
 
29
32
 
@@ -133,10 +136,16 @@ def train_one_run(
133
136
  shuffle=True,
134
137
  pin_memory=True,
135
138
  generator=loader_generator,
136
- )
137
- if device is None:
138
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
139
- model = CNModel(dataset.num_features, config).to(device)
139
+ )
140
+ if device is None:
141
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
142
+ else:
143
+ device = torch.device(device)
144
+ if device.type == "cuda":
145
+ print(f" Training device: GPU ({torch.cuda.get_device_name(device)})")
146
+ else:
147
+ print(" Training device: CPU")
148
+ model = CNModel(dataset.num_features, config).to(device)
140
149
  optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)
141
150
 
142
151
  loss_csv = output_folder / "Epoch_TrainLoss.csv"
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: cytocommunity2
3
+ Version: 0.1.2
4
+ Summary: Weakly supervised cellular-neighborhood learning for spatial omics
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: matplotlib>=3.6
9
+ Requires-Dist: numpy<2,>=1.23
10
+ Requires-Dist: pandas<3,>=1.5
11
+ Requires-Dist: sci-palettes
12
+ Requires-Dist: scikit-learn>=1.1
13
+ Requires-Dist: scipy>=1.9
14
+ Requires-Dist: seaborn>=0.12
15
+ Requires-Dist: torch-geometric>=2.0
16
+ Provides-Extra: downstream
17
+ Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
18
+ Requires-Dist: jupyter; extra == "downstream"
19
+ Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
20
+ Dynamic: license-file
21
+
22
+ # CytoCommunity2
23
+
24
+ ## Contents
25
+
26
+ - [Overview](#overview)
27
+ - [Installation](#installation)
28
+ - [Usage](#usage)
29
+ - [Maintainers](#maintainers)
30
+ - [Citation](#citation)
31
+
32
+ ## Overview
33
+
34
+ To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
35
+ present CytoCommunity2, **a unified weakly-supervised framework** for
36
+ identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
37
+ **large-scale spatial omics samples with single or multiple biological
38
+ conditions**.
39
+
40
+ Inspired by histopathology workflows, CytoCommunity2 first hierarchically
41
+ partitions the large single-cell spatial map into small patches, performs graph
42
+ construction and weakly supervised TCN learning for each patch, and finally
43
+ merges results through KNN-based TCN reassignment at segmentation boundaries to
44
+ ensure TCN spatial continuity. This strategy divides the original sample into
45
+ patches for TCN learning, achieving **memory efficiency (typical 24G graphics
46
+ memory is enough)** and also increased sample throughput. These optimizations
47
+ significantly **enhance the robustness of TCNs and cross-sample alignment
48
+ performance**.
49
+
50
+ Furthermore, to make CytoCommunity2 a unified framework that is also
51
+ **applicable for single-condition spatial omics datasets**, pseudo-samples with
52
+ artificial labels are generated, enabling automatic TCN alignment across real
53
+ samples via **contrastive learning**.
54
+
55
+ **_In summary, the advantages of CytoCommunity2 include:_**
56
+
57
+ **_(1) Using significantly less memory for large-scale spatial omics samples
58
+ with millions of cells._**
59
+
60
+ **_(2) A unified weakly-supervised model applicable for both multi-condition
61
+ and single-condition datasets._**
62
+
63
+ **_(3) High TCN alignment performance makes it well-suited for comparative
64
+ analysis in large cohort studies._**
65
+
66
+ ## Installation
67
+
68
+ ### Hardware requirement
69
+
70
+ Graphics memory: 24G
71
+
72
+ Storage: 10GB or more
73
+
74
+ ### Install CytoCommunity2
75
+
76
+ CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
77
+ activating a Conda environment first:
78
+
79
+ ```bash
80
+ conda create -n cytocommunity2 python=3.10
81
+ conda activate cytocommunity2
82
+ ```
83
+
84
+ Install PyTorch for your compute platform before installing CytoCommunity2.
85
+ For an NVIDIA GPU on Windows or Linux, first install the appropriate
86
+ CUDA-enabled PyTorch build from the
87
+ [official PyTorch installation page](https://pytorch.org/get-started/locally/).
88
+
89
+ Install the package:
90
+
91
+ ```bash
92
+ pip install cytocommunity2
93
+ ```
94
+
95
+ To run the downstream analyses, install the
96
+ optional downstream dependencies with:
97
+
98
+ ```bash
99
+ pip install "cytocommunity2[downstream]"
100
+ ```
101
+
102
+ This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
103
+ by the basic command above.
104
+
105
+ ## Usage
106
+
107
+ ### 1. Prepare input data
108
+
109
+ The input data to CytoCommunity2 includes four types of files :
110
+
111
+ (1) An image (sample) name list file, named as "ImageNameList.txt".
112
+
113
+ (2) A cell type label file for each image (sample), named as "[image
114
+ name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
115
+ customized image names listed in the "ImageNameList.txt". This file lists cell
116
+ type names of all cells in an image (sample).
117
+
118
+ (3) A cell spatial coordinate file for each image (sample), named as "[image
119
+ name]_Coordinates.txt". Note that [image_name] should be consistent with your
120
+ customized image names listed in the "ImageNameList.txt". This file lists cell
121
+ coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
122
+ orders should be exactly the same with "[image name]_CellTypeLabel.txt".
123
+
124
+ (4) A graph label file for each image (sample), named as "[image
125
+ name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
126
+ for every image (sample) and contain one integer indicating its biological
127
+ condition. For single-condition datasets, graph label
128
+ files can be omitted for all images (samples), and CytoCommunity2 will generate
129
+ pseudo-samples for weakly-supervised learning.
130
+
131
+ ### 2. Configure CytoCommunity2
132
+
133
+ Before running CytoCommunity2, set `input_dir` and review all
134
+ model and downstream parameters in
135
+ [`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
136
+
137
+ ### 3. Run CytoCommunity2
138
+
139
+ The tutorial uses a TNBC dataset as a worked example and demonstrates the
140
+ entire CytoCommunity2 workflow, from model training and CN identification to
141
+ downstream analysis:
142
+
143
+ ```bash
144
+ jupyter notebook tutorial/TNBC_workflow.ipynb
145
+ ```
146
+
147
+ ### 4. Output files
148
+
149
+ The main output directories are:
150
+
151
+ - `CNLearning_Output/`: CN-number screening, trained models and CN labels.
152
+ - `CNVisualization_Output/`: spatial plots and result tables.
153
+ - `data/`: downstream analysis results and intermediate data tables.
154
+ - `plot/`: figures generated by downstream analyses.
155
+
156
+ ## Maintainers
157
+
158
+ - Liukang Wu (yetong@stu.xidian.edu.cn)
159
+ - Yafei Xu (22031212416@stu.xidian.edu.cn)
160
+ - Yuxuan Hu (huyuxuan@xidian.edu.cn)
161
+
162
+ ## Citation
163
+
164
+ Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
165
+ Kai Tan. “Unsupervised and supervised discovery of tissue cellular
166
+ neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
167
+ https://doi.org/10.1038/s41592-023-02124-2
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.md
2
3
  pyproject.toml
3
4
  src/cytocommunity2/__init__.py
@@ -5,7 +5,6 @@ sci-palettes
5
5
  scikit-learn>=1.1
6
6
  scipy>=1.9
7
7
  seaborn>=0.12
8
- torch>=1.11
9
8
  torch-geometric>=2.0
10
9
 
11
10
  [downstream]
@@ -1,27 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: cytocommunity2
3
- Version: 0.1.1
4
- Summary: Weakly supervised cellular-neighborhood learning for spatial omics
5
- Requires-Python: >=3.10
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: matplotlib>=3.6
8
- Requires-Dist: numpy<2,>=1.23
9
- Requires-Dist: pandas<3,>=1.5
10
- Requires-Dist: sci-palettes
11
- Requires-Dist: scikit-learn>=1.1
12
- Requires-Dist: scipy>=1.9
13
- Requires-Dist: seaborn>=0.12
14
- Requires-Dist: torch>=1.11
15
- Requires-Dist: torch-geometric>=2.0
16
- Provides-Extra: downstream
17
- Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
18
- Requires-Dist: jupyter; extra == "downstream"
19
- Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
20
-
21
- # CytoCommunity2
22
-
23
- CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
24
-
25
- ## Installation
26
-
27
- Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
@@ -1,7 +0,0 @@
1
- # CytoCommunity2
2
-
3
- CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
4
-
5
- ## Installation
6
-
7
- Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
@@ -1,27 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: cytocommunity2
3
- Version: 0.1.1
4
- Summary: Weakly supervised cellular-neighborhood learning for spatial omics
5
- Requires-Python: >=3.10
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: matplotlib>=3.6
8
- Requires-Dist: numpy<2,>=1.23
9
- Requires-Dist: pandas<3,>=1.5
10
- Requires-Dist: sci-palettes
11
- Requires-Dist: scikit-learn>=1.1
12
- Requires-Dist: scipy>=1.9
13
- Requires-Dist: seaborn>=0.12
14
- Requires-Dist: torch>=1.11
15
- Requires-Dist: torch-geometric>=2.0
16
- Provides-Extra: downstream
17
- Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
18
- Requires-Dist: jupyter; extra == "downstream"
19
- Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
20
-
21
- # CytoCommunity2
22
-
23
- CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
24
-
25
- ## Installation
26
-
27
- Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
File without changes