sclab 0.3.0__tar.gz → 0.3.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.
Potentially problematic release.
This version of sclab might be problematic. Click here for more details.
- {sclab-0.3.0 → sclab-0.3.2}/PKG-INFO +1 -1
- {sclab-0.3.0 → sclab-0.3.2}/pyproject.toml +1 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/__init__.py +1 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/_sclab.py +9 -4
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/_processor.py +19 -4
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/_results_panel.py +26 -12
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_differential_expression.py +2 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_integration.py +10 -16
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_pca.py +6 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/gui/components/__init__.py +2 -0
- sclab-0.3.2/src/sclab/gui/components/_transfer_metadata.py +186 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/methods/__init__.py +3 -11
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/__init__.py +2 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_cca.py +1 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_cca_integrate.py +33 -1
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_harmony_integrate.py +5 -2
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/density_dynamics/_density_dynamics.py +4 -4
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/utils/density_nd.py +79 -0
- {sclab-0.3.0 → sclab-0.3.2}/LICENSE +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/README.md +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/_io.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/_methods_registry.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/_dataset.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/_exceptions.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/plotter/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/plotter/_controls.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/plotter/_plotter.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/plotter/_utils.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/step/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/step/_basic_processor_step.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/dataset/processor/step/_processor_step_base.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/event/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/event/_broker.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/event/_client.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/event/_utils.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_cluster.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_doublet_detection.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_gene_expression.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_neighbors.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_preprocess.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_qc.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/examples/processor_steps/_umap.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/gui/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/gui/components/_guided_pseudotime.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_filter_obs.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_harmony.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_normalize_weighted.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_subset.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_transfer_metadata.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_transform.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/preprocess/_utils.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/_compat.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/_settings.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/logging.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/plotting/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/plotting/_rcmod.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/plotting/palettes.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/scanpy/readwrite.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/density_dynamics/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/pseudotime/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/pseudotime/_pseudotime.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/pseudotime/timeseries.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/utils/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/utils/interpolate.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/utils/smoothen.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/cellflow/utils/times.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/differential_expression/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/differential_expression/_pseudobulk_edger.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/differential_expression/_pseudobulk_helpers.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/doublet_detection/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/doublet_detection/_scrublet.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/labeling/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/tools/labeling/sctype.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/utils/__init__.py +0 -0
- {sclab-0.3.0 → sclab-0.3.2}/src/sclab/utils/_write_excel.py +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from io import BytesIO
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
import tempfile
|
|
5
4
|
|
|
6
5
|
from anndata import AnnData
|
|
7
6
|
from IPython.display import display
|
|
@@ -171,7 +170,11 @@ class DataLoader(VBox):
|
|
|
171
170
|
self.upload = FileUpload(layout=Layout(width="200px"))
|
|
172
171
|
self.upload_info = Output(layout=Layout(width="95%"))
|
|
173
172
|
self.upload_row = HBox(
|
|
174
|
-
[
|
|
173
|
+
[
|
|
174
|
+
self.upload_row_label,
|
|
175
|
+
self.upload,
|
|
176
|
+
self.upload_info,
|
|
177
|
+
],
|
|
175
178
|
layout=Layout(width="100%"),
|
|
176
179
|
)
|
|
177
180
|
self.upload.observe(self.on_upload, "value")
|
|
@@ -214,8 +217,8 @@ class DataLoader(VBox):
|
|
|
214
217
|
VBox.__init__(
|
|
215
218
|
self,
|
|
216
219
|
[
|
|
217
|
-
self.url_row,
|
|
218
|
-
self.upload_row,
|
|
220
|
+
# self.url_row,
|
|
221
|
+
# self.upload_row,
|
|
219
222
|
self.defined_adatas_row,
|
|
220
223
|
self.progress_output,
|
|
221
224
|
],
|
|
@@ -234,6 +237,8 @@ class DataLoader(VBox):
|
|
|
234
237
|
self.adata = adata
|
|
235
238
|
|
|
236
239
|
def on_upload(self, *args, **kwargs):
|
|
240
|
+
import tempfile
|
|
241
|
+
|
|
237
242
|
from .scanpy.readwrite import read_10x_h5, read_h5ad
|
|
238
243
|
|
|
239
244
|
files = self.upload.value
|
|
@@ -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):
|
|
@@ -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)
|
|
@@ -10,20 +10,6 @@ class Integration(ProcessorStepBase):
|
|
|
10
10
|
description: str = "Integration"
|
|
11
11
|
|
|
12
12
|
def __init__(self, parent: Processor) -> None:
|
|
13
|
-
try:
|
|
14
|
-
from scanpy.external.pp import harmony_integrate # noqa
|
|
15
|
-
except ImportError:
|
|
16
|
-
try:
|
|
17
|
-
from scanpy.external.pp import scanorama_integrate # noqa
|
|
18
|
-
except ImportError:
|
|
19
|
-
raise ImportError(
|
|
20
|
-
"Integration requires scanorama or harmony to be installed.\n"
|
|
21
|
-
"\nInstall with one of:\n"
|
|
22
|
-
"\npip install harmony"
|
|
23
|
-
"\npip install scanorama"
|
|
24
|
-
"\n"
|
|
25
|
-
)
|
|
26
|
-
|
|
27
13
|
cat_metadata = parent.dataset._metadata.select_dtypes(
|
|
28
14
|
include=["object", "category"]
|
|
29
15
|
)
|
|
@@ -44,8 +30,8 @@ class Integration(ProcessorStepBase):
|
|
|
44
30
|
description="Reference Batch",
|
|
45
31
|
),
|
|
46
32
|
flavor=Dropdown(
|
|
47
|
-
options=["harmony", "scanorama"],
|
|
48
|
-
value="
|
|
33
|
+
options=["cca", "harmony", "scanorama"],
|
|
34
|
+
value="cca",
|
|
49
35
|
description="Flavor",
|
|
50
36
|
),
|
|
51
37
|
max_iters=IntText(
|
|
@@ -96,6 +82,14 @@ class Integration(ProcessorStepBase):
|
|
|
96
82
|
self.broker.std_output.clear_output(wait=False)
|
|
97
83
|
with self.broker.std_output:
|
|
98
84
|
match flavor:
|
|
85
|
+
case "cca":
|
|
86
|
+
from sclab.preprocess import cca_integrate
|
|
87
|
+
|
|
88
|
+
cca_integrate(
|
|
89
|
+
**kvargs,
|
|
90
|
+
reference_batch=reference_batch,
|
|
91
|
+
)
|
|
92
|
+
|
|
99
93
|
case "harmony":
|
|
100
94
|
from sclab.preprocess import harmony_integrate
|
|
101
95
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
import plotly.express as px
|
|
3
|
-
from ipywidgets import Button, Dropdown, IntText
|
|
3
|
+
from ipywidgets import Button, Checkbox, Dropdown, IntText
|
|
4
4
|
|
|
5
5
|
from sclab.dataset.processor import Processor
|
|
6
6
|
from sclab.dataset.processor.step import ProcessorStepBase
|
|
@@ -26,6 +26,7 @@ class PCA(ProcessorStepBase):
|
|
|
26
26
|
n_comps=IntText(value=30, description="N comps."),
|
|
27
27
|
mask_var=Dropdown(options=mask_var_options, description="Genes mask"),
|
|
28
28
|
**parent.make_selectbatch_drowpdown(description="Reference Batch"),
|
|
29
|
+
zero_center=Checkbox(value=False, description="Zero center"),
|
|
29
30
|
)
|
|
30
31
|
|
|
31
32
|
super().__init__(
|
|
@@ -56,6 +57,7 @@ class PCA(ProcessorStepBase):
|
|
|
56
57
|
n_comps: int = 30,
|
|
57
58
|
mask_var: str | None = None,
|
|
58
59
|
reference_batch: str | None = None,
|
|
60
|
+
zero_center: bool = False,
|
|
59
61
|
):
|
|
60
62
|
import scanpy as sc
|
|
61
63
|
|
|
@@ -93,6 +95,9 @@ class PCA(ProcessorStepBase):
|
|
|
93
95
|
sc.pp.pca(adata, n_comps=n_comps, mask_var=mask_var, svd_solver="arpack")
|
|
94
96
|
adata.obsm["X_pca"] = adata.X.dot(adata.varm["PCs"])
|
|
95
97
|
|
|
98
|
+
if zero_center:
|
|
99
|
+
adata.obsm["X_pca"] -= adata.obsm["X_pca"].mean(axis=0, keepdims=True)
|
|
100
|
+
|
|
96
101
|
self.plot_variance_ratio_button.disabled = False
|
|
97
102
|
self.broker.publish(
|
|
98
103
|
"dset_data_dict_change", self.parent.dataset.data_dict, "X_pca"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ipywidgets import (
|
|
5
|
+
Checkbox,
|
|
6
|
+
Dropdown,
|
|
7
|
+
FloatText,
|
|
8
|
+
IntText,
|
|
9
|
+
)
|
|
10
|
+
from pandas.api.types import is_numeric_dtype
|
|
11
|
+
|
|
12
|
+
from sclab.dataset.processor import Processor
|
|
13
|
+
from sclab.dataset.processor.step import ProcessorStepBase
|
|
14
|
+
|
|
15
|
+
_2PI = 2 * np.pi
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TransferMetadata(ProcessorStepBase):
|
|
19
|
+
parent: Processor
|
|
20
|
+
name: str = "transfer_metadata"
|
|
21
|
+
description: str = "Transfer Metadata"
|
|
22
|
+
|
|
23
|
+
run_button_description = "Transfer Metadata"
|
|
24
|
+
|
|
25
|
+
def __init__(self, parent: Processor) -> None:
|
|
26
|
+
variable_controls = dict(
|
|
27
|
+
group_key=Dropdown(
|
|
28
|
+
options=[],
|
|
29
|
+
value=None,
|
|
30
|
+
description="Group Key",
|
|
31
|
+
),
|
|
32
|
+
source_group=Dropdown(
|
|
33
|
+
options=[],
|
|
34
|
+
value=None,
|
|
35
|
+
description="Source Group",
|
|
36
|
+
),
|
|
37
|
+
column=Dropdown(
|
|
38
|
+
options=[],
|
|
39
|
+
value=None,
|
|
40
|
+
description="Column",
|
|
41
|
+
),
|
|
42
|
+
periodic=Checkbox(
|
|
43
|
+
value=False,
|
|
44
|
+
description="Periodic",
|
|
45
|
+
),
|
|
46
|
+
vmin=FloatText(
|
|
47
|
+
value=0,
|
|
48
|
+
description="Vmin",
|
|
49
|
+
continuous_update=False,
|
|
50
|
+
),
|
|
51
|
+
vmax=FloatText(
|
|
52
|
+
value=1,
|
|
53
|
+
description="Vmax",
|
|
54
|
+
continuous_update=False,
|
|
55
|
+
),
|
|
56
|
+
min_neighs=IntText(
|
|
57
|
+
value=5,
|
|
58
|
+
min=3,
|
|
59
|
+
description="Min Neighs",
|
|
60
|
+
continuous_update=False,
|
|
61
|
+
),
|
|
62
|
+
weight_by=Dropdown(
|
|
63
|
+
options=["connectivity", "distance", "constant"],
|
|
64
|
+
value="connectivity",
|
|
65
|
+
description="Weight By",
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
super().__init__(
|
|
70
|
+
parent=parent,
|
|
71
|
+
fixed_params={},
|
|
72
|
+
variable_controls=variable_controls,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self._update_groupby_options()
|
|
76
|
+
self._update_column_options()
|
|
77
|
+
self._update_numeric_column_controls()
|
|
78
|
+
|
|
79
|
+
self.variable_controls["group_key"].observe(
|
|
80
|
+
self._update_source_group_options, "value", "change"
|
|
81
|
+
)
|
|
82
|
+
self.variable_controls["column"].observe(
|
|
83
|
+
self._update_numeric_column_controls, "value", "change"
|
|
84
|
+
)
|
|
85
|
+
self.variable_controls["periodic"].observe(
|
|
86
|
+
self._update_vmin_vmax_visibility, "value", "change"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def _update_groupby_options(self, *args, **kwargs):
|
|
90
|
+
metadata = self.parent.dataset._metadata.select_dtypes(include=["category"])
|
|
91
|
+
options = {"": None, **{c: c for c in metadata.columns}}
|
|
92
|
+
self.variable_controls["group_key"].options = options
|
|
93
|
+
|
|
94
|
+
def _update_source_group_options(self, *args, **kwargs):
|
|
95
|
+
group_key = self.variable_controls["group_key"].value
|
|
96
|
+
if group_key is None:
|
|
97
|
+
self.variable_controls["source_group"].options = ("",)
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
options = self.parent.dataset._metadata[group_key].sort_values().unique()
|
|
101
|
+
options = {"": None, **{c: c for c in options}}
|
|
102
|
+
self.variable_controls["source_group"].options = options
|
|
103
|
+
|
|
104
|
+
def _update_column_options(self, *args, **kwargs):
|
|
105
|
+
metadata = self.parent.dataset._metadata.select_dtypes(
|
|
106
|
+
include=["category", "bool", "number"]
|
|
107
|
+
)
|
|
108
|
+
options = {"": None, **{c: c for c in metadata.columns}}
|
|
109
|
+
self.variable_controls["column"].options = options
|
|
110
|
+
|
|
111
|
+
def _update_numeric_column_controls(self, *args, **kwargs):
|
|
112
|
+
column = self.variable_controls["column"].value
|
|
113
|
+
if column is None:
|
|
114
|
+
self._hide_control(self.variable_controls["periodic"])
|
|
115
|
+
self._hide_control(self.variable_controls["vmin"])
|
|
116
|
+
self._hide_control(self.variable_controls["vmax"])
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
series = self.parent.dataset._metadata[column]
|
|
120
|
+
periodic = self.variable_controls["periodic"].value
|
|
121
|
+
|
|
122
|
+
if is_numeric_dtype(series):
|
|
123
|
+
self._show_control(self.variable_controls["periodic"])
|
|
124
|
+
if periodic:
|
|
125
|
+
self._show_control(self.variable_controls["vmin"])
|
|
126
|
+
self._show_control(self.variable_controls["vmax"])
|
|
127
|
+
else:
|
|
128
|
+
self._hide_control(self.variable_controls["periodic"])
|
|
129
|
+
self._hide_control(self.variable_controls["vmin"])
|
|
130
|
+
self._hide_control(self.variable_controls["vmax"])
|
|
131
|
+
|
|
132
|
+
def _update_vmin_vmax_visibility(self, *args, **kwargs):
|
|
133
|
+
periodic = self.variable_controls["periodic"].value
|
|
134
|
+
|
|
135
|
+
if periodic:
|
|
136
|
+
self._show_control(self.variable_controls["vmin"])
|
|
137
|
+
self._show_control(self.variable_controls["vmax"])
|
|
138
|
+
else:
|
|
139
|
+
self._hide_control(self.variable_controls["vmin"])
|
|
140
|
+
self._hide_control(self.variable_controls["vmax"])
|
|
141
|
+
|
|
142
|
+
def _hide_control(self, control):
|
|
143
|
+
control.layout.visibility = "hidden"
|
|
144
|
+
control.layout.height = "0px"
|
|
145
|
+
|
|
146
|
+
def _show_control(self, control):
|
|
147
|
+
control.layout.visibility = "visible"
|
|
148
|
+
control.layout.height = "28px"
|
|
149
|
+
|
|
150
|
+
def function(
|
|
151
|
+
self,
|
|
152
|
+
group_key: str,
|
|
153
|
+
source_group: str,
|
|
154
|
+
column: str,
|
|
155
|
+
periodic: bool = False,
|
|
156
|
+
vmin: float = 0,
|
|
157
|
+
vmax: float = 1,
|
|
158
|
+
min_neighs: int = 5,
|
|
159
|
+
weight_by: Literal["connectivity", "distance", "constant"] = "connectivity",
|
|
160
|
+
**kwargs,
|
|
161
|
+
):
|
|
162
|
+
from ...preprocess._transfer_metadata import transfer_metadata
|
|
163
|
+
|
|
164
|
+
self.output.clear_output(wait=True)
|
|
165
|
+
with self.output:
|
|
166
|
+
transfer_metadata(
|
|
167
|
+
self.parent.dataset.adata,
|
|
168
|
+
group_key=group_key,
|
|
169
|
+
source_group=source_group,
|
|
170
|
+
column=column,
|
|
171
|
+
periodic=periodic,
|
|
172
|
+
vmin=vmin,
|
|
173
|
+
vmax=vmax,
|
|
174
|
+
min_neighs=min_neighs,
|
|
175
|
+
weight_by=weight_by,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
new_column = f"transferred_{column}"
|
|
179
|
+
|
|
180
|
+
self.broker.publish(
|
|
181
|
+
"dset_metadata_change", self.parent.dataset.metadata, new_column
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def dset_metadata_change_callback(self, *args, **kwargs):
|
|
185
|
+
self._update_groupby_options(*args, **kwargs)
|
|
186
|
+
self._update_column_options(*args, **kwargs)
|
|
@@ -13,7 +13,7 @@ from ..examples.processor_steps import (
|
|
|
13
13
|
Neighbors,
|
|
14
14
|
Preprocess,
|
|
15
15
|
)
|
|
16
|
-
from ..gui.components import GuidedPseudotime
|
|
16
|
+
from ..gui.components import GuidedPseudotime, TransferMetadata
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"QC",
|
|
@@ -32,17 +32,9 @@ __all__ = [
|
|
|
32
32
|
register_sclab_method("Processing")(QC)
|
|
33
33
|
register_sclab_method("Processing")(Preprocess)
|
|
34
34
|
register_sclab_method("Processing")(PCA)
|
|
35
|
-
|
|
36
|
-
if any(
|
|
37
|
-
[
|
|
38
|
-
find_spec("harmonypy"),
|
|
39
|
-
find_spec("scanorama"),
|
|
40
|
-
]
|
|
41
|
-
):
|
|
42
|
-
register_sclab_method("Processing")(Integration)
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
register_sclab_method("Processing")(Integration)
|
|
45
36
|
register_sclab_method("Processing")(Neighbors)
|
|
37
|
+
register_sclab_method("Processing")(TransferMetadata)
|
|
46
38
|
register_sclab_method("Processing")(UMAP)
|
|
47
39
|
register_sclab_method("Processing")(Cluster)
|
|
48
40
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from ._cca_integrate import cca_integrate_pair
|
|
1
|
+
from ._cca_integrate import cca_integrate, cca_integrate_pair
|
|
2
2
|
from ._filter_obs import filter_obs
|
|
3
3
|
from ._harmony_integrate import harmony_integrate
|
|
4
4
|
from ._normalize_weighted import normalize_weighted
|
|
@@ -7,6 +7,7 @@ from ._transfer_metadata import transfer_metadata
|
|
|
7
7
|
from ._transform import pool_neighbors
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
|
+
"cca_integrate",
|
|
10
11
|
"cca_integrate_pair",
|
|
11
12
|
"filter_obs",
|
|
12
13
|
"harmony_integrate",
|
|
@@ -19,7 +19,7 @@ def cca(
|
|
|
19
19
|
svd_solver: Literal["full", "partial", "randomized"] = "partial",
|
|
20
20
|
normalize: bool = False,
|
|
21
21
|
random_state=42,
|
|
22
|
-
):
|
|
22
|
+
) -> tuple[NDArray, NDArray, NDArray]:
|
|
23
23
|
"""
|
|
24
24
|
CCA-style integration for two single-cell matrices with unequal numbers of cells.
|
|
25
25
|
|
|
@@ -4,6 +4,38 @@ from anndata import AnnData
|
|
|
4
4
|
from ._cca import cca
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def cca_integrate(
|
|
8
|
+
adata: AnnData,
|
|
9
|
+
key: str,
|
|
10
|
+
*,
|
|
11
|
+
basis: str = "X",
|
|
12
|
+
adjusted_basis: str | None = None,
|
|
13
|
+
reference_batch: str | list[str] | None = None,
|
|
14
|
+
mask_var: str | None = None,
|
|
15
|
+
n_components: int = 30,
|
|
16
|
+
svd_solver: str = "partial",
|
|
17
|
+
normalize: bool = False,
|
|
18
|
+
random_state: int | None = None,
|
|
19
|
+
):
|
|
20
|
+
n_groups = adata.obs[key].nunique()
|
|
21
|
+
if n_groups == 2:
|
|
22
|
+
cca_integrate_pair(
|
|
23
|
+
adata,
|
|
24
|
+
key,
|
|
25
|
+
adata.obs[key].unique()[0],
|
|
26
|
+
adata.obs[key].unique()[1],
|
|
27
|
+
basis=basis,
|
|
28
|
+
adjusted_basis=adjusted_basis,
|
|
29
|
+
mask_var=mask_var,
|
|
30
|
+
n_components=n_components,
|
|
31
|
+
svd_solver=svd_solver,
|
|
32
|
+
normalize=normalize,
|
|
33
|
+
random_state=random_state,
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
|
|
38
|
+
|
|
7
39
|
def cca_integrate_pair(
|
|
8
40
|
adata: AnnData,
|
|
9
41
|
key: str,
|
|
@@ -13,7 +45,7 @@ def cca_integrate_pair(
|
|
|
13
45
|
basis: str | None = None,
|
|
14
46
|
adjusted_basis: str | None = None,
|
|
15
47
|
mask_var: str | None = None,
|
|
16
|
-
n_components: int =
|
|
48
|
+
n_components: int = 30,
|
|
17
49
|
svd_solver: str = "partial",
|
|
18
50
|
normalize: bool = False,
|
|
19
51
|
random_state: int | None = None,
|
|
@@ -17,8 +17,8 @@ Nat Biotechnol 41, 604-606 (2023). https://doi.org/10.1038/s41587-023-01733-8
|
|
|
17
17
|
|
|
18
18
|
from collections.abc import Sequence
|
|
19
19
|
|
|
20
|
-
from anndata import AnnData
|
|
21
20
|
import numpy as np
|
|
21
|
+
from anndata import AnnData
|
|
22
22
|
|
|
23
23
|
from ._harmony import run_harmony
|
|
24
24
|
|
|
@@ -28,12 +28,15 @@ def harmony_integrate(
|
|
|
28
28
|
key: str | Sequence[str],
|
|
29
29
|
*,
|
|
30
30
|
basis: str = "X_pca",
|
|
31
|
-
adjusted_basis: str =
|
|
31
|
+
adjusted_basis: str | None = None,
|
|
32
32
|
reference_batch: str | list[str] | None = None,
|
|
33
33
|
**kwargs,
|
|
34
34
|
):
|
|
35
35
|
"""Use harmonypy :cite:p:`Korsunsky2019` to integrate different experiments."""
|
|
36
36
|
|
|
37
|
+
if adjusted_basis is None:
|
|
38
|
+
adjusted_basis = f"{basis}_harmony"
|
|
39
|
+
|
|
37
40
|
if isinstance(reference_batch, str):
|
|
38
41
|
reference_batch = [reference_batch]
|
|
39
42
|
|
|
@@ -89,9 +89,9 @@ def density(
|
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
if plot_density | plot_density_fit | plot_density_fit_derivative | plot_histogram:
|
|
92
|
-
from ..utils import
|
|
92
|
+
from ..utils.density_nd import density_result_1d
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
density_result_1d(
|
|
95
95
|
rslt,
|
|
96
96
|
data=times[~np.isnan(times)],
|
|
97
97
|
density_fit_lam=lam,
|
|
@@ -207,9 +207,9 @@ def density_dynamics(
|
|
|
207
207
|
)
|
|
208
208
|
|
|
209
209
|
if plot_density | plot_density_fit | plot_density_fit_derivative | plot_histogram:
|
|
210
|
-
from ..utils import
|
|
210
|
+
from ..utils.density_nd import density_result_1d
|
|
211
211
|
|
|
212
|
-
ax =
|
|
212
|
+
ax = density_result_1d(
|
|
213
213
|
rslt,
|
|
214
214
|
data=times[~np.isnan(times)],
|
|
215
215
|
density_fit_lam=lam,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from itertools import product
|
|
2
2
|
from typing import Literal, NamedTuple
|
|
3
3
|
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
4
5
|
import numpy as np
|
|
5
6
|
from numpy.typing import NDArray
|
|
6
7
|
from scipy.integrate import trapezoid
|
|
@@ -134,3 +135,81 @@ def fit_density_1d(
|
|
|
134
135
|
)
|
|
135
136
|
|
|
136
137
|
return rslt, bspl
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def density_result_1d(
|
|
141
|
+
rslt: DensityResult,
|
|
142
|
+
data: NDArray | None = None,
|
|
143
|
+
density_fit_lam: float = 1e-6,
|
|
144
|
+
plot_density: bool = False,
|
|
145
|
+
plot_density_fit: bool = True,
|
|
146
|
+
plot_density_fit_derivative: bool = False,
|
|
147
|
+
plot_histogram: bool = False,
|
|
148
|
+
histogram_nbins: int = 50,
|
|
149
|
+
ax: plt.Axes | None = None,
|
|
150
|
+
show: bool = True,
|
|
151
|
+
):
|
|
152
|
+
if plot_density | plot_density_fit | plot_density_fit_derivative | plot_histogram:
|
|
153
|
+
pass
|
|
154
|
+
else:
|
|
155
|
+
raise ValueError("At least one of the plotting options must be True")
|
|
156
|
+
|
|
157
|
+
tmin, tmax = rslt.grid.min(), rslt.grid.max()
|
|
158
|
+
bspl = fit_smoothing_spline(
|
|
159
|
+
rslt.grid[:, 0],
|
|
160
|
+
rslt.density,
|
|
161
|
+
t_range=(tmin, tmax),
|
|
162
|
+
lam=density_fit_lam,
|
|
163
|
+
periodic=rslt.periodic,
|
|
164
|
+
)
|
|
165
|
+
if ax is None:
|
|
166
|
+
plt.figure(figsize=(10, 3))
|
|
167
|
+
else:
|
|
168
|
+
plt.sca(ax)
|
|
169
|
+
|
|
170
|
+
ax = plt.gca()
|
|
171
|
+
if plot_density:
|
|
172
|
+
ax.plot(rslt.grid.flatten(), rslt.density, color="black", linewidth=0.5)
|
|
173
|
+
|
|
174
|
+
if plot_histogram:
|
|
175
|
+
assert data is not None, "data must be provided if plot_histogram=True"
|
|
176
|
+
# we expand the time vector to make sure that the first and last point
|
|
177
|
+
# are not cut by the boundary. This also helps to avoid the problem of
|
|
178
|
+
# the first and last point having different values (should be periodic).
|
|
179
|
+
tt = np.concatenate([data - tmax, data, data + tmax])
|
|
180
|
+
bins = np.linspace(-tmax, 2 * tmax, histogram_nbins * 3 + 1)
|
|
181
|
+
dd = np.histogram(tt, bins=bins, density=True)[0]
|
|
182
|
+
# we take the middle points of the bins
|
|
183
|
+
xx = bins[:-1] + np.diff(bins) / 2
|
|
184
|
+
# we recover the original time vector and corresponding density
|
|
185
|
+
x = xx[histogram_nbins : 2 * histogram_nbins]
|
|
186
|
+
d = dd[histogram_nbins : 2 * histogram_nbins] * 3 # correct the density
|
|
187
|
+
ax.bar(x, d, width=1 / histogram_nbins, fill=False, linewidth=0.5)
|
|
188
|
+
|
|
189
|
+
x = np.linspace(tmin, tmax, 2**10 + 1)
|
|
190
|
+
if plot_density_fit:
|
|
191
|
+
plt.plot(x, bspl(x), color="blue")
|
|
192
|
+
|
|
193
|
+
ax.set_ylabel("Density", color="blue")
|
|
194
|
+
ax.set_yticks([])
|
|
195
|
+
|
|
196
|
+
ymin, ymax = plt.ylim()
|
|
197
|
+
# add a bit of padding in the y axis (about 10% of current range)
|
|
198
|
+
plt.ylim(ymin, ymax + 0.10 * (ymax - ymin))
|
|
199
|
+
|
|
200
|
+
if plot_density_fit_derivative:
|
|
201
|
+
if plot_density or histogram_nbins or plot_density_fit:
|
|
202
|
+
plt.twinx()
|
|
203
|
+
plt.plot(x, bspl.derivative()(x), color="red")
|
|
204
|
+
plt.hlines(0, tmin, tmax, linestyles="dashed", linewidth=0.5, color="black")
|
|
205
|
+
plt.ylabel("Derivative", color="red")
|
|
206
|
+
plt.gca().set_yticks([])
|
|
207
|
+
|
|
208
|
+
# add padding in the y axis to make zero be in the middle
|
|
209
|
+
ymax = np.abs(plt.ylim()).max() * 1.05
|
|
210
|
+
plt.ylim(-ymax, ymax)
|
|
211
|
+
|
|
212
|
+
if show:
|
|
213
|
+
plt.show()
|
|
214
|
+
else:
|
|
215
|
+
return plt.gca()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|