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.
- sclab/__init__.py +3 -1
- sclab/_io.py +83 -12
- sclab/_methods_registry.py +65 -0
- sclab/_sclab.py +241 -21
- sclab/dataset/_dataset.py +4 -6
- sclab/dataset/processor/_processor.py +41 -19
- sclab/dataset/processor/_results_panel.py +94 -0
- sclab/dataset/processor/step/_processor_step_base.py +12 -6
- sclab/examples/processor_steps/__init__.py +8 -0
- sclab/examples/processor_steps/_cluster.py +2 -2
- sclab/examples/processor_steps/_differential_expression.py +329 -0
- sclab/examples/processor_steps/_doublet_detection.py +68 -0
- sclab/examples/processor_steps/_gene_expression.py +125 -0
- sclab/examples/processor_steps/_integration.py +116 -0
- sclab/examples/processor_steps/_neighbors.py +26 -6
- sclab/examples/processor_steps/_pca.py +13 -8
- sclab/examples/processor_steps/_preprocess.py +52 -25
- sclab/examples/processor_steps/_qc.py +24 -8
- sclab/examples/processor_steps/_umap.py +2 -2
- sclab/gui/__init__.py +0 -0
- sclab/gui/components/__init__.py +7 -0
- sclab/gui/components/_guided_pseudotime.py +482 -0
- sclab/gui/components/_transfer_metadata.py +186 -0
- sclab/methods/__init__.py +50 -0
- sclab/preprocess/__init__.py +26 -0
- sclab/preprocess/_cca.py +176 -0
- sclab/preprocess/_cca_integrate.py +109 -0
- sclab/preprocess/_filter_obs.py +42 -0
- sclab/preprocess/_harmony.py +421 -0
- sclab/preprocess/_harmony_integrate.py +53 -0
- sclab/preprocess/_normalize_weighted.py +65 -0
- sclab/preprocess/_pca.py +51 -0
- sclab/preprocess/_preprocess.py +155 -0
- sclab/preprocess/_qc.py +38 -0
- sclab/preprocess/_rpca.py +116 -0
- sclab/preprocess/_subset.py +208 -0
- sclab/preprocess/_transfer_metadata.py +196 -0
- sclab/preprocess/_transform.py +82 -0
- sclab/preprocess/_utils.py +96 -0
- sclab/scanpy/__init__.py +0 -0
- sclab/scanpy/_compat.py +92 -0
- sclab/scanpy/_settings.py +526 -0
- sclab/scanpy/logging.py +290 -0
- sclab/scanpy/plotting/__init__.py +0 -0
- sclab/scanpy/plotting/_rcmod.py +73 -0
- sclab/scanpy/plotting/palettes.py +221 -0
- sclab/scanpy/readwrite.py +1108 -0
- sclab/tools/__init__.py +0 -0
- sclab/tools/cellflow/__init__.py +0 -0
- sclab/tools/cellflow/density_dynamics/__init__.py +0 -0
- sclab/tools/cellflow/density_dynamics/_density_dynamics.py +349 -0
- sclab/tools/cellflow/pseudotime/__init__.py +0 -0
- sclab/tools/cellflow/pseudotime/_pseudotime.py +336 -0
- sclab/tools/cellflow/pseudotime/timeseries.py +226 -0
- sclab/tools/cellflow/utils/__init__.py +0 -0
- sclab/tools/cellflow/utils/density_nd.py +215 -0
- sclab/tools/cellflow/utils/interpolate.py +334 -0
- sclab/tools/cellflow/utils/periodic_genes.py +106 -0
- sclab/tools/cellflow/utils/smoothen.py +124 -0
- sclab/tools/cellflow/utils/times.py +55 -0
- sclab/tools/differential_expression/__init__.py +7 -0
- sclab/tools/differential_expression/_pseudobulk_edger.py +309 -0
- sclab/tools/differential_expression/_pseudobulk_helpers.py +290 -0
- sclab/tools/differential_expression/_pseudobulk_limma.py +257 -0
- sclab/tools/doublet_detection/__init__.py +5 -0
- sclab/tools/doublet_detection/_scrublet.py +64 -0
- sclab/tools/embedding/__init__.py +0 -0
- sclab/tools/imputation/__init__.py +0 -0
- sclab/tools/imputation/_alra.py +135 -0
- sclab/tools/labeling/__init__.py +6 -0
- sclab/tools/labeling/sctype.py +233 -0
- sclab/tools/utils/__init__.py +5 -0
- sclab/tools/utils/_aggregate_and_filter.py +290 -0
- sclab/utils/__init__.py +5 -0
- sclab/utils/_write_excel.py +510 -0
- {sclab-0.1.7.dist-info → sclab-0.3.4.dist-info}/METADATA +29 -12
- sclab-0.3.4.dist-info/RECORD +93 -0
- {sclab-0.1.7.dist-info → sclab-0.3.4.dist-info}/WHEEL +1 -1
- sclab-0.3.4.dist-info/licenses/LICENSE +29 -0
- sclab-0.1.7.dist-info/RECORD +0 -30
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import sys
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from enum import IntEnum
|
|
7
|
+
from logging import getLevelName
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from time import time
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from . import logging
|
|
13
|
+
from ._compat import old_positionals
|
|
14
|
+
from .logging import _RootLogger, _set_log_file, _set_log_level
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Generator, Iterable
|
|
18
|
+
from typing import Any, Literal, TextIO
|
|
19
|
+
|
|
20
|
+
# Collected from the print_* functions in matplotlib.backends
|
|
21
|
+
_Format = (
|
|
22
|
+
Literal["png", "jpg", "tif", "tiff"]
|
|
23
|
+
| Literal["pdf", "ps", "eps", "svg", "svgz", "pgf"]
|
|
24
|
+
| Literal["raw", "rgba"]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
_VERBOSITY_TO_LOGLEVEL = {
|
|
28
|
+
"error": "ERROR",
|
|
29
|
+
"warning": "WARNING",
|
|
30
|
+
"info": "INFO",
|
|
31
|
+
"hint": "HINT",
|
|
32
|
+
"debug": "DEBUG",
|
|
33
|
+
}
|
|
34
|
+
# Python 3.7+ ensures iteration order
|
|
35
|
+
for v, level in enumerate(list(_VERBOSITY_TO_LOGLEVEL.values())):
|
|
36
|
+
_VERBOSITY_TO_LOGLEVEL[v] = level
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Verbosity(IntEnum):
|
|
40
|
+
"""Logging verbosity levels."""
|
|
41
|
+
|
|
42
|
+
error = 0
|
|
43
|
+
warning = 1
|
|
44
|
+
info = 2
|
|
45
|
+
hint = 3
|
|
46
|
+
debug = 4
|
|
47
|
+
|
|
48
|
+
def __eq__(self, other: Verbosity | int | str) -> bool:
|
|
49
|
+
if isinstance(other, Verbosity):
|
|
50
|
+
return self is other
|
|
51
|
+
if isinstance(other, int):
|
|
52
|
+
return self.value == other
|
|
53
|
+
if isinstance(other, str):
|
|
54
|
+
return self.name == other
|
|
55
|
+
return NotImplemented
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def level(self) -> int:
|
|
59
|
+
# getLevelName(str) returns the int level…
|
|
60
|
+
return getLevelName(_VERBOSITY_TO_LOGLEVEL[self.name])
|
|
61
|
+
|
|
62
|
+
@contextmanager
|
|
63
|
+
def override(
|
|
64
|
+
self, verbosity: Verbosity | str | int
|
|
65
|
+
) -> Generator[Verbosity, None, None]:
|
|
66
|
+
"""\
|
|
67
|
+
Temporarily override verbosity
|
|
68
|
+
"""
|
|
69
|
+
settings.verbosity = verbosity
|
|
70
|
+
yield self
|
|
71
|
+
settings.verbosity = self
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# backwards compat
|
|
75
|
+
Verbosity.warn = Verbosity.warning
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _type_check(var: Any, varname: str, types: type | tuple[type, ...]):
|
|
79
|
+
if isinstance(var, types):
|
|
80
|
+
return
|
|
81
|
+
if isinstance(types, type):
|
|
82
|
+
possible_types_str = types.__name__
|
|
83
|
+
else:
|
|
84
|
+
type_names = [t.__name__ for t in types]
|
|
85
|
+
possible_types_str = "{} or {}".format(
|
|
86
|
+
", ".join(type_names[:-1]), type_names[-1]
|
|
87
|
+
)
|
|
88
|
+
raise TypeError(f"{varname} must be of type {possible_types_str}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ScanpyConfig:
|
|
92
|
+
"""\
|
|
93
|
+
Config manager for scanpy.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
N_PCS: int
|
|
97
|
+
"""Default number of principal components to use."""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
*,
|
|
102
|
+
verbosity: Verbosity | int | str = Verbosity.warning,
|
|
103
|
+
plot_suffix: str = "",
|
|
104
|
+
file_format_data: str = "h5ad",
|
|
105
|
+
file_format_figs: str = "pdf",
|
|
106
|
+
autosave: bool = False,
|
|
107
|
+
autoshow: bool = True,
|
|
108
|
+
writedir: Path | str = "./write/",
|
|
109
|
+
cachedir: Path | str = "./cache/",
|
|
110
|
+
datasetdir: Path | str = "./data/",
|
|
111
|
+
figdir: Path | str = "./figures/",
|
|
112
|
+
cache_compression: str | None = "lzf",
|
|
113
|
+
max_memory=15,
|
|
114
|
+
n_jobs=1,
|
|
115
|
+
logfile: Path | str | None = None,
|
|
116
|
+
categories_to_ignore: Iterable[str] = ("N/A", "dontknow", "no_gate", "?"),
|
|
117
|
+
_frameon: bool = True,
|
|
118
|
+
_vector_friendly: bool = False,
|
|
119
|
+
_low_resolution_warning: bool = True,
|
|
120
|
+
n_pcs=50,
|
|
121
|
+
):
|
|
122
|
+
# logging
|
|
123
|
+
self._root_logger = _RootLogger(logging.INFO) # level will be replaced
|
|
124
|
+
self.logfile = logfile
|
|
125
|
+
self.verbosity = verbosity
|
|
126
|
+
# rest
|
|
127
|
+
self.plot_suffix = plot_suffix
|
|
128
|
+
self.file_format_data = file_format_data
|
|
129
|
+
self.file_format_figs = file_format_figs
|
|
130
|
+
self.autosave = autosave
|
|
131
|
+
self.autoshow = autoshow
|
|
132
|
+
self.writedir = writedir
|
|
133
|
+
self.cachedir = cachedir
|
|
134
|
+
self.datasetdir = datasetdir
|
|
135
|
+
self.figdir = figdir
|
|
136
|
+
self.cache_compression = cache_compression
|
|
137
|
+
self.max_memory = max_memory
|
|
138
|
+
self.n_jobs = n_jobs
|
|
139
|
+
self.categories_to_ignore = categories_to_ignore
|
|
140
|
+
self._frameon = _frameon
|
|
141
|
+
"""bool: See set_figure_params."""
|
|
142
|
+
|
|
143
|
+
self._vector_friendly = _vector_friendly
|
|
144
|
+
"""Set to true if you want to include pngs in svgs and pdfs."""
|
|
145
|
+
|
|
146
|
+
self._low_resolution_warning = _low_resolution_warning
|
|
147
|
+
"""Print warning when saving a figure with low resolution."""
|
|
148
|
+
|
|
149
|
+
self._start = time()
|
|
150
|
+
"""Time when the settings module is first imported."""
|
|
151
|
+
|
|
152
|
+
self._previous_time = self._start
|
|
153
|
+
"""Variable for timing program parts."""
|
|
154
|
+
|
|
155
|
+
self._previous_memory_usage = -1
|
|
156
|
+
"""Stores the previous memory usage."""
|
|
157
|
+
|
|
158
|
+
self.N_PCS = n_pcs
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def verbosity(self) -> Verbosity:
|
|
162
|
+
"""
|
|
163
|
+
Verbosity level (default `warning`)
|
|
164
|
+
|
|
165
|
+
Level 0: only show 'error' messages.
|
|
166
|
+
Level 1: also show 'warning' messages.
|
|
167
|
+
Level 2: also show 'info' messages.
|
|
168
|
+
Level 3: also show 'hint' messages.
|
|
169
|
+
Level 4: also show very detailed progress for 'debug'ging.
|
|
170
|
+
"""
|
|
171
|
+
return self._verbosity
|
|
172
|
+
|
|
173
|
+
@verbosity.setter
|
|
174
|
+
def verbosity(self, verbosity: Verbosity | int | str):
|
|
175
|
+
verbosity_str_options = [
|
|
176
|
+
v for v in _VERBOSITY_TO_LOGLEVEL if isinstance(v, str)
|
|
177
|
+
]
|
|
178
|
+
if isinstance(verbosity, Verbosity):
|
|
179
|
+
self._verbosity = verbosity
|
|
180
|
+
elif isinstance(verbosity, int):
|
|
181
|
+
self._verbosity = Verbosity(verbosity)
|
|
182
|
+
elif isinstance(verbosity, str):
|
|
183
|
+
verbosity = verbosity.lower()
|
|
184
|
+
if verbosity not in verbosity_str_options:
|
|
185
|
+
raise ValueError(
|
|
186
|
+
f"Cannot set verbosity to {verbosity}. "
|
|
187
|
+
f"Accepted string values are: {verbosity_str_options}"
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
self._verbosity = Verbosity(verbosity_str_options.index(verbosity))
|
|
191
|
+
else:
|
|
192
|
+
_type_check(verbosity, "verbosity", (str, int))
|
|
193
|
+
_set_log_level(self, _VERBOSITY_TO_LOGLEVEL[self._verbosity.name])
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def plot_suffix(self) -> str:
|
|
197
|
+
"""Global suffix that is appended to figure filenames."""
|
|
198
|
+
return self._plot_suffix
|
|
199
|
+
|
|
200
|
+
@plot_suffix.setter
|
|
201
|
+
def plot_suffix(self, plot_suffix: str):
|
|
202
|
+
_type_check(plot_suffix, "plot_suffix", str)
|
|
203
|
+
self._plot_suffix = plot_suffix
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def file_format_data(self) -> str:
|
|
207
|
+
"""File format for saving AnnData objects.
|
|
208
|
+
|
|
209
|
+
Allowed are 'txt', 'csv' (comma separated value file) for exporting and 'h5ad'
|
|
210
|
+
(hdf5) for lossless saving.
|
|
211
|
+
"""
|
|
212
|
+
return self._file_format_data
|
|
213
|
+
|
|
214
|
+
@file_format_data.setter
|
|
215
|
+
def file_format_data(self, file_format: str):
|
|
216
|
+
_type_check(file_format, "file_format_data", str)
|
|
217
|
+
file_format_options = {"txt", "csv", "h5ad"}
|
|
218
|
+
if file_format not in file_format_options:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Cannot set file_format_data to {file_format}. "
|
|
221
|
+
f"Must be one of {file_format_options}"
|
|
222
|
+
)
|
|
223
|
+
self._file_format_data = file_format
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def file_format_figs(self) -> str:
|
|
227
|
+
"""File format for saving figures.
|
|
228
|
+
|
|
229
|
+
For example 'png', 'pdf' or 'svg'. Many other formats work as well (see
|
|
230
|
+
`matplotlib.pyplot.savefig`).
|
|
231
|
+
"""
|
|
232
|
+
return self._file_format_figs
|
|
233
|
+
|
|
234
|
+
@file_format_figs.setter
|
|
235
|
+
def file_format_figs(self, figure_format: str):
|
|
236
|
+
_type_check(figure_format, "figure_format_data", str)
|
|
237
|
+
self._file_format_figs = figure_format
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def autosave(self) -> bool:
|
|
241
|
+
"""\
|
|
242
|
+
Automatically save figures in :attr:`~scanpy._settings.ScanpyConfig.figdir` (default `False`).
|
|
243
|
+
|
|
244
|
+
Do not show plots/figures interactively.
|
|
245
|
+
"""
|
|
246
|
+
return self._autosave
|
|
247
|
+
|
|
248
|
+
@autosave.setter
|
|
249
|
+
def autosave(self, autosave: bool):
|
|
250
|
+
_type_check(autosave, "autosave", bool)
|
|
251
|
+
self._autosave = autosave
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def autoshow(self) -> bool:
|
|
255
|
+
"""\
|
|
256
|
+
Automatically show figures if `autosave == False` (default `True`).
|
|
257
|
+
|
|
258
|
+
There is no need to call the matplotlib pl.show() in this case.
|
|
259
|
+
"""
|
|
260
|
+
return self._autoshow
|
|
261
|
+
|
|
262
|
+
@autoshow.setter
|
|
263
|
+
def autoshow(self, autoshow: bool):
|
|
264
|
+
_type_check(autoshow, "autoshow", bool)
|
|
265
|
+
self._autoshow = autoshow
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def writedir(self) -> Path:
|
|
269
|
+
"""\
|
|
270
|
+
Directory where the function scanpy.write writes to by default.
|
|
271
|
+
"""
|
|
272
|
+
return self._writedir
|
|
273
|
+
|
|
274
|
+
@writedir.setter
|
|
275
|
+
def writedir(self, writedir: Path | str):
|
|
276
|
+
_type_check(writedir, "writedir", (str, Path))
|
|
277
|
+
self._writedir = Path(writedir)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def cachedir(self) -> Path:
|
|
281
|
+
"""\
|
|
282
|
+
Directory for cache files (default `'./cache/'`).
|
|
283
|
+
"""
|
|
284
|
+
return self._cachedir
|
|
285
|
+
|
|
286
|
+
@cachedir.setter
|
|
287
|
+
def cachedir(self, cachedir: Path | str):
|
|
288
|
+
_type_check(cachedir, "cachedir", (str, Path))
|
|
289
|
+
self._cachedir = Path(cachedir)
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def datasetdir(self) -> Path:
|
|
293
|
+
"""\
|
|
294
|
+
Directory for example :mod:`~scanpy.datasets` (default `'./data/'`).
|
|
295
|
+
"""
|
|
296
|
+
return self._datasetdir
|
|
297
|
+
|
|
298
|
+
@datasetdir.setter
|
|
299
|
+
def datasetdir(self, datasetdir: Path | str):
|
|
300
|
+
_type_check(datasetdir, "datasetdir", (str, Path))
|
|
301
|
+
self._datasetdir = Path(datasetdir).resolve()
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def figdir(self) -> Path:
|
|
305
|
+
"""\
|
|
306
|
+
Directory for saving figures (default `'./figures/'`).
|
|
307
|
+
"""
|
|
308
|
+
return self._figdir
|
|
309
|
+
|
|
310
|
+
@figdir.setter
|
|
311
|
+
def figdir(self, figdir: Path | str):
|
|
312
|
+
_type_check(figdir, "figdir", (str, Path))
|
|
313
|
+
self._figdir = Path(figdir)
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def cache_compression(self) -> str | None:
|
|
317
|
+
"""\
|
|
318
|
+
Compression for `sc.read(..., cache=True)` (default `'lzf'`).
|
|
319
|
+
|
|
320
|
+
May be `'lzf'`, `'gzip'`, or `None`.
|
|
321
|
+
"""
|
|
322
|
+
return self._cache_compression
|
|
323
|
+
|
|
324
|
+
@cache_compression.setter
|
|
325
|
+
def cache_compression(self, cache_compression: str | None):
|
|
326
|
+
if cache_compression not in {"lzf", "gzip", None}:
|
|
327
|
+
raise ValueError(
|
|
328
|
+
f"`cache_compression` ({cache_compression}) "
|
|
329
|
+
"must be in {'lzf', 'gzip', None}"
|
|
330
|
+
)
|
|
331
|
+
self._cache_compression = cache_compression
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def max_memory(self) -> int | float:
|
|
335
|
+
"""\
|
|
336
|
+
Maximum memory usage in Gigabyte.
|
|
337
|
+
|
|
338
|
+
Is currently not well respected…
|
|
339
|
+
"""
|
|
340
|
+
return self._max_memory
|
|
341
|
+
|
|
342
|
+
@max_memory.setter
|
|
343
|
+
def max_memory(self, max_memory: int | float):
|
|
344
|
+
_type_check(max_memory, "max_memory", (int, float))
|
|
345
|
+
self._max_memory = max_memory
|
|
346
|
+
|
|
347
|
+
@property
|
|
348
|
+
def n_jobs(self) -> int:
|
|
349
|
+
"""\
|
|
350
|
+
Default number of jobs/ CPUs to use for parallel computing.
|
|
351
|
+
|
|
352
|
+
Set to `-1` in order to use all available cores.
|
|
353
|
+
Not all algorithms support special behavior for numbers < `-1`,
|
|
354
|
+
so make sure to leave this setting as >= `-1`.
|
|
355
|
+
"""
|
|
356
|
+
return self._n_jobs
|
|
357
|
+
|
|
358
|
+
@n_jobs.setter
|
|
359
|
+
def n_jobs(self, n_jobs: int):
|
|
360
|
+
_type_check(n_jobs, "n_jobs", int)
|
|
361
|
+
self._n_jobs = n_jobs
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def logpath(self) -> Path | None:
|
|
365
|
+
"""\
|
|
366
|
+
The file path `logfile` was set to.
|
|
367
|
+
"""
|
|
368
|
+
return self._logpath
|
|
369
|
+
|
|
370
|
+
@logpath.setter
|
|
371
|
+
def logpath(self, logpath: Path | str | None):
|
|
372
|
+
_type_check(logpath, "logfile", (str, Path))
|
|
373
|
+
# set via “file object” branch of logfile.setter
|
|
374
|
+
self.logfile = Path(logpath).open("a") # noqa: SIM115
|
|
375
|
+
self._logpath = Path(logpath)
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def logfile(self) -> TextIO:
|
|
379
|
+
"""\
|
|
380
|
+
The open file to write logs to.
|
|
381
|
+
|
|
382
|
+
Set it to a :class:`~pathlib.Path` or :class:`str` to open a new one.
|
|
383
|
+
The default `None` corresponds to :obj:`sys.stdout` in jupyter notebooks
|
|
384
|
+
and to :obj:`sys.stderr` otherwise.
|
|
385
|
+
|
|
386
|
+
For backwards compatibility, setting it to `''` behaves like setting it to `None`.
|
|
387
|
+
"""
|
|
388
|
+
return self._logfile
|
|
389
|
+
|
|
390
|
+
@logfile.setter
|
|
391
|
+
def logfile(self, logfile: Path | str | TextIO | None):
|
|
392
|
+
if not hasattr(logfile, "write") and logfile:
|
|
393
|
+
self.logpath = logfile
|
|
394
|
+
else: # file object
|
|
395
|
+
if not logfile: # None or ''
|
|
396
|
+
logfile = sys.stdout if self._is_run_from_ipython() else sys.stderr
|
|
397
|
+
self._logfile = logfile
|
|
398
|
+
self._logpath = None
|
|
399
|
+
_set_log_file(self)
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def categories_to_ignore(self) -> list[str]:
|
|
403
|
+
"""\
|
|
404
|
+
Categories that are omitted in plotting etc.
|
|
405
|
+
"""
|
|
406
|
+
return self._categories_to_ignore
|
|
407
|
+
|
|
408
|
+
@categories_to_ignore.setter
|
|
409
|
+
def categories_to_ignore(self, categories_to_ignore: Iterable[str]):
|
|
410
|
+
categories_to_ignore = list(categories_to_ignore)
|
|
411
|
+
for i, cat in enumerate(categories_to_ignore):
|
|
412
|
+
_type_check(cat, f"categories_to_ignore[{i}]", str)
|
|
413
|
+
self._categories_to_ignore = categories_to_ignore
|
|
414
|
+
|
|
415
|
+
# --------------------------------------------------------------------------------
|
|
416
|
+
# Functions
|
|
417
|
+
# --------------------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
@old_positionals(
|
|
420
|
+
"scanpy",
|
|
421
|
+
"dpi",
|
|
422
|
+
"dpi_save",
|
|
423
|
+
"frameon",
|
|
424
|
+
"vector_friendly",
|
|
425
|
+
"fontsize",
|
|
426
|
+
"figsize",
|
|
427
|
+
"color_map",
|
|
428
|
+
"format",
|
|
429
|
+
"facecolor",
|
|
430
|
+
"transparent",
|
|
431
|
+
"ipython_format",
|
|
432
|
+
)
|
|
433
|
+
def set_figure_params(
|
|
434
|
+
self,
|
|
435
|
+
*,
|
|
436
|
+
scanpy: bool = True,
|
|
437
|
+
dpi: int = 80,
|
|
438
|
+
dpi_save: int = 150,
|
|
439
|
+
frameon: bool = True,
|
|
440
|
+
vector_friendly: bool = True,
|
|
441
|
+
fontsize: int = 14,
|
|
442
|
+
figsize: int | None = None,
|
|
443
|
+
color_map: str | None = None,
|
|
444
|
+
format: _Format = "pdf",
|
|
445
|
+
facecolor: str | None = None,
|
|
446
|
+
transparent: bool = False,
|
|
447
|
+
ipython_format: str = "png2x",
|
|
448
|
+
) -> None:
|
|
449
|
+
"""\
|
|
450
|
+
Set resolution/size, styling and format of figures.
|
|
451
|
+
|
|
452
|
+
Parameters
|
|
453
|
+
----------
|
|
454
|
+
scanpy
|
|
455
|
+
Init default values for :obj:`matplotlib.rcParams` suited for Scanpy.
|
|
456
|
+
dpi
|
|
457
|
+
Resolution of rendered figures – this influences the size of figures in notebooks.
|
|
458
|
+
dpi_save
|
|
459
|
+
Resolution of saved figures. This should typically be higher to achieve
|
|
460
|
+
publication quality.
|
|
461
|
+
frameon
|
|
462
|
+
Add frames and axes labels to scatter plots.
|
|
463
|
+
vector_friendly
|
|
464
|
+
Plot scatter plots using `png` backend even when exporting as `pdf` or `svg`.
|
|
465
|
+
fontsize
|
|
466
|
+
Set the fontsize for several `rcParams` entries. Ignored if `scanpy=False`.
|
|
467
|
+
figsize
|
|
468
|
+
Set plt.rcParams['figure.figsize'].
|
|
469
|
+
color_map
|
|
470
|
+
Convenience method for setting the default color map. Ignored if `scanpy=False`.
|
|
471
|
+
format
|
|
472
|
+
This sets the default format for saving figures: `file_format_figs`.
|
|
473
|
+
facecolor
|
|
474
|
+
Sets backgrounds via `rcParams['figure.facecolor'] = facecolor` and
|
|
475
|
+
`rcParams['axes.facecolor'] = facecolor`.
|
|
476
|
+
transparent
|
|
477
|
+
Save figures with transparent back ground. Sets
|
|
478
|
+
`rcParams['savefig.transparent']`.
|
|
479
|
+
ipython_format
|
|
480
|
+
Only concerns the notebook/IPython environment; see
|
|
481
|
+
:func:`~IPython.display.set_matplotlib_formats` for details.
|
|
482
|
+
"""
|
|
483
|
+
if self._is_run_from_ipython():
|
|
484
|
+
import IPython
|
|
485
|
+
|
|
486
|
+
if isinstance(ipython_format, str):
|
|
487
|
+
ipython_format = [ipython_format]
|
|
488
|
+
IPython.display.set_matplotlib_formats(*ipython_format)
|
|
489
|
+
|
|
490
|
+
from matplotlib import rcParams
|
|
491
|
+
|
|
492
|
+
self._vector_friendly = vector_friendly
|
|
493
|
+
self.file_format_figs = format
|
|
494
|
+
if dpi is not None:
|
|
495
|
+
rcParams["figure.dpi"] = dpi
|
|
496
|
+
if dpi_save is not None:
|
|
497
|
+
rcParams["savefig.dpi"] = dpi_save
|
|
498
|
+
if transparent is not None:
|
|
499
|
+
rcParams["savefig.transparent"] = transparent
|
|
500
|
+
if facecolor is not None:
|
|
501
|
+
rcParams["figure.facecolor"] = facecolor
|
|
502
|
+
rcParams["axes.facecolor"] = facecolor
|
|
503
|
+
if scanpy:
|
|
504
|
+
from .plotting._rcmod import set_rcParams_scanpy
|
|
505
|
+
|
|
506
|
+
set_rcParams_scanpy(fontsize=fontsize, color_map=color_map)
|
|
507
|
+
if figsize is not None:
|
|
508
|
+
rcParams["figure.figsize"] = figsize
|
|
509
|
+
self._frameon = frameon
|
|
510
|
+
|
|
511
|
+
@staticmethod
|
|
512
|
+
def _is_run_from_ipython():
|
|
513
|
+
"""Determines whether we're currently in IPython."""
|
|
514
|
+
import builtins
|
|
515
|
+
|
|
516
|
+
return getattr(builtins, "__IPYTHON__", False)
|
|
517
|
+
|
|
518
|
+
def __str__(self) -> str:
|
|
519
|
+
return "\n".join(
|
|
520
|
+
f"{k} = {v!r}"
|
|
521
|
+
for k, v in inspect.getmembers(self)
|
|
522
|
+
if not k.startswith("_") and k != "getdoc"
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
settings = ScanpyConfig()
|