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.
Files changed (93) hide show
  1. {sclab-0.2.2 → sclab-0.3.4}/PKG-INFO +14 -8
  2. {sclab-0.2.2 → sclab-0.3.4}/README.md +4 -4
  3. {sclab-0.2.2 → sclab-0.3.4}/pyproject.toml +16 -7
  4. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/__init__.py +1 -1
  5. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_io.py +0 -6
  6. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_sclab.py +19 -7
  7. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/_dataset.py +1 -1
  8. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/_processor.py +19 -4
  9. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/_results_panel.py +26 -12
  10. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/__init__.py +6 -0
  11. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_differential_expression.py +2 -1
  12. sclab-0.3.4/src/sclab/examples/processor_steps/_doublet_detection.py +68 -0
  13. sclab-0.3.4/src/sclab/examples/processor_steps/_gene_expression.py +125 -0
  14. sclab-0.3.4/src/sclab/examples/processor_steps/_integration.py +116 -0
  15. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_neighbors.py +24 -4
  16. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_pca.py +11 -6
  17. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_preprocess.py +50 -23
  18. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_qc.py +22 -6
  19. sclab-0.3.4/src/sclab/gui/components/__init__.py +7 -0
  20. sclab-0.3.4/src/sclab/gui/components/_guided_pseudotime.py +482 -0
  21. sclab-0.3.4/src/sclab/gui/components/_transfer_metadata.py +186 -0
  22. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/methods/__init__.py +22 -0
  23. sclab-0.3.4/src/sclab/preprocess/__init__.py +26 -0
  24. sclab-0.3.4/src/sclab/preprocess/_cca.py +176 -0
  25. sclab-0.3.4/src/sclab/preprocess/_cca_integrate.py +109 -0
  26. sclab-0.3.4/src/sclab/preprocess/_filter_obs.py +42 -0
  27. sclab-0.3.4/src/sclab/preprocess/_harmony.py +421 -0
  28. sclab-0.3.4/src/sclab/preprocess/_harmony_integrate.py +53 -0
  29. sclab-0.3.4/src/sclab/preprocess/_normalize_weighted.py +65 -0
  30. sclab-0.3.4/src/sclab/preprocess/_pca.py +51 -0
  31. sclab-0.3.4/src/sclab/preprocess/_preprocess.py +155 -0
  32. sclab-0.3.4/src/sclab/preprocess/_qc.py +38 -0
  33. sclab-0.3.4/src/sclab/preprocess/_rpca.py +116 -0
  34. sclab-0.3.4/src/sclab/preprocess/_subset.py +208 -0
  35. sclab-0.3.4/src/sclab/preprocess/_transfer_metadata.py +196 -0
  36. sclab-0.3.4/src/sclab/preprocess/_transform.py +82 -0
  37. sclab-0.3.4/src/sclab/preprocess/_utils.py +96 -0
  38. sclab-0.3.4/src/sclab/scanpy/__init__.py +0 -0
  39. sclab-0.3.4/src/sclab/scanpy/plotting/__init__.py +0 -0
  40. sclab-0.3.4/src/sclab/tools/__init__.py +0 -0
  41. sclab-0.3.4/src/sclab/tools/cellflow/__init__.py +0 -0
  42. sclab-0.3.4/src/sclab/tools/cellflow/density_dynamics/__init__.py +0 -0
  43. sclab-0.3.4/src/sclab/tools/cellflow/density_dynamics/_density_dynamics.py +349 -0
  44. sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/__init__.py +0 -0
  45. sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/_pseudotime.py +336 -0
  46. sclab-0.3.4/src/sclab/tools/cellflow/pseudotime/timeseries.py +226 -0
  47. sclab-0.3.4/src/sclab/tools/cellflow/utils/__init__.py +0 -0
  48. sclab-0.3.4/src/sclab/tools/cellflow/utils/density_nd.py +215 -0
  49. sclab-0.3.4/src/sclab/tools/cellflow/utils/interpolate.py +334 -0
  50. sclab-0.3.4/src/sclab/tools/cellflow/utils/periodic_genes.py +106 -0
  51. sclab-0.3.4/src/sclab/tools/cellflow/utils/smoothen.py +124 -0
  52. sclab-0.3.4/src/sclab/tools/cellflow/utils/times.py +55 -0
  53. sclab-0.3.4/src/sclab/tools/differential_expression/__init__.py +7 -0
  54. sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_edger.py +309 -0
  55. sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_helpers.py +290 -0
  56. sclab-0.3.4/src/sclab/tools/differential_expression/_pseudobulk_limma.py +257 -0
  57. sclab-0.3.4/src/sclab/tools/doublet_detection/__init__.py +5 -0
  58. sclab-0.3.4/src/sclab/tools/doublet_detection/_scrublet.py +64 -0
  59. sclab-0.3.4/src/sclab/tools/embedding/__init__.py +0 -0
  60. sclab-0.3.4/src/sclab/tools/imputation/__init__.py +0 -0
  61. sclab-0.3.4/src/sclab/tools/imputation/_alra.py +135 -0
  62. sclab-0.3.4/src/sclab/tools/labeling/__init__.py +6 -0
  63. sclab-0.3.4/src/sclab/tools/labeling/sctype.py +233 -0
  64. sclab-0.3.4/src/sclab/tools/utils/__init__.py +5 -0
  65. sclab-0.3.4/src/sclab/tools/utils/_aggregate_and_filter.py +290 -0
  66. sclab-0.3.4/src/sclab/utils/__init__.py +5 -0
  67. sclab-0.3.4/src/sclab/utils/_write_excel.py +510 -0
  68. {sclab-0.2.2 → sclab-0.3.4}/LICENSE +0 -0
  69. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/_methods_registry.py +0 -0
  70. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/__init__.py +0 -0
  71. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/_exceptions.py +0 -0
  72. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/__init__.py +0 -0
  73. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_controls.py +0 -0
  74. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_plotter.py +0 -0
  75. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/plotter/_utils.py +0 -0
  76. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/__init__.py +0 -0
  77. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/__init__.py +0 -0
  78. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/_basic_processor_step.py +0 -0
  79. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/dataset/processor/step/_processor_step_base.py +0 -0
  80. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/__init__.py +0 -0
  81. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_broker.py +0 -0
  82. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_client.py +0 -0
  83. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/event/_utils.py +0 -0
  84. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/__init__.py +0 -0
  85. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_cluster.py +0 -0
  86. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/examples/processor_steps/_umap.py +0 -0
  87. {sclab-0.2.2/src/sclab/scanpy/plotting → sclab-0.3.4/src/sclab/gui}/__init__.py +0 -0
  88. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/_compat.py +0 -0
  89. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/_settings.py +0 -0
  90. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/logging.py +0 -0
  91. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/plotting/_rcmod.py +0 -0
  92. {sclab-0.2.2 → sclab-0.3.4}/src/sclab/scanpy/plotting/palettes.py +0 -0
  93. {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.2.2
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>=2.32.3
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>=4.67.1
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 resulting AnnData object is found within the dataset object:
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 resulting AnnData object is found within the dataset object:
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.2.2"
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>=2.32.3",
26
+ "requests",
27
+ "ripser>=0.6.12",
26
28
  "scikit-learn",
29
+ "scipy<1.16",
27
30
  "svgpathtools",
28
- "tqdm>=4.67.1",
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
  ]
@@ -6,4 +6,4 @@ __all__ = [
6
6
  "SCLabDashboard",
7
7
  ]
8
8
 
9
- __version__ = "0.2.2"
9
+ __version__ = "0.3.4"
@@ -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
- [self.upload_row_label, self.upload, self.upload_info],
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
- adata = read_10x_h5(contents)
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
- adata = read_h5ad(contents)
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 = self.adata[index].copy()
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, layers):
1029
- options = {layer: layer for layer in layers}
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 = 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 = {v: v for v in self.dataset.adata.obsm.keys()}
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 GridBox, Layout, Stack, ToggleButtons, link
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(GridBox, EventClient):
11
- available_results: ToggleButtons
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 = ToggleButtons(options={})
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
- GridBox.__init__(
43
+ VBox.__init__(
34
44
  self,
35
- [self.available_results, self.results_stack],
36
- layout=Layout(
37
- width="100%",
38
- grid_template_columns="150px auto",
39
- grid_template_areas=""" "available-results selected-results_stack" """,
40
- border="0px solid black",
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)