sclab 0.2.2__tar.gz → 0.3.4__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.
- {sclab-0.2.2 → sclab-0.3.4}/PKG-INFO +14 -8
- {sclab-0.2.2 → sclab-0.3.4}/README.md +4 -4
- {sclab-0.2.2 → sclab-0.3.4}/pyproject.toml +16 -7
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/__init__.py +1 -1
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_io.py +0 -6
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_sclab.py +19 -7
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/_dataset.py +1 -1
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/_processor.py +19 -4
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/_results_panel.py +26 -12
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/__init__.py +6 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_differential_expression.py +2 -1
- sclab-0.3.4/src/sclab/examples/processor_steps/_doublet_detection.py +68 -0
- sclab-0.3.4/src/sclab/examples/processor_steps/_gene_expression.py +125 -0
- sclab-0.3.4/src/sclab/examples/processor_steps/_integration.py +116 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_neighbors.py +24 -4
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_pca.py +11 -6
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_preprocess.py +50 -23
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_qc.py +22 -6
- sclab-0.3.4/src/sclab/gui/components/__init__.py +7 -0
- sclab-0.3.4/src/sclab/gui/components/_guided_pseudotime.py +482 -0
- sclab-0.3.4/src/sclab/gui/components/_transfer_metadata.py +186 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/methods/__init__.py +22 -0
- sclab-0.3.4/src/sclab/preprocess/__init__.py +26 -0
- sclab-0.3.4/src/sclab/preprocess/_cca.py +176 -0
- sclab-0.3.4/src/sclab/preprocess/_cca_integrate.py +109 -0
- sclab-0.3.4/src/sclab/preprocess/_filter_obs.py +42 -0
- sclab-0.3.4/src/sclab/preprocess/_harmony.py +421 -0
- sclab-0.3.4/src/sclab/preprocess/_harmony_integrate.py +53 -0
- sclab-0.3.4/src/sclab/preprocess/_normalize_weighted.py +65 -0
- sclab-0.3.4/src/sclab/preprocess/_pca.py +51 -0
- sclab-0.3.4/src/sclab/preprocess/_preprocess.py +155 -0
- sclab-0.3.4/src/sclab/preprocess/_qc.py +38 -0
- sclab-0.3.4/src/sclab/preprocess/_rpca.py +116 -0
- sclab-0.3.4/src/sclab/preprocess/_subset.py +208 -0
- sclab-0.3.4/src/sclab/preprocess/_transfer_metadata.py +196 -0
- sclab-0.3.4/src/sclab/preprocess/_transform.py +82 -0
- sclab-0.3.4/src/sclab/preprocess/_utils.py +96 -0
- sclab-0.3.4/src/sclab/scanpy/__init__.py +0 -0
- sclab-0.3.4/src/sclab/scanpy/plotting/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/cellflow/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/cellflow/density_dynamics/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/cellflow/density_dynamics/_density_dynamics.py +349 -0
- sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/_pseudotime.py +336 -0
- sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/timeseries.py +226 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/density_nd.py +215 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/interpolate.py +334 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/periodic_genes.py +106 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/smoothen.py +124 -0
- sclab-0.3.4/src/sclab/tools/cellflow/utils/times.py +55 -0
- sclab-0.3.4/src/sclab/tools/differential_expression/__init__.py +7 -0
- sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_edger.py +309 -0
- sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_helpers.py +290 -0
- sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_limma.py +257 -0
- sclab-0.3.4/src/sclab/tools/doublet_detection/__init__.py +5 -0
- sclab-0.3.4/src/sclab/tools/doublet_detection/_scrublet.py +64 -0
- sclab-0.3.4/src/sclab/tools/embedding/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/imputation/__init__.py +0 -0
- sclab-0.3.4/src/sclab/tools/imputation/_alra.py +135 -0
- sclab-0.3.4/src/sclab/tools/labeling/__init__.py +6 -0
- sclab-0.3.4/src/sclab/tools/labeling/sctype.py +233 -0
- sclab-0.3.4/src/sclab/tools/utils/__init__.py +5 -0
- sclab-0.3.4/src/sclab/tools/utils/_aggregate_and_filter.py +290 -0
- sclab-0.3.4/src/sclab/utils/__init__.py +5 -0
- sclab-0.3.4/src/sclab/utils/_write_excel.py +510 -0
- {sclab-0.2.2 → sclab-0.3.4}/LICENSE +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_methods_registry.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/_exceptions.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_controls.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_plotter.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_utils.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/_basic_processor_step.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/_processor_step_base.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_broker.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_client.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_utils.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_cluster.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_umap.py +0 -0
- {sclab-0.2.2/src/sclab/scanpy/plotting → sclab-0.3.4/src/sclab/gui}/__init__.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/_compat.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/_settings.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/logging.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/plotting/_rcmod.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/plotting/palettes.py +0 -0
- {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/readwrite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sclab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: sclab
|
|
5
5
|
Author-email: Argenis Arriojas <ArriojasMaldonado001@umb.edu>
|
|
6
6
|
Requires-Python: >=3.10,<3.13
|
|
@@ -14,15 +14,20 @@ License-File: LICENSE
|
|
|
14
14
|
Requires-Dist: anndata
|
|
15
15
|
Requires-Dist: anywidget
|
|
16
16
|
Requires-Dist: ipywidgets
|
|
17
|
-
Requires-Dist: itables
|
|
17
|
+
Requires-Dist: itables<2.4
|
|
18
|
+
Requires-Dist: matplotlib
|
|
18
19
|
Requires-Dist: numpy<2.2
|
|
19
20
|
Requires-Dist: pandas
|
|
20
21
|
Requires-Dist: plotly<6.0
|
|
21
|
-
Requires-Dist: requests
|
|
22
|
+
Requires-Dist: requests
|
|
23
|
+
Requires-Dist: ripser>=0.6.12
|
|
22
24
|
Requires-Dist: scikit-learn
|
|
25
|
+
Requires-Dist: scipy<1.16
|
|
23
26
|
Requires-Dist: svgpathtools
|
|
24
|
-
Requires-Dist: tqdm
|
|
27
|
+
Requires-Dist: tqdm
|
|
25
28
|
Requires-Dist: jupyterlab>=4.3.6 ; extra == "jupyter"
|
|
29
|
+
Requires-Dist: anndata2ri>=1.3 ; extra == "r"
|
|
30
|
+
Requires-Dist: rpy2>=3.5 ; extra == "r"
|
|
26
31
|
Requires-Dist: scanpy[leiden, skmisc]>=1.10 ; extra == "scanpy"
|
|
27
32
|
Requires-Dist: pytest>=8.3.4 ; extra == "test"
|
|
28
33
|
Project-URL: Bug Tracker, https://github.com/umbibio/sclab/issues
|
|
@@ -31,6 +36,7 @@ Project-URL: Documentation, https://github.com/umbibio/sclab/docs
|
|
|
31
36
|
Project-URL: Homepage, https://github.com/umbibio/sclab
|
|
32
37
|
Project-URL: Repository, https://github.com/umbibio/sclab.git
|
|
33
38
|
Provides-Extra: jupyter
|
|
39
|
+
Provides-Extra: r
|
|
34
40
|
Provides-Extra: scanpy
|
|
35
41
|
Provides-Extra: test
|
|
36
42
|
|
|
@@ -60,7 +66,6 @@ Open a Jupyter Notebook and run the following:
|
|
|
60
66
|
```python
|
|
61
67
|
from IPython.display import display
|
|
62
68
|
from sclab import SCLabDashboard
|
|
63
|
-
from sclab.examples.processor_steps import QC, Preprocess, PCA, Neighbors, UMAP, Cluster
|
|
64
69
|
import scanpy as sc
|
|
65
70
|
|
|
66
71
|
# Load your data
|
|
@@ -68,8 +73,6 @@ adata = sc.read_10x_h5("your_data.h5")
|
|
|
68
73
|
|
|
69
74
|
# Create dashboard
|
|
70
75
|
dashboard = SCLabDashboard(adata, name="My Analysis")
|
|
71
|
-
# Add desired processing steps to the interface
|
|
72
|
-
dashboard.pr.add_steps({"Processing": [QC, Preprocess, PCA, Neighbors, UMAP, Cluster]})
|
|
73
76
|
|
|
74
77
|
# Display dashboard
|
|
75
78
|
display(dashboard)
|
|
@@ -79,8 +82,10 @@ display(dashboard)
|
|
|
79
82
|
# dashboard.pl # Plotter
|
|
80
83
|
# dashboard.pr # Processor
|
|
81
84
|
|
|
82
|
-
# the
|
|
85
|
+
# the active AnnData object is found within the dataset object:
|
|
83
86
|
# dashboard.ds.adata
|
|
87
|
+
|
|
88
|
+
# by default, the dashboard will update the loaded AnnData object in-place
|
|
84
89
|
```
|
|
85
90
|
|
|
86
91
|
## Components
|
|
@@ -89,6 +94,7 @@ display(dashboard)
|
|
|
89
94
|
|
|
90
95
|
The main interface that integrates all components with a tabbed layout:
|
|
91
96
|
- Main graph for visualizations
|
|
97
|
+
- Results panel
|
|
92
98
|
- Observations table
|
|
93
99
|
- Genes table
|
|
94
100
|
- Event logs
|
|
@@ -24,7 +24,6 @@ Open a Jupyter Notebook and run the following:
|
|
|
24
24
|
```python
|
|
25
25
|
from IPython.display import display
|
|
26
26
|
from sclab import SCLabDashboard
|
|
27
|
-
from sclab.examples.processor_steps import QC, Preprocess, PCA, Neighbors, UMAP, Cluster
|
|
28
27
|
import scanpy as sc
|
|
29
28
|
|
|
30
29
|
# Load your data
|
|
@@ -32,8 +31,6 @@ adata = sc.read_10x_h5("your_data.h5")
|
|
|
32
31
|
|
|
33
32
|
# Create dashboard
|
|
34
33
|
dashboard = SCLabDashboard(adata, name="My Analysis")
|
|
35
|
-
# Add desired processing steps to the interface
|
|
36
|
-
dashboard.pr.add_steps({"Processing": [QC, Preprocess, PCA, Neighbors, UMAP, Cluster]})
|
|
37
34
|
|
|
38
35
|
# Display dashboard
|
|
39
36
|
display(dashboard)
|
|
@@ -43,8 +40,10 @@ display(dashboard)
|
|
|
43
40
|
# dashboard.pl # Plotter
|
|
44
41
|
# dashboard.pr # Processor
|
|
45
42
|
|
|
46
|
-
# the
|
|
43
|
+
# the active AnnData object is found within the dataset object:
|
|
47
44
|
# dashboard.ds.adata
|
|
45
|
+
|
|
46
|
+
# by default, the dashboard will update the loaded AnnData object in-place
|
|
48
47
|
```
|
|
49
48
|
|
|
50
49
|
## Components
|
|
@@ -53,6 +52,7 @@ display(dashboard)
|
|
|
53
52
|
|
|
54
53
|
The main interface that integrates all components with a tabbed layout:
|
|
55
54
|
- Main graph for visualizations
|
|
55
|
+
- Results panel
|
|
56
56
|
- Observations table
|
|
57
57
|
- Genes table
|
|
58
58
|
- Event logs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sclab"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.4"
|
|
4
4
|
description = "sclab"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -18,14 +18,17 @@ dependencies = [
|
|
|
18
18
|
"anndata",
|
|
19
19
|
"anywidget",
|
|
20
20
|
"ipywidgets",
|
|
21
|
-
"itables",
|
|
21
|
+
"itables<2.4",
|
|
22
|
+
"matplotlib",
|
|
22
23
|
"numpy<2.2",
|
|
23
24
|
"pandas",
|
|
24
25
|
"plotly<6.0",
|
|
25
|
-
"requests
|
|
26
|
+
"requests",
|
|
27
|
+
"ripser>=0.6.12",
|
|
26
28
|
"scikit-learn",
|
|
29
|
+
"scipy<1.16",
|
|
27
30
|
"svgpathtools",
|
|
28
|
-
"tqdm
|
|
31
|
+
"tqdm",
|
|
29
32
|
]
|
|
30
33
|
|
|
31
34
|
[project.urls]
|
|
@@ -36,9 +39,10 @@ dependencies = [
|
|
|
36
39
|
"Changelog" = "https://github.com/umbibio/sclab/blob/main/CHANGELOG.md"
|
|
37
40
|
|
|
38
41
|
[project.optional-dependencies]
|
|
39
|
-
test = ["pytest>=8.3.4"]
|
|
40
|
-
scanpy = ["scanpy[leiden,skmisc]>=1.10"]
|
|
41
42
|
jupyter = ["jupyterlab>=4.3.6"]
|
|
43
|
+
r = ["anndata2ri>=1.3", "rpy2>=3.5"]
|
|
44
|
+
scanpy = ["scanpy[leiden,skmisc]>=1.10"]
|
|
45
|
+
test = ["pytest>=8.3.4"]
|
|
42
46
|
|
|
43
47
|
[build-system]
|
|
44
48
|
requires = ["flit_core>=3.2,<4"]
|
|
@@ -48,6 +52,9 @@ build-backend = "flit_core.buildapi"
|
|
|
48
52
|
name = "sclab"
|
|
49
53
|
source = "src/sclab"
|
|
50
54
|
|
|
55
|
+
[tool.uv.sources]
|
|
56
|
+
sclab-tools = { path = "../sclab-tools" }
|
|
57
|
+
|
|
51
58
|
[dependency-groups]
|
|
52
59
|
dev = [
|
|
53
60
|
"bump-my-version>=0.31.1",
|
|
@@ -58,6 +65,8 @@ dev = [
|
|
|
58
65
|
"mkdocs>=1.6.1",
|
|
59
66
|
"mkdocs-material>=9.6.3",
|
|
60
67
|
"mkdocs-jupyter>=0.25.1",
|
|
61
|
-
"scanpy[leiden]>=1.10.4",
|
|
68
|
+
"scanpy[harmony,leiden,scrublet,skmisc]>=1.10.4",
|
|
62
69
|
"jupyterlab>=4.3.6",
|
|
70
|
+
"sclab-tools",
|
|
71
|
+
"scrublet>=0.2.3",
|
|
63
72
|
]
|
|
@@ -58,12 +58,6 @@ def load_adata_from_url(
|
|
|
58
58
|
assert is_valid_url(url), "URL is not valid"
|
|
59
59
|
url_path = Path(urlparse(url).path)
|
|
60
60
|
|
|
61
|
-
if url_path.suffix == ".h5":
|
|
62
|
-
try:
|
|
63
|
-
import scanpy as sc
|
|
64
|
-
except ImportError:
|
|
65
|
-
raise ImportError("Please install scanpy: `pip install scanpy`")
|
|
66
|
-
|
|
67
61
|
file_content = fetch_file(url, progress=progress)
|
|
68
62
|
match url_path.suffix:
|
|
69
63
|
case ".h5":
|
|
@@ -170,7 +170,11 @@ class DataLoader(VBox):
|
|
|
170
170
|
self.upload = FileUpload(layout=Layout(width="200px"))
|
|
171
171
|
self.upload_info = Output(layout=Layout(width="95%"))
|
|
172
172
|
self.upload_row = HBox(
|
|
173
|
-
[
|
|
173
|
+
[
|
|
174
|
+
self.upload_row_label,
|
|
175
|
+
self.upload,
|
|
176
|
+
self.upload_info,
|
|
177
|
+
],
|
|
174
178
|
layout=Layout(width="100%"),
|
|
175
179
|
)
|
|
176
180
|
self.upload.observe(self.on_upload, "value")
|
|
@@ -187,7 +191,7 @@ class DataLoader(VBox):
|
|
|
187
191
|
user_f_locals = inspect.stack()[2].frame.f_locals
|
|
188
192
|
self.defined_adatas_dict = {}
|
|
189
193
|
for name, variable_type in [(k, type(v)) for k, v in user_f_locals.items()]:
|
|
190
|
-
if variable_type is AnnData:
|
|
194
|
+
if variable_type is AnnData and not name.startswith("_"):
|
|
191
195
|
self.defined_adatas_dict[name] = user_f_locals[name]
|
|
192
196
|
|
|
193
197
|
self.defined_adatas_label = Label(
|
|
@@ -213,8 +217,8 @@ class DataLoader(VBox):
|
|
|
213
217
|
VBox.__init__(
|
|
214
218
|
self,
|
|
215
219
|
[
|
|
216
|
-
self.url_row,
|
|
217
|
-
self.upload_row,
|
|
220
|
+
# self.url_row,
|
|
221
|
+
# self.upload_row,
|
|
218
222
|
self.defined_adatas_row,
|
|
219
223
|
self.progress_output,
|
|
220
224
|
],
|
|
@@ -233,6 +237,8 @@ class DataLoader(VBox):
|
|
|
233
237
|
self.adata = adata
|
|
234
238
|
|
|
235
239
|
def on_upload(self, *args, **kwargs):
|
|
240
|
+
import tempfile
|
|
241
|
+
|
|
236
242
|
from .scanpy.readwrite import read_10x_h5, read_h5ad
|
|
237
243
|
|
|
238
244
|
files = self.upload.value
|
|
@@ -256,14 +262,20 @@ class DataLoader(VBox):
|
|
|
256
262
|
|
|
257
263
|
match path.suffix:
|
|
258
264
|
case ".h5":
|
|
259
|
-
|
|
265
|
+
with tempfile.NamedTemporaryFile(suffix=".h5") as tmp:
|
|
266
|
+
tmp.write(contents.getbuffer())
|
|
267
|
+
tmp.flush()
|
|
268
|
+
adata = read_10x_h5(tmp.name)
|
|
260
269
|
case ".h5ad":
|
|
261
|
-
|
|
270
|
+
with tempfile.NamedTemporaryFile(suffix=".h5ad") as tmp:
|
|
271
|
+
tmp.write(contents.getbuffer())
|
|
272
|
+
tmp.flush()
|
|
273
|
+
adata = read_h5ad(tmp.name)
|
|
262
274
|
case _:
|
|
263
275
|
self.upload_info.clear_output()
|
|
264
276
|
with self.upload_info:
|
|
265
277
|
print(f"`{filename}` is not valid")
|
|
266
|
-
print("Please upload a 10x h5 or h5ad file")
|
|
278
|
+
print("Please upload a 10x .h5 or .h5ad file")
|
|
267
279
|
return
|
|
268
280
|
|
|
269
281
|
if var_names in adata.var:
|
|
@@ -365,7 +365,7 @@ class SCLabDataset(EventClient):
|
|
|
365
365
|
if not index.isin(self.metadata.index).all():
|
|
366
366
|
raise InvalidRowSubset("index contains invalid values")
|
|
367
367
|
|
|
368
|
-
self.adata
|
|
368
|
+
self.adata._inplace_subset_obs(index)
|
|
369
369
|
|
|
370
370
|
self.broker.publish("dset_total_rows_change", self.metadata)
|
|
371
371
|
|
|
@@ -1025,22 +1025,37 @@ class Processor(EventClient):
|
|
|
1025
1025
|
else:
|
|
1026
1026
|
control.value = current_value
|
|
1027
1027
|
|
|
1028
|
-
def dset_anndata_layers_change_callback(self,
|
|
1029
|
-
|
|
1028
|
+
def dset_anndata_layers_change_callback(self, *args, **kwargs):
|
|
1029
|
+
layer_options = {key: key for key in self.dataset.adata.layers.keys()}
|
|
1030
|
+
obsm_options = {key: key for key in self.dataset.adata.obsm.keys()}
|
|
1031
|
+
|
|
1030
1032
|
for control in self.all_controls_list:
|
|
1031
1033
|
if not isinstance(control, Dropdown):
|
|
1032
1034
|
continue
|
|
1033
1035
|
description: str = control.description
|
|
1036
|
+
|
|
1034
1037
|
if description.lower().strip(" :.") == "layer":
|
|
1035
1038
|
current_value = control.value
|
|
1036
|
-
control.options =
|
|
1039
|
+
control.options = layer_options
|
|
1040
|
+
if current_value not in control.options:
|
|
1041
|
+
control.value = None
|
|
1042
|
+
else:
|
|
1043
|
+
control.value = current_value
|
|
1044
|
+
|
|
1045
|
+
if description.lower().strip(" :.") == "use rep":
|
|
1046
|
+
current_value = control.value
|
|
1047
|
+
control.options = {**layer_options, **obsm_options}
|
|
1037
1048
|
if current_value not in control.options:
|
|
1038
1049
|
control.value = None
|
|
1039
1050
|
else:
|
|
1040
1051
|
control.value = current_value
|
|
1041
1052
|
|
|
1042
1053
|
def dset_data_dict_change_callback(self, *args, **kwargs):
|
|
1043
|
-
options =
|
|
1054
|
+
options = [
|
|
1055
|
+
*self.dataset.adata.layers.keys(),
|
|
1056
|
+
*self.dataset.adata.obsm.keys(),
|
|
1057
|
+
]
|
|
1058
|
+
options = {v: v for v in options}
|
|
1044
1059
|
for control in self.all_controls_list:
|
|
1045
1060
|
if not isinstance(control, Dropdown):
|
|
1046
1061
|
continue
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
from ipywidgets import
|
|
1
|
+
from ipywidgets import Box, Dropdown, Layout, Stack, VBox, link
|
|
2
2
|
|
|
3
3
|
from sclab.event import EventBroker, EventClient
|
|
4
4
|
|
|
5
|
+
# Create a layout with a bottom border to act as the horizontal line
|
|
6
|
+
hr_layout = Layout(
|
|
7
|
+
border="1px solid black", # 1px width, solid style, black color
|
|
8
|
+
margin="10px 0", # Add margin for spacing above and below
|
|
9
|
+
width="100%", # Extend the line across the full width
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Create a Box widget with the styled layout
|
|
13
|
+
hr = Box(layout=hr_layout)
|
|
14
|
+
|
|
5
15
|
|
|
6
16
|
class _Results:
|
|
7
17
|
namespace: str
|
|
8
18
|
|
|
9
19
|
|
|
10
|
-
class ResultsPanel(
|
|
11
|
-
available_results:
|
|
20
|
+
class ResultsPanel(VBox, EventClient):
|
|
21
|
+
available_results: Dropdown
|
|
12
22
|
results_stack: Stack
|
|
13
23
|
|
|
14
24
|
events: list[str] = [
|
|
@@ -22,7 +32,7 @@ class ResultsPanel(GridBox, EventClient):
|
|
|
22
32
|
):
|
|
23
33
|
EventClient.__init__(self, broker)
|
|
24
34
|
|
|
25
|
-
self.available_results =
|
|
35
|
+
self.available_results = Dropdown(options={}, description="Category")
|
|
26
36
|
self.results_stack = Stack([])
|
|
27
37
|
|
|
28
38
|
link(
|
|
@@ -30,15 +40,19 @@ class ResultsPanel(GridBox, EventClient):
|
|
|
30
40
|
(self.results_stack, "selected_index"),
|
|
31
41
|
)
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
VBox.__init__(
|
|
34
44
|
self,
|
|
35
|
-
[
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
[
|
|
46
|
+
self.available_results,
|
|
47
|
+
hr,
|
|
48
|
+
self.results_stack,
|
|
49
|
+
],
|
|
50
|
+
# layout=Layout(
|
|
51
|
+
# width="100%",
|
|
52
|
+
# grid_template_columns="150px auto",
|
|
53
|
+
# grid_template_areas=""" "available-results selected-results_stack" """,
|
|
54
|
+
# border="0px solid black",
|
|
55
|
+
# ),
|
|
42
56
|
)
|
|
43
57
|
|
|
44
58
|
def add_result(self, results: _Results):
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from ._cluster import Cluster
|
|
2
2
|
from ._differential_expression import DifferentialExpression
|
|
3
|
+
from ._doublet_detection import DoubletDetection
|
|
4
|
+
from ._gene_expression import GeneExpression
|
|
5
|
+
from ._integration import Integration
|
|
3
6
|
from ._neighbors import Neighbors
|
|
4
7
|
from ._pca import PCA
|
|
5
8
|
from ._preprocess import Preprocess
|
|
@@ -10,8 +13,11 @@ __all__ = [
|
|
|
10
13
|
"QC",
|
|
11
14
|
"Preprocess",
|
|
12
15
|
"PCA",
|
|
16
|
+
"Integration",
|
|
13
17
|
"Neighbors",
|
|
14
18
|
"UMAP",
|
|
15
19
|
"Cluster",
|
|
20
|
+
"DoubletDetection",
|
|
21
|
+
"GeneExpression",
|
|
16
22
|
"DifferentialExpression",
|
|
17
23
|
]
|
|
@@ -25,7 +25,7 @@ class DifferentialExpressionResults(VBox):
|
|
|
25
25
|
|
|
26
26
|
def __init__(self, dataset: SCLabDataset):
|
|
27
27
|
self.dataset = dataset
|
|
28
|
-
self.result_selector = Dropdown()
|
|
28
|
+
self.result_selector = Dropdown(description="Analysis Name")
|
|
29
29
|
self.group_selector = ToggleButtons()
|
|
30
30
|
self.table_output = Output()
|
|
31
31
|
|
|
@@ -198,6 +198,7 @@ class DifferentialExpression(ProcessorStepBase):
|
|
|
198
198
|
reference=reference,
|
|
199
199
|
layer=layer,
|
|
200
200
|
key_added=key_added,
|
|
201
|
+
pts=True,
|
|
201
202
|
)
|
|
202
203
|
|
|
203
204
|
self.results.sync_results_list(focus_result=key_added)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from ipywidgets import Dropdown
|
|
2
|
+
|
|
3
|
+
from sclab.dataset.processor import Processor
|
|
4
|
+
from sclab.dataset.processor.step import ProcessorStepBase
|
|
5
|
+
from sclab.tools.doublet_detection import scrublet
|
|
6
|
+
|
|
7
|
+
# from sclab.tools.doublet_detection import doubletdetection
|
|
8
|
+
# from sclab.tools.doublet_detection import scdblfinder
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DoubletDetection(ProcessorStepBase):
|
|
12
|
+
parent: Processor
|
|
13
|
+
name: str = "doublet_detection"
|
|
14
|
+
description: str = "Doublet Detection"
|
|
15
|
+
|
|
16
|
+
def __init__(self, parent: Processor) -> None:
|
|
17
|
+
variable_controls = dict(
|
|
18
|
+
layer=Dropdown(
|
|
19
|
+
options=tuple(parent.dataset.adata.layers.keys()),
|
|
20
|
+
value=None,
|
|
21
|
+
description="Layer",
|
|
22
|
+
),
|
|
23
|
+
flavor=Dropdown(
|
|
24
|
+
options=[
|
|
25
|
+
"scrublet",
|
|
26
|
+
# "doubletdetection",
|
|
27
|
+
# "scDblFinder",
|
|
28
|
+
],
|
|
29
|
+
description="Flavor",
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
super().__init__(
|
|
34
|
+
parent=parent,
|
|
35
|
+
fixed_params={},
|
|
36
|
+
variable_controls=variable_controls,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def function(self, layer: str, flavor: str):
|
|
40
|
+
adata = self.parent.dataset.adata
|
|
41
|
+
|
|
42
|
+
kvargs = {"adata": adata, "layer": layer, "key_added": flavor}
|
|
43
|
+
|
|
44
|
+
self.broker.std_output.clear_output(wait=False)
|
|
45
|
+
with self.broker.std_output:
|
|
46
|
+
match flavor:
|
|
47
|
+
# case "scDblFinder":
|
|
48
|
+
# scdblfinder(**kvargs, clusters_col="leiden")
|
|
49
|
+
|
|
50
|
+
# case "doubletdetection":
|
|
51
|
+
# doubletdetection(
|
|
52
|
+
# **kvargs,
|
|
53
|
+
# pseudocount=1,
|
|
54
|
+
# clustering_algorithm="leiden",
|
|
55
|
+
# clustering_kwargs=dict(resolution=5.0),
|
|
56
|
+
# )
|
|
57
|
+
|
|
58
|
+
case "scrublet":
|
|
59
|
+
scrublet(**kvargs)
|
|
60
|
+
|
|
61
|
+
case _:
|
|
62
|
+
raise ValueError(f"Unknown flavor: {flavor}")
|
|
63
|
+
|
|
64
|
+
self.broker.publish(
|
|
65
|
+
"dset_metadata_change",
|
|
66
|
+
self.parent.dataset.metadata,
|
|
67
|
+
f"{flavor}_label",
|
|
68
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import plotly.colors as pc
|
|
4
|
+
import plotly.express as px
|
|
5
|
+
from ipywidgets import Combobox, Dropdown
|
|
6
|
+
|
|
7
|
+
from sclab.dataset.processor import Processor
|
|
8
|
+
from sclab.dataset.processor.step import ProcessorStepBase
|
|
9
|
+
|
|
10
|
+
colorscales = list(
|
|
11
|
+
filter(lambda s: "swatch" not in s and not s.startswith("_"), dir(pc.sequential))
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GeneExpression(ProcessorStepBase):
|
|
16
|
+
parent: Processor
|
|
17
|
+
name: str = "gene_expression"
|
|
18
|
+
description: str = "Gene Expression"
|
|
19
|
+
|
|
20
|
+
run_button_description = "Plot Expression"
|
|
21
|
+
|
|
22
|
+
def __init__(self, parent: Processor) -> None:
|
|
23
|
+
df = parent.dataset.metadata.select_dtypes("number")
|
|
24
|
+
df = parent.dataset.metadata
|
|
25
|
+
axis_key_options = {"": None, **{c: c for c in df.columns}}
|
|
26
|
+
|
|
27
|
+
gene_input_options = parent.dataset.adata.var_names
|
|
28
|
+
genes_df = parent.dataset.adata.var
|
|
29
|
+
info_cols = ["name", "symbol", "description"]
|
|
30
|
+
for col in [
|
|
31
|
+
c for c in genes_df.columns if any([s.lower() in c for s in info_cols])
|
|
32
|
+
]:
|
|
33
|
+
new_info = genes_df[col].astype(str).str.replace("nan", "")
|
|
34
|
+
gene_input_options = gene_input_options + " - " + new_info
|
|
35
|
+
|
|
36
|
+
variable_controls = dict(
|
|
37
|
+
gene_input=Combobox(
|
|
38
|
+
placeholder="Type gene name",
|
|
39
|
+
options=gene_input_options.to_list(),
|
|
40
|
+
description="Gene",
|
|
41
|
+
ensure_option=True,
|
|
42
|
+
),
|
|
43
|
+
layer=Dropdown(
|
|
44
|
+
options=tuple(parent.dataset.adata.layers.keys()),
|
|
45
|
+
value=None,
|
|
46
|
+
description="Layer",
|
|
47
|
+
),
|
|
48
|
+
time_key=Dropdown(
|
|
49
|
+
options=axis_key_options,
|
|
50
|
+
value=None,
|
|
51
|
+
description="Horiz. axis",
|
|
52
|
+
),
|
|
53
|
+
colorscale=Dropdown(
|
|
54
|
+
options=colorscales, value="Oryel", description="Col. scale"
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
super().__init__(
|
|
59
|
+
parent=parent,
|
|
60
|
+
fixed_params={},
|
|
61
|
+
variable_controls=variable_controls,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self.variable_controls["gene_input"].observe(self.send_plot, names="value")
|
|
65
|
+
self.variable_controls["layer"].observe(self.send_plot, names="value")
|
|
66
|
+
self.variable_controls["time_key"].observe(self.send_plot, names="value")
|
|
67
|
+
self.variable_controls["colorscale"].observe(self.send_plot, names="value")
|
|
68
|
+
|
|
69
|
+
def function(self, *pargs, **kwargs):
|
|
70
|
+
self.send_plot({})
|
|
71
|
+
|
|
72
|
+
def send_plot(self, change: dict):
|
|
73
|
+
adata = self.parent.dataset.adata
|
|
74
|
+
metadata = self.parent.dataset.metadata
|
|
75
|
+
selected_cells = self.parent.dataset.selected_rows
|
|
76
|
+
|
|
77
|
+
gene_input: str = self.variable_controls["gene_input"].value
|
|
78
|
+
layer: str = self.variable_controls["layer"].value
|
|
79
|
+
time_key: str = self.variable_controls["time_key"].value
|
|
80
|
+
colorscale: str = self.variable_controls["colorscale"].value
|
|
81
|
+
|
|
82
|
+
if gene_input is None or gene_input == "":
|
|
83
|
+
self.update_output("")
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if layer is None or layer == "":
|
|
87
|
+
self.update_output("")
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
gene_id = gene_input.split(" ")[0]
|
|
91
|
+
|
|
92
|
+
if layer == "X":
|
|
93
|
+
X = adata[:, gene_id].X
|
|
94
|
+
else:
|
|
95
|
+
X = adata[:, gene_id].layers[layer]
|
|
96
|
+
|
|
97
|
+
E = np.asarray(X.sum(axis=1)).flatten()
|
|
98
|
+
|
|
99
|
+
self.update_output(f"Showing gene: {gene_id}")
|
|
100
|
+
# self.variable_controls["gene_input"].value = ""
|
|
101
|
+
|
|
102
|
+
df = pd.DataFrame({gene_id: E}, index=adata.obs.index)
|
|
103
|
+
metadata = metadata.join(df)
|
|
104
|
+
if selected_cells.size > 0:
|
|
105
|
+
metadata = metadata.loc[selected_cells]
|
|
106
|
+
|
|
107
|
+
if time_key is None:
|
|
108
|
+
self.broker.publish(
|
|
109
|
+
"dplt_plot_figure_request",
|
|
110
|
+
metadata=metadata,
|
|
111
|
+
colorby=gene_id,
|
|
112
|
+
color_continuous_scale=colorscale,
|
|
113
|
+
)
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
fig = px.scatter(
|
|
117
|
+
metadata,
|
|
118
|
+
x=time_key,
|
|
119
|
+
y=gene_id,
|
|
120
|
+
color=gene_id,
|
|
121
|
+
color_continuous_scale=colorscale,
|
|
122
|
+
hover_name=adata.obs.index,
|
|
123
|
+
title=f"Gene: {gene_id}, Layer: {layer}",
|
|
124
|
+
)
|
|
125
|
+
self.broker.publish("dplt_plot_figure_request", figure=fig)
|