sclab 0.1.7__py3-none-any.whl → 0.3.4__py3-none-any.whl

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 (80) hide show
  1. sclab/__init__.py +3 -1
  2. sclab/_io.py +83 -12
  3. sclab/_methods_registry.py +65 -0
  4. sclab/_sclab.py +241 -21
  5. sclab/dataset/_dataset.py +4 -6
  6. sclab/dataset/processor/_processor.py +41 -19
  7. sclab/dataset/processor/_results_panel.py +94 -0
  8. sclab/dataset/processor/step/_processor_step_base.py +12 -6
  9. sclab/examples/processor_steps/__init__.py +8 -0
  10. sclab/examples/processor_steps/_cluster.py +2 -2
  11. sclab/examples/processor_steps/_differential_expression.py +329 -0
  12. sclab/examples/processor_steps/_doublet_detection.py +68 -0
  13. sclab/examples/processor_steps/_gene_expression.py +125 -0
  14. sclab/examples/processor_steps/_integration.py +116 -0
  15. sclab/examples/processor_steps/_neighbors.py +26 -6
  16. sclab/examples/processor_steps/_pca.py +13 -8
  17. sclab/examples/processor_steps/_preprocess.py +52 -25
  18. sclab/examples/processor_steps/_qc.py +24 -8
  19. sclab/examples/processor_steps/_umap.py +2 -2
  20. sclab/gui/__init__.py +0 -0
  21. sclab/gui/components/__init__.py +7 -0
  22. sclab/gui/components/_guided_pseudotime.py +482 -0
  23. sclab/gui/components/_transfer_metadata.py +186 -0
  24. sclab/methods/__init__.py +50 -0
  25. sclab/preprocess/__init__.py +26 -0
  26. sclab/preprocess/_cca.py +176 -0
  27. sclab/preprocess/_cca_integrate.py +109 -0
  28. sclab/preprocess/_filter_obs.py +42 -0
  29. sclab/preprocess/_harmony.py +421 -0
  30. sclab/preprocess/_harmony_integrate.py +53 -0
  31. sclab/preprocess/_normalize_weighted.py +65 -0
  32. sclab/preprocess/_pca.py +51 -0
  33. sclab/preprocess/_preprocess.py +155 -0
  34. sclab/preprocess/_qc.py +38 -0
  35. sclab/preprocess/_rpca.py +116 -0
  36. sclab/preprocess/_subset.py +208 -0
  37. sclab/preprocess/_transfer_metadata.py +196 -0
  38. sclab/preprocess/_transform.py +82 -0
  39. sclab/preprocess/_utils.py +96 -0
  40. sclab/scanpy/__init__.py +0 -0
  41. sclab/scanpy/_compat.py +92 -0
  42. sclab/scanpy/_settings.py +526 -0
  43. sclab/scanpy/logging.py +290 -0
  44. sclab/scanpy/plotting/__init__.py +0 -0
  45. sclab/scanpy/plotting/_rcmod.py +73 -0
  46. sclab/scanpy/plotting/palettes.py +221 -0
  47. sclab/scanpy/readwrite.py +1108 -0
  48. sclab/tools/__init__.py +0 -0
  49. sclab/tools/cellflow/__init__.py +0 -0
  50. sclab/tools/cellflow/density_dynamics/__init__.py +0 -0
  51. sclab/tools/cellflow/density_dynamics/_density_dynamics.py +349 -0
  52. sclab/tools/cellflow/pseudotime/__init__.py +0 -0
  53. sclab/tools/cellflow/pseudotime/_pseudotime.py +336 -0
  54. sclab/tools/cellflow/pseudotime/timeseries.py +226 -0
  55. sclab/tools/cellflow/utils/__init__.py +0 -0
  56. sclab/tools/cellflow/utils/density_nd.py +215 -0
  57. sclab/tools/cellflow/utils/interpolate.py +334 -0
  58. sclab/tools/cellflow/utils/periodic_genes.py +106 -0
  59. sclab/tools/cellflow/utils/smoothen.py +124 -0
  60. sclab/tools/cellflow/utils/times.py +55 -0
  61. sclab/tools/differential_expression/__init__.py +7 -0
  62. sclab/tools/differential_expression/_pseudobulk_edger.py +309 -0
  63. sclab/tools/differential_expression/_pseudobulk_helpers.py +290 -0
  64. sclab/tools/differential_expression/_pseudobulk_limma.py +257 -0
  65. sclab/tools/doublet_detection/__init__.py +5 -0
  66. sclab/tools/doublet_detection/_scrublet.py +64 -0
  67. sclab/tools/embedding/__init__.py +0 -0
  68. sclab/tools/imputation/__init__.py +0 -0
  69. sclab/tools/imputation/_alra.py +135 -0
  70. sclab/tools/labeling/__init__.py +6 -0
  71. sclab/tools/labeling/sctype.py +233 -0
  72. sclab/tools/utils/__init__.py +5 -0
  73. sclab/tools/utils/_aggregate_and_filter.py +290 -0
  74. sclab/utils/__init__.py +5 -0
  75. sclab/utils/_write_excel.py +510 -0
  76. {sclab-0.1.7.dist-info → sclab-0.3.4.dist-info}/METADATA +29 -12
  77. sclab-0.3.4.dist-info/RECORD +93 -0
  78. {sclab-0.1.7.dist-info → sclab-0.3.4.dist-info}/WHEEL +1 -1
  79. sclab-0.3.4.dist-info/licenses/LICENSE +29 -0
  80. sclab-0.1.7.dist-info/RECORD +0 -30
@@ -0,0 +1,94 @@
1
+ from ipywidgets import Box, Dropdown, Layout, Stack, VBox, link
2
+
3
+ from sclab.event import EventBroker, EventClient
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
+
15
+
16
+ class _Results:
17
+ namespace: str
18
+
19
+
20
+ class ResultsPanel(VBox, EventClient):
21
+ available_results: Dropdown
22
+ results_stack: Stack
23
+
24
+ events: list[str] = [
25
+ # "rslt_add_result",
26
+ # "rslt_remove_result",
27
+ ]
28
+
29
+ def __init__(
30
+ self,
31
+ broker: EventBroker,
32
+ ):
33
+ EventClient.__init__(self, broker)
34
+
35
+ self.available_results = Dropdown(options={}, description="Category")
36
+ self.results_stack = Stack([])
37
+
38
+ link(
39
+ (self.available_results, "value"),
40
+ (self.results_stack, "selected_index"),
41
+ )
42
+
43
+ VBox.__init__(
44
+ self,
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
+ # ),
56
+ )
57
+
58
+ def add_result(self, results: _Results):
59
+ current_stack = list(self.results_stack.children)
60
+ namespace = results.namespace
61
+
62
+ options: dict[str, int] = self.available_results.options
63
+ options = options.copy()
64
+ idx = options.get(namespace, len(options))
65
+ options[namespace] = idx
66
+
67
+ if len(current_stack) < idx + 1:
68
+ current_stack.append(results)
69
+ else:
70
+ current_stack[idx] = results
71
+
72
+ self.results_stack.children = tuple(current_stack)
73
+ self.available_results.options = options
74
+
75
+ def remove_result(self, name: str):
76
+ options: dict[str, int] = self.available_results.options
77
+ options = options.copy()
78
+ idx = options.pop(name)
79
+
80
+ current_stack = list(self.results_stack.children)
81
+ current_stack.pop(idx)
82
+
83
+ current_selection = self.results_stack.selected_index
84
+ if (
85
+ current_selection is not None
86
+ and current_selection > 0
87
+ and current_selection == idx
88
+ ):
89
+ idx = current_selection - 1
90
+ self.results_stack.selected_index = idx
91
+
92
+ self.results_stack.children = tuple(current_stack)
93
+ self.available_results.options = options
94
+ self.available_results.value = idx
@@ -8,33 +8,36 @@ from ipywidgets.widgets.widget_description import DescriptionWidget
8
8
 
9
9
  from ....event import EventClient
10
10
  from .._processor import Processor
11
+ from .._results_panel import _Results
11
12
 
12
13
 
13
14
  class ProcessorStepBase(EventClient):
14
15
  events: list[str] = None
15
16
  parent: Processor
16
- name: str
17
- description: str
17
+ name: str = None
18
+ description: str = None
18
19
  fixed_params: dict[str, Any]
19
20
  variable_controls: dict[str, DescriptionWidget | ValueWidget]
20
21
  output: Output
21
22
  run_button: Button
22
23
  controls_list: list[DescriptionWidget | ValueWidget | Button]
23
24
  controls: VBox
25
+ results: _Results | None
26
+ order: int = 1000
24
27
 
25
28
  run_button_description = "Run"
26
29
 
27
30
  def __init__(
28
31
  self,
29
32
  parent: Processor,
30
- name: str,
31
- description: str,
32
33
  fixed_params: dict[str, Any],
33
34
  variable_controls: dict[str, DescriptionWidget | ValueWidget],
35
+ results: _Results | None = None,
34
36
  ):
37
+ assert self.name
38
+ assert self.description
39
+
35
40
  self.parent = parent
36
- self.name = name
37
- self.description = description
38
41
  self.fixed_params = fixed_params
39
42
  self.variable_controls = variable_controls
40
43
 
@@ -56,6 +59,9 @@ class ProcessorStepBase(EventClient):
56
59
  ]
57
60
  self.make_controls()
58
61
 
62
+ if results is not None:
63
+ self.results = results
64
+ parent.results_panel.add_result(self.results)
59
65
  super().__init__(parent.broker)
60
66
 
61
67
  def make_controls(self):
@@ -1,4 +1,8 @@
1
1
  from ._cluster import Cluster
2
+ from ._differential_expression import DifferentialExpression
3
+ from ._doublet_detection import DoubletDetection
4
+ from ._gene_expression import GeneExpression
5
+ from ._integration import Integration
2
6
  from ._neighbors import Neighbors
3
7
  from ._pca import PCA
4
8
  from ._preprocess import Preprocess
@@ -9,7 +13,11 @@ __all__ = [
9
13
  "QC",
10
14
  "Preprocess",
11
15
  "PCA",
16
+ "Integration",
12
17
  "Neighbors",
13
18
  "UMAP",
14
19
  "Cluster",
20
+ "DoubletDetection",
21
+ "GeneExpression",
22
+ "DifferentialExpression",
15
23
  ]
@@ -6,6 +6,8 @@ from sclab.dataset.processor.step import ProcessorStepBase
6
6
 
7
7
  class Cluster(ProcessorStepBase):
8
8
  parent: Processor
9
+ name: str = "cluster"
10
+ description: str = "Cluster"
9
11
 
10
12
  def __init__(self, parent: Processor) -> None:
11
13
  try:
@@ -21,8 +23,6 @@ class Cluster(ProcessorStepBase):
21
23
 
22
24
  super().__init__(
23
25
  parent=parent,
24
- name="cluster",
25
- description="Cluster",
26
26
  fixed_params={},
27
27
  variable_controls=variable_controls,
28
28
  )
@@ -0,0 +1,329 @@
1
+ from typing import Any, Iterable, Literal
2
+
3
+ import itables
4
+ import numpy as np
5
+ import pandas as pd
6
+ from anndata import AnnData
7
+ from IPython.display import Markdown, display
8
+ from ipywidgets import Dropdown, Output, SelectMultiple, Text, ToggleButtons
9
+ from ipywidgets.widgets.valuewidget import ValueWidget
10
+ from ipywidgets.widgets.widget_box import VBox
11
+ from ipywidgets.widgets.widget_description import DescriptionWidget
12
+ from packaging.version import Version
13
+
14
+ from sclab.dataset import SCLabDataset
15
+ from sclab.dataset.processor import Processor
16
+ from sclab.dataset.processor.step import ProcessorStepBase
17
+
18
+
19
+ class DifferentialExpressionResults(VBox):
20
+ dataset: SCLabDataset
21
+ result_selector: Dropdown
22
+ group_selector: ToggleButtons
23
+ table_output: Output
24
+ namespace: str = "differential_expression"
25
+
26
+ def __init__(self, dataset: SCLabDataset):
27
+ self.dataset = dataset
28
+ self.result_selector = Dropdown(description="Analysis Name")
29
+ self.group_selector = ToggleButtons()
30
+ self.table_output = Output()
31
+
32
+ self.result_selector.observe(self._update_group_selector, "value")
33
+ self.result_selector.observe(self._update_table, "value")
34
+ self.group_selector.observe(self._update_table, "value")
35
+
36
+ super().__init__(
37
+ [
38
+ self.result_selector,
39
+ self.group_selector,
40
+ self.table_output,
41
+ ]
42
+ )
43
+
44
+ self.sync_results_list()
45
+
46
+ def sync_results_list(self, focus_result: str | None = None):
47
+ adata = self.dataset.adata
48
+ uns: dict[str, Any] = adata.uns
49
+ current_selection = self.result_selector.value
50
+ new_options = tuple(filter(lambda x: x.startswith(self.namespace), uns.keys()))
51
+
52
+ if focus_result is not None and focus_result in new_options:
53
+ current_selection = focus_result
54
+ elif current_selection not in new_options:
55
+ current_selection = None
56
+
57
+ self.result_selector.options = new_options
58
+ self.result_selector.value = current_selection
59
+
60
+ def _update_group_selector(self, *args, **kwargs):
61
+ selected_result = self.result_selector.value
62
+ uns: dict[str, Any] = self.dataset.adata.uns
63
+ gene_names: np.rec.recarray = uns[selected_result]["names"]
64
+ self.group_selector.options = ("all",) + gene_names.dtype.names
65
+ self.group_selector.value = "all"
66
+
67
+ def _update_table(self, *args, **kwargs):
68
+ selected_result = self.result_selector.value
69
+ selected_group = self.group_selector.value
70
+
71
+ adata = self.dataset.adata
72
+ params = adata.uns[selected_result]["params"]
73
+
74
+ groupby = params["groupby"]
75
+ reference = params["reference"]
76
+ table_name = f"{selected_result}_by_{groupby}_{selected_group}_vs_{reference}"
77
+
78
+ params_text = "Parameters:\n "
79
+ params_text += "\n ".join(f"{k}: {v}" for k, v in params.items())
80
+ params_text = f"```\n{params_text}\n```"
81
+
82
+ if "gene_name" in adata.var:
83
+ gene_symbols = "gene_name"
84
+ elif "name" in adata.var:
85
+ gene_symbols = "name"
86
+ elif "gene_symbol" in adata.var:
87
+ gene_symbols = "gene_symbol"
88
+ elif "symbol" in adata.var:
89
+ gene_symbols = "symbol"
90
+ else:
91
+ gene_symbols = None
92
+
93
+ group = selected_group if selected_group != "all" else None
94
+ df = _rank_genes_groups_df(
95
+ adata, group=group, key=selected_result, gene_symbols=gene_symbols
96
+ )
97
+
98
+ self.table_output.clear_output()
99
+ with self.table_output:
100
+ display(Markdown(f"## {table_name}"))
101
+ itables.show(
102
+ df,
103
+ buttons=[
104
+ "pageLength",
105
+ {
106
+ "extend": "colvis",
107
+ "collectionLayout": "fixed columns",
108
+ "popoverTitle": "Column visibility control",
109
+ },
110
+ "copyHtml5",
111
+ {"extend": "csvHtml5", "title": table_name},
112
+ ],
113
+ columnDefs=[
114
+ {"visible": True, "targets": [0]},
115
+ {"visible": True, "targets": "_all"},
116
+ ],
117
+ style="width:100%",
118
+ classes="display cell-border",
119
+ stateSave=False,
120
+ )
121
+ display(Markdown(params_text))
122
+
123
+
124
+ class DifferentialExpression(ProcessorStepBase):
125
+ parent: Processor
126
+ results: DifferentialExpressionResults
127
+ name: str = "differential_expression"
128
+ description: str = "Differential Expression"
129
+
130
+ def __init__(self, parent: Processor) -> None:
131
+ try:
132
+ import scanpy as sc # noqa: F401
133
+ except ImportError:
134
+ raise ImportError("Please install scanpy: `pip install scanpy`")
135
+
136
+ metadata = parent.dataset._metadata.select_dtypes(
137
+ include=["object", "category"]
138
+ )
139
+ groupby_options = (None,) + tuple(metadata.columns)
140
+
141
+ variable_controls: dict[str, DescriptionWidget | ValueWidget]
142
+ variable_controls = dict(
143
+ groupby=Dropdown(options=groupby_options, description="Group by"),
144
+ groups=SelectMultiple(description="Groups"),
145
+ reference=Dropdown(description="Reference"),
146
+ layer=Dropdown(
147
+ options=(None,) + tuple(parent.dataset.adata.layers.keys()),
148
+ value=None,
149
+ description="Layer",
150
+ ),
151
+ name=Text(description="Name", value="", continuous_update=False),
152
+ )
153
+
154
+ variable_controls["groupby"].observe(
155
+ self._update_groups_options, "value", "change"
156
+ )
157
+ variable_controls["groupby"].observe(
158
+ self._update_reference_options, "value", "change"
159
+ )
160
+
161
+ results = DifferentialExpressionResults(parent.dataset)
162
+ super().__init__(
163
+ parent=parent,
164
+ fixed_params={},
165
+ variable_controls=variable_controls,
166
+ results=results,
167
+ )
168
+
169
+ def function(
170
+ self,
171
+ groupby: str,
172
+ groups: Iterable[str] | Literal["all"],
173
+ reference: str,
174
+ layer: str | None,
175
+ name: str | None,
176
+ ):
177
+ import scanpy as sc
178
+
179
+ assert groupby
180
+
181
+ if not groups:
182
+ groups = "all"
183
+
184
+ key_added = "differential_expression"
185
+ if name:
186
+ key_added = f"{key_added}_{name}"
187
+
188
+ adata = self.parent.dataset.adata
189
+ uns: dict[str, Any] = adata.uns
190
+ if key_added in adata.uns:
191
+ related_names = list(filter(lambda x: x.startswith(key_added), uns.keys()))
192
+ key_added = f"{key_added}_{len(related_names) + 1}"
193
+
194
+ sc.tl.rank_genes_groups(
195
+ adata,
196
+ groupby,
197
+ groups=groups,
198
+ reference=reference,
199
+ layer=layer,
200
+ key_added=key_added,
201
+ pts=True,
202
+ )
203
+
204
+ self.results.sync_results_list(focus_result=key_added)
205
+
206
+ def _update_groups_options(self, *args, **kwargs):
207
+ groupby = self.variable_controls["groupby"].value
208
+ metadata = self.parent.dataset._metadata
209
+ control: Dropdown = self.variable_controls["groups"]
210
+
211
+ if groupby is None:
212
+ control.options = ("",)
213
+ return
214
+
215
+ options = tuple(metadata[groupby].sort_values().unique())
216
+ control.options = options
217
+
218
+ def _update_reference_options(self, *args, **kwargs):
219
+ groupby = self.variable_controls["groupby"].value
220
+ metadata = self.parent.dataset._metadata
221
+ control: Dropdown = self.variable_controls["reference"]
222
+
223
+ if groupby is None:
224
+ control.options = ("",)
225
+ control.value = ""
226
+ return
227
+
228
+ options = ("rest",)
229
+ options += tuple(metadata[groupby].sort_values().unique())
230
+
231
+ current_value = control.value
232
+ control.options = options
233
+ if current_value not in control.options:
234
+ control.value = "rest"
235
+ else:
236
+ control.value = current_value
237
+
238
+ def dset_var_dataframe_change_callback(self, *args, **kwargs):
239
+ var_df = self.parent.dataset.adata.var
240
+ df = var_df.select_dtypes(include=["bool"])
241
+ options = {"": None, **{c: c for c in df.columns}}
242
+
243
+ control: Dropdown = self.variable_controls["mask_var"]
244
+ current_value = control.value
245
+ control.options = options
246
+ if current_value not in control.options:
247
+ control.value = None
248
+ else:
249
+ control.value = current_value
250
+
251
+
252
+ # from scanpy 1.10.4
253
+ # scanpy/src/scanpy/get/get.py
254
+ def _rank_genes_groups_df(
255
+ adata: AnnData,
256
+ group: str | Iterable[str] | None,
257
+ *,
258
+ key: str = "rank_genes_groups",
259
+ pval_cutoff: float | None = None,
260
+ log2fc_min: float | None = None,
261
+ log2fc_max: float | None = None,
262
+ gene_symbols: str | None = None,
263
+ ) -> pd.DataFrame:
264
+ """\
265
+ Params
266
+ ------
267
+ adata
268
+ Object to get results from.
269
+ group
270
+ Which group (as in :func:`scanpy.tl.rank_genes_groups`'s `groupby`
271
+ argument) to return results from. Can be a list. All groups are
272
+ returned if groups is `None`.
273
+ key
274
+ Key differential expression groups were stored under.
275
+ pval_cutoff
276
+ Return only adjusted p-values below the cutoff.
277
+ log2fc_min
278
+ Minimum logfc to return.
279
+ log2fc_max
280
+ Maximum logfc to return.
281
+ gene_symbols
282
+ Column name in `.var` DataFrame that stores gene symbols. Specifying
283
+ this will add that column to the returned dataframe.
284
+
285
+ """
286
+ if isinstance(group, str):
287
+ group = [group]
288
+ if group is None:
289
+ group = list(adata.uns[key]["names"].dtype.names)
290
+ method = adata.uns[key]["params"]["method"]
291
+ if method == "logreg":
292
+ colnames = ["names", "scores"]
293
+ else:
294
+ colnames = ["names", "scores", "logfoldchanges", "pvals", "pvals_adj"]
295
+
296
+ d = [pd.DataFrame(adata.uns[key][c])[group] for c in colnames]
297
+ d = pd.concat(d, axis=1, names=[None, "group"], keys=colnames)
298
+ if Version(pd.__version__) >= Version("2.1"):
299
+ d = d.stack(level=1, future_stack=True).reset_index()
300
+ else:
301
+ d = d.stack(level=1).reset_index()
302
+ d["group"] = pd.Categorical(d["group"], categories=group)
303
+ d = d.sort_values(["group", "level_0"]).drop(columns="level_0")
304
+
305
+ if method != "logreg":
306
+ if pval_cutoff is not None:
307
+ d = d[d["pvals_adj"] < pval_cutoff]
308
+ if log2fc_min is not None:
309
+ d = d[d["logfoldchanges"] > log2fc_min]
310
+ if log2fc_max is not None:
311
+ d = d[d["logfoldchanges"] < log2fc_max]
312
+ if gene_symbols is not None:
313
+ d = d.join(adata.var[gene_symbols], on="names")
314
+
315
+ for pts, name in {"pts": "pct_nz_group", "pts_rest": "pct_nz_reference"}.items():
316
+ if pts in adata.uns[key]:
317
+ pts_df = (
318
+ adata.uns[key][pts][group]
319
+ .rename_axis(index="names")
320
+ .reset_index()
321
+ .melt(id_vars="names", var_name="group", value_name=name)
322
+ )
323
+ d = d.merge(pts_df)
324
+
325
+ # remove group column for backward compat if len(group) == 1
326
+ if len(group) == 1:
327
+ d.drop(columns="group", inplace=True)
328
+
329
+ return d.reset_index(drop=True)
@@ -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
+ )