diffsvg 0.1.0__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.
- diffsvg/__init__.py +3 -0
- diffsvg/pl/__init__.py +5 -0
- diffsvg/pl/plot_clusters.py +216 -0
- diffsvg/pl/plot_results.py +484 -0
- diffsvg/pl/plot_stability.py +184 -0
- diffsvg/pp/__init__.py +10 -0
- diffsvg/pp/utils.py +620 -0
- diffsvg/py.typed +0 -0
- diffsvg/tl/__init__.py +24 -0
- diffsvg/tl/_exact.py +332 -0
- diffsvg/tl/clustering.py +154 -0
- diffsvg/tl/model.py +1857 -0
- diffsvg/tl/stability.py +335 -0
- diffsvg-0.1.0.dist-info/METADATA +132 -0
- diffsvg-0.1.0.dist-info/RECORD +17 -0
- diffsvg-0.1.0.dist-info/WHEEL +4 -0
- diffsvg-0.1.0.dist-info/licenses/LICENSE +29 -0
diffsvg/tl/stability.py
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""Grid search over preprocessing/model options, to see how stable the gene ranking is.
|
|
2
|
+
|
|
3
|
+
`check_stability` re-runs the pipeline per parameter combination (re-binning once per binning signature,
|
|
4
|
+
on a gene subsample), ranks on a statistic rather than the panel-dependent p-values, and re-runs the
|
|
5
|
+
baseline under other seeds to get a noise floor for the agreement.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
import itertools
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pandas as pd
|
|
14
|
+
from tqdm.auto import tqdm
|
|
15
|
+
|
|
16
|
+
from diffsvg.pp.utils import bin_obs
|
|
17
|
+
|
|
18
|
+
from . import _exact
|
|
19
|
+
from .model import DiffSVGModelSetup, _resolve_priors, fit_model, setup_model
|
|
20
|
+
|
|
21
|
+
#: Parameters that need `bin_obs` re-run; the others only need a refit.
|
|
22
|
+
_BIN_PARAMS = ("n_bsplines", "degree", "n_bins", "window_radius", "layer")
|
|
23
|
+
_MODEL_PARAMS = ("likelihood", "priors", "use_binned")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _stability_defaults():
|
|
27
|
+
"""Default for every tunable, read off the real signatures so they cannot drift."""
|
|
28
|
+
bin_params = inspect.signature(bin_obs).parameters
|
|
29
|
+
setup_params = inspect.signature(setup_model).parameters
|
|
30
|
+
fit_params = inspect.signature(fit_model).parameters
|
|
31
|
+
return {
|
|
32
|
+
**{name: bin_params[name].default for name in _BIN_PARAMS},
|
|
33
|
+
"likelihood": setup_params["likelihood"].default,
|
|
34
|
+
"priors": setup_params["priors"].default,
|
|
35
|
+
"use_binned": fit_params["use_binned"].default,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _format_value(value):
|
|
40
|
+
"""Compact, readable rendering of a parameter value for run labels."""
|
|
41
|
+
if isinstance(value, dict): # a priors override
|
|
42
|
+
# The full spec, not just the distribution name: two overrides of the same prior with different
|
|
43
|
+
# parameters must get different run labels.
|
|
44
|
+
return "+".join(
|
|
45
|
+
f"{name}:{spec[0]}({','.join(str(v) for v in spec[1:])})" for name, spec in sorted(value.items())
|
|
46
|
+
)
|
|
47
|
+
return str(value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class StabilityResult:
|
|
52
|
+
"""What `check_stability` found; consumed by `diffsvg.pl.plot_stability`.
|
|
53
|
+
|
|
54
|
+
Attributes
|
|
55
|
+
----------
|
|
56
|
+
stats, ranks
|
|
57
|
+
`genes x runs` tables of the ranking statistic and of the rank
|
|
58
|
+
derived from it (``1`` = strongest).
|
|
59
|
+
similarity
|
|
60
|
+
`runs x runs` Spearman correlation between rank columns.
|
|
61
|
+
top_overlap
|
|
62
|
+
`runs x runs` fraction of each run's top `top_k` genes that it
|
|
63
|
+
shares with the other run.
|
|
64
|
+
sensitivity
|
|
65
|
+
One row per configuration that differs from the baseline in exactly
|
|
66
|
+
one parameter, with its agreement against the baseline - i.e. the
|
|
67
|
+
one-factor-at-a-time view of which knob actually moves the ranking.
|
|
68
|
+
configs
|
|
69
|
+
`runs x parameters` table of what each run was.
|
|
70
|
+
noise_floor
|
|
71
|
+
Baseline-vs-reseeded-baseline agreement, as
|
|
72
|
+
``{"spearman": ..., "top_overlap": ...}``. Agreement at or below
|
|
73
|
+
this is within MCMC noise.
|
|
74
|
+
baseline, rank_by, top_k, genes
|
|
75
|
+
The baseline run's label, and the settings the comparison used.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
stats: pd.DataFrame
|
|
79
|
+
ranks: pd.DataFrame
|
|
80
|
+
similarity: pd.DataFrame
|
|
81
|
+
top_overlap: pd.DataFrame
|
|
82
|
+
sensitivity: pd.DataFrame
|
|
83
|
+
configs: pd.DataFrame
|
|
84
|
+
noise_floor: dict
|
|
85
|
+
baseline: str
|
|
86
|
+
rank_by: str
|
|
87
|
+
top_k: int
|
|
88
|
+
genes: list
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _resolve_genes(adata, genes, random_state):
|
|
92
|
+
if genes is None:
|
|
93
|
+
selected = list(adata.var_names)
|
|
94
|
+
elif isinstance(genes, int):
|
|
95
|
+
rng = np.random.default_rng(random_state)
|
|
96
|
+
size = min(genes, adata.n_vars)
|
|
97
|
+
selected = sorted(rng.choice(np.asarray(adata.var_names), size=size, replace=False).tolist())
|
|
98
|
+
else:
|
|
99
|
+
selected = list(genes)
|
|
100
|
+
missing = set(selected) - set(adata.var_names)
|
|
101
|
+
if missing:
|
|
102
|
+
raise KeyError(f"Genes not found in adata.var_names: {sorted(missing)}")
|
|
103
|
+
if len(selected) < 3:
|
|
104
|
+
raise ValueError(f"need at least 3 genes to compare rankings, got {len(selected)}.")
|
|
105
|
+
return selected
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def check_stability(
|
|
109
|
+
adata,
|
|
110
|
+
anchor,
|
|
111
|
+
condition,
|
|
112
|
+
grid,
|
|
113
|
+
baseline=None,
|
|
114
|
+
genes=50,
|
|
115
|
+
rank_by=None,
|
|
116
|
+
higher_is_stronger=True,
|
|
117
|
+
top_k=10,
|
|
118
|
+
n_seeds=2,
|
|
119
|
+
n_workers=1,
|
|
120
|
+
progressbar=True,
|
|
121
|
+
random_state=0,
|
|
122
|
+
design_matrix="design_matrix",
|
|
123
|
+
sample_kwargs=None,
|
|
124
|
+
test="parallel",
|
|
125
|
+
tau=None,
|
|
126
|
+
sample=None,
|
|
127
|
+
inference="auto",
|
|
128
|
+
):
|
|
129
|
+
"""Re-rank genes under every combination in `grid` and measure how much the ranking moves.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
adata
|
|
134
|
+
Annotated data matrix, with `anchor`/`condition` in
|
|
135
|
+
``adata.obsm[design_matrix]``. Never modified: each run bins and
|
|
136
|
+
fits out-of-place.
|
|
137
|
+
anchor, condition, design_matrix
|
|
138
|
+
Passed to `diffsvg.pp.bin_obs`. `condition` is required - the
|
|
139
|
+
ranking being checked is the differential one, so the condition-free
|
|
140
|
+
models are out of scope here.
|
|
141
|
+
grid
|
|
142
|
+
``{parameter: [values]}`` over any of
|
|
143
|
+
``("n_bsplines", "degree", "n_bins", "window_radius", "layer")``
|
|
144
|
+
(these re-bin) and ``("likelihood", "priors", "use_binned")`` (these
|
|
145
|
+
only refit). Priors are varied by passing a list of override dicts,
|
|
146
|
+
e.g. ``{"priors": [{"beta": ("normal", 0.0, 0.5)},
|
|
147
|
+
{"beta": ("normal", 0.0, 2.0)}]}``; see
|
|
148
|
+
`diffsvg.tl.model.DEFAULT_PRIORS`.
|
|
149
|
+
baseline
|
|
150
|
+
The configuration everything is compared against, as a dict of
|
|
151
|
+
parameter values. Defaults to the first value of each `grid` axis.
|
|
152
|
+
genes
|
|
153
|
+
Number of genes to subsample (default 50), an explicit list, or
|
|
154
|
+
`None` for all of them. The same genes are used for every run - a
|
|
155
|
+
different subsample per run would confound the comparison.
|
|
156
|
+
rank_by, higher_is_stronger
|
|
157
|
+
Column of `fit_model`'s statistics table to rank on, and whether
|
|
158
|
+
larger means stronger. Rank on a statistic rather than a p-value:
|
|
159
|
+
the Wald test's p-values are computed against the panel, so
|
|
160
|
+
subsampling shifts them and they do not compare across runs. Defaults
|
|
161
|
+
to ``"parallel_stat"`` for ``test="parallel"`` and ``"wald_stat"`` for
|
|
162
|
+
``test="wald"``.
|
|
163
|
+
top_k
|
|
164
|
+
Size of the "top genes" set used for the overlap metric.
|
|
165
|
+
n_seeds
|
|
166
|
+
How many times to run the baseline, each with a different
|
|
167
|
+
`random_seed`, to establish the noise floor. `1` skips it. Ignored
|
|
168
|
+
when every run uses exact inference: the statistics are then
|
|
169
|
+
deterministic and the noise floor is 1.
|
|
170
|
+
n_workers, progressbar, sample_kwargs
|
|
171
|
+
As in `fit_model`; `sample_kwargs` is where to make the sweep
|
|
172
|
+
cheaper (e.g. ``{"draws": 300, "tune": 300}``), since ranking needs
|
|
173
|
+
far less precision than a final fit. The per-run progress bars are
|
|
174
|
+
suppressed in favour of one bar over runs.
|
|
175
|
+
random_state
|
|
176
|
+
Seed for the gene subsample.
|
|
177
|
+
test, tau
|
|
178
|
+
Which condition test every run uses, as in `fit_model`.
|
|
179
|
+
inference
|
|
180
|
+
Passed to `fit_model`.
|
|
181
|
+
sample
|
|
182
|
+
Passed to `diffsvg.pp.bin_obs`: the slide column for data with several
|
|
183
|
+
slides per condition, so every run uses the model with slide effects
|
|
184
|
+
(needs ``window_radius=None`` and ``test="parallel"``). Without it,
|
|
185
|
+
multi-slide data is pooled and the rankings are over-confident.
|
|
186
|
+
|
|
187
|
+
Returns
|
|
188
|
+
-------
|
|
189
|
+
StabilityResult
|
|
190
|
+
"""
|
|
191
|
+
unknown = set(grid) - set(_BIN_PARAMS) - set(_MODEL_PARAMS)
|
|
192
|
+
if unknown:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f"Unknown grid parameter(s) {sorted(unknown)}. Available: {sorted(_BIN_PARAMS + _MODEL_PARAMS)}"
|
|
195
|
+
)
|
|
196
|
+
if not grid:
|
|
197
|
+
raise ValueError("`grid` is empty - pass at least one parameter to vary.")
|
|
198
|
+
|
|
199
|
+
if rank_by is None:
|
|
200
|
+
rank_by = "parallel_stat" if test == "parallel" else "wald_stat"
|
|
201
|
+
defaults = _stability_defaults()
|
|
202
|
+
gene_list = _resolve_genes(adata, genes, random_state)
|
|
203
|
+
sample_kwargs = dict(sample_kwargs or {})
|
|
204
|
+
base_seed = sample_kwargs.pop("random_seed", 42)
|
|
205
|
+
|
|
206
|
+
axes = list(grid)
|
|
207
|
+
configs = [
|
|
208
|
+
dict(defaults, **dict(zip(axes, combo, strict=True))) for combo in itertools.product(*(grid[a] for a in axes))
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
baseline_config = dict(defaults, **{a: grid[a][0] for a in axes})
|
|
212
|
+
if baseline is not None:
|
|
213
|
+
unknown_baseline = set(baseline) - set(defaults)
|
|
214
|
+
if unknown_baseline:
|
|
215
|
+
raise ValueError(f"Unknown baseline parameter(s) {sorted(unknown_baseline)}.")
|
|
216
|
+
baseline_config = dict(defaults, **baseline)
|
|
217
|
+
if baseline_config not in configs:
|
|
218
|
+
configs.insert(0, baseline_config)
|
|
219
|
+
|
|
220
|
+
# Label runs by every parameter that differs between them, including a `baseline` value that is not on
|
|
221
|
+
# the grid, so that no two runs share a label (they would overwrite each other's column).
|
|
222
|
+
varying = [a for a in defaults if len({_format_value(c[a]) for c in configs}) > 1]
|
|
223
|
+
|
|
224
|
+
def label_for(config):
|
|
225
|
+
if not varying:
|
|
226
|
+
return "default"
|
|
227
|
+
return ", ".join(f"{a}={_format_value(config[a])}" for a in varying)
|
|
228
|
+
|
|
229
|
+
baseline_label = label_for(baseline_config)
|
|
230
|
+
runs = [(label_for(config), config, base_seed) for config in configs]
|
|
231
|
+
# Exact inference has no Monte Carlo error, so reseeding would only repeat the same run.
|
|
232
|
+
all_exact = inference in ("auto", "exact") and all(
|
|
233
|
+
_exact.supported(_resolve_priors(c["priors"]), c["likelihood"], sample is not None) for c in configs
|
|
234
|
+
)
|
|
235
|
+
if not all_exact:
|
|
236
|
+
runs += [
|
|
237
|
+
(f"{baseline_label} (reseed {i})", baseline_config, base_seed + i) for i in range(1, max(1, int(n_seeds)))
|
|
238
|
+
]
|
|
239
|
+
labels = [label for label, _, _ in runs]
|
|
240
|
+
if len(set(labels)) != len(labels):
|
|
241
|
+
raise ValueError(f"Two runs got the same label, which would merge their results: {labels}")
|
|
242
|
+
|
|
243
|
+
# Group by binning signature so each distinct binning is computed once.
|
|
244
|
+
grouped = {}
|
|
245
|
+
for run in runs:
|
|
246
|
+
grouped.setdefault(tuple(_format_value(run[1][p]) for p in _BIN_PARAMS), []).append(run)
|
|
247
|
+
|
|
248
|
+
iterator = tqdm(total=len(runs), desc="Checking DiffSVG stability") if progressbar else None
|
|
249
|
+
columns = {}
|
|
250
|
+
for group in grouped.values():
|
|
251
|
+
bin_config = group[0][1]
|
|
252
|
+
binned = bin_obs(
|
|
253
|
+
adata,
|
|
254
|
+
anchor,
|
|
255
|
+
condition,
|
|
256
|
+
genes=gene_list,
|
|
257
|
+
design_matrix=design_matrix,
|
|
258
|
+
inplace=False,
|
|
259
|
+
sample=sample,
|
|
260
|
+
**{p: bin_config[p] for p in _BIN_PARAMS},
|
|
261
|
+
)
|
|
262
|
+
for label, config, seed in group:
|
|
263
|
+
model_setup = DiffSVGModelSetup(binned=binned, likelihood=config["likelihood"], priors=config["priors"])
|
|
264
|
+
stats_df, _, _ = fit_model(
|
|
265
|
+
adata,
|
|
266
|
+
model_setup,
|
|
267
|
+
n_workers=n_workers,
|
|
268
|
+
progressbar=False,
|
|
269
|
+
inplace=False,
|
|
270
|
+
use_binned=config["use_binned"],
|
|
271
|
+
sample_kwargs={**sample_kwargs, "random_seed": seed},
|
|
272
|
+
test=test,
|
|
273
|
+
tau=tau,
|
|
274
|
+
inference=inference,
|
|
275
|
+
)
|
|
276
|
+
columns[label] = stats_df[rank_by]
|
|
277
|
+
if iterator is not None:
|
|
278
|
+
iterator.update(1)
|
|
279
|
+
if iterator is not None:
|
|
280
|
+
iterator.close()
|
|
281
|
+
|
|
282
|
+
stats = pd.DataFrame(columns).reindex(gene_list)
|
|
283
|
+
ranks = stats.rank(ascending=not higher_is_stronger, method="average")
|
|
284
|
+
|
|
285
|
+
similarity = ranks.corr(method="spearman")
|
|
286
|
+
tops = {label: set(ranks[label].nsmallest(top_k).index) for label in ranks}
|
|
287
|
+
top_overlap = pd.DataFrame(
|
|
288
|
+
[[len(tops[a] & tops[b]) / top_k for b in ranks] for a in ranks], index=list(ranks), columns=list(ranks)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
reseed_labels = [label for label, _, _ in runs if label.endswith(")") and " (reseed " in label]
|
|
292
|
+
noise_floor = {
|
|
293
|
+
"spearman": 1.0
|
|
294
|
+
if all_exact
|
|
295
|
+
else (float(min(similarity.loc[baseline_label, reseed_labels])) if reseed_labels else float("nan")),
|
|
296
|
+
"top_overlap": 1.0
|
|
297
|
+
if all_exact
|
|
298
|
+
else (float(min(top_overlap.loc[baseline_label, reseed_labels])) if reseed_labels else float("nan")),
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
sensitivity_rows = []
|
|
302
|
+
for label, config, _ in runs:
|
|
303
|
+
if label == baseline_label or label in reseed_labels:
|
|
304
|
+
continue
|
|
305
|
+
changed = [a for a in varying if config[a] != baseline_config[a]]
|
|
306
|
+
if len(changed) == 1:
|
|
307
|
+
sensitivity_rows.append(
|
|
308
|
+
{
|
|
309
|
+
"parameter": changed[0],
|
|
310
|
+
"value": _format_value(config[changed[0]]),
|
|
311
|
+
"run": label,
|
|
312
|
+
"spearman": float(similarity.loc[baseline_label, label]),
|
|
313
|
+
"top_overlap": float(top_overlap.loc[baseline_label, label]),
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
sensitivity = pd.DataFrame(sensitivity_rows, columns=["parameter", "value", "run", "spearman", "top_overlap"])
|
|
317
|
+
|
|
318
|
+
config_table = pd.DataFrame(
|
|
319
|
+
[{a: _format_value(config[a]) for a in axes} for _, config, _ in runs], index=[label for label, _, _ in runs]
|
|
320
|
+
)
|
|
321
|
+
config_table = config_table[~config_table.index.duplicated()]
|
|
322
|
+
|
|
323
|
+
return StabilityResult(
|
|
324
|
+
stats=stats,
|
|
325
|
+
ranks=ranks,
|
|
326
|
+
similarity=similarity,
|
|
327
|
+
top_overlap=top_overlap,
|
|
328
|
+
sensitivity=sensitivity,
|
|
329
|
+
configs=config_table,
|
|
330
|
+
noise_floor=noise_floor,
|
|
331
|
+
baseline=baseline_label,
|
|
332
|
+
rank_by=rank_by,
|
|
333
|
+
top_k=top_k,
|
|
334
|
+
genes=gene_list,
|
|
335
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: diffsvg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Differential spatially variable genes
|
|
5
|
+
Project-URL: Documentation, https://diffsvg.readthedocs.io/
|
|
6
|
+
Project-URL: Homepage, https://github.com/theislab/diffsvg
|
|
7
|
+
Project-URL: Source, https://github.com/theislab/diffsvg
|
|
8
|
+
Author: Tim Treis
|
|
9
|
+
Maintainer-email: Tim Treis <tim.treis@helmholtz-munich.de>
|
|
10
|
+
License: BSD 3-Clause License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026, Tim Treis
|
|
13
|
+
All rights reserved.
|
|
14
|
+
|
|
15
|
+
Redistribution and use in source and binary forms, with or without
|
|
16
|
+
modification, are permitted provided that the following conditions are met:
|
|
17
|
+
|
|
18
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
19
|
+
list of conditions and the following disclaimer.
|
|
20
|
+
|
|
21
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
22
|
+
this list of conditions and the following disclaimer in the documentation
|
|
23
|
+
and/or other materials provided with the distribution.
|
|
24
|
+
|
|
25
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
26
|
+
contributors may be used to endorse or promote products derived from
|
|
27
|
+
this software without specific prior written permission.
|
|
28
|
+
|
|
29
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
30
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
31
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
32
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
33
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
34
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
35
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
36
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
37
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
38
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
44
|
+
Classifier: Typing :: Typed
|
|
45
|
+
Requires-Python: >=3.12
|
|
46
|
+
Requires-Dist: anndata>=0.13
|
|
47
|
+
Requires-Dist: colorcet
|
|
48
|
+
Requires-Dist: joblib>=1.3
|
|
49
|
+
Requires-Dist: matplotlib
|
|
50
|
+
Requires-Dist: numpy
|
|
51
|
+
Requires-Dist: pandas
|
|
52
|
+
Requires-Dist: patsy
|
|
53
|
+
Requires-Dist: scikit-learn
|
|
54
|
+
Requires-Dist: scipy
|
|
55
|
+
Requires-Dist: session-info2
|
|
56
|
+
Requires-Dist: tqdm
|
|
57
|
+
Provides-Extra: pymc
|
|
58
|
+
Requires-Dist: arviz; extra == 'pymc'
|
|
59
|
+
Requires-Dist: nutpie; extra == 'pymc'
|
|
60
|
+
Requires-Dist: pymc; extra == 'pymc'
|
|
61
|
+
Description-Content-Type: text/markdown
|
|
62
|
+
|
|
63
|
+
# diffsvg
|
|
64
|
+
|
|
65
|
+
[![Tests][badge-tests]][tests]
|
|
66
|
+
[![Documentation][badge-docs]][documentation]
|
|
67
|
+
|
|
68
|
+
[badge-tests]: https://img.shields.io/github/actions/workflow/status/theislab/diffsvg/test.yaml?branch=main
|
|
69
|
+
[badge-docs]: https://app.readthedocs.org/projects/diffsvg/badge/
|
|
70
|
+
|
|
71
|
+
Differential spatially variable genes
|
|
72
|
+
|
|
73
|
+
## Getting started
|
|
74
|
+
|
|
75
|
+
Please refer to the [documentation][],
|
|
76
|
+
in particular, the [API documentation][].
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
You need to have Python 3.12 or newer installed on your system.
|
|
81
|
+
If you don't have Python installed, we recommend installing [uv][].
|
|
82
|
+
|
|
83
|
+
We recommend managing dependencies in project-specific virtual environments to avoid dependency conflicts.
|
|
84
|
+
This is most convenient using package managers such as [uv][].
|
|
85
|
+
Choose from the options below to install diffsvg:
|
|
86
|
+
|
|
87
|
+
<!--
|
|
88
|
+
1. Add the latest release of `diffsvg` from [PyPI][] to your `uv` project:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv add diffsvg
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
1. Install the latest release into a [standard virtual environment][venv]:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
(after activating your venv)
|
|
98
|
+
pip install diffsvg
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
-->
|
|
102
|
+
|
|
103
|
+
1. Install the latest development version:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install git+https://github.com/theislab/diffsvg.git # (or `uv add`)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The default inference is exact and needs no sampler. To use NUTS (`inference="nuts"`, only needed for non-normal priors), install the optional extra: `pip install "diffsvg[pymc] @ git+https://github.com/theislab/diffsvg.git"`.
|
|
110
|
+
|
|
111
|
+
## Release notes
|
|
112
|
+
|
|
113
|
+
See the [changelog][].
|
|
114
|
+
|
|
115
|
+
## Contact
|
|
116
|
+
|
|
117
|
+
For questions and help requests, you can reach out in the [scverse discourse][].
|
|
118
|
+
If you found a bug, please use the [issue tracker][].
|
|
119
|
+
|
|
120
|
+
## Citation
|
|
121
|
+
|
|
122
|
+
> t.b.a
|
|
123
|
+
|
|
124
|
+
[uv]: https://github.com/astral-sh/uv
|
|
125
|
+
[scverse discourse]: https://discourse.scverse.org/
|
|
126
|
+
[issue tracker]: https://github.com/theislab/diffsvg/issues
|
|
127
|
+
[tests]: https://github.com/theislab/diffsvg/actions/workflows/test.yaml
|
|
128
|
+
[documentation]: https://diffsvg.readthedocs.io
|
|
129
|
+
[changelog]: https://diffsvg.readthedocs.io/page/changelog.html
|
|
130
|
+
[api documentation]: https://diffsvg.readthedocs.io/page/api.html
|
|
131
|
+
[pypi]: https://pypi.org/project/diffsvg
|
|
132
|
+
[venv]: https://docs.python.org/3/tutorial/venv.html
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
diffsvg/__init__.py,sha256=27tffAL8AnPIPAA9qmTpK63xhWzXn6iy6g9IbmiNxsk,55
|
|
2
|
+
diffsvg/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
diffsvg/pl/__init__.py,sha256=cQ9h7e9S0TjaqwOrCnk3CFsf917P3v-C9A3Yxr7MnEA,294
|
|
4
|
+
diffsvg/pl/plot_clusters.py,sha256=GNigoKu4-s3fZrE5Hcg5Yu91ThIOG5D8hbKXRVqVFVk,9721
|
|
5
|
+
diffsvg/pl/plot_results.py,sha256=WW-To73N9DKJw9NyQaQTgIL2ZLm9qsf2e223Y0yduZ0,18592
|
|
6
|
+
diffsvg/pl/plot_stability.py,sha256=NrXzxMwVyR5Mf8Qb-AzjsdwXHO8h0mY3_6gEoShaTl8,7588
|
|
7
|
+
diffsvg/pp/__init__.py,sha256=NqY-t6lR62vuInYDZu-PFF7uO5kuq9KApSaZ0_jPuDc,257
|
|
8
|
+
diffsvg/pp/utils.py,sha256=7ISyCr-UFjCL_Vsa_0PwhtX2Fsbfm3aMFgbq34l8Ln0,26413
|
|
9
|
+
diffsvg/tl/__init__.py,sha256=AEetgToMRQf8pi1KbrY3UULyl6UTb_B-60Ez3mm0Br0,483
|
|
10
|
+
diffsvg/tl/_exact.py,sha256=V6ZRyx90igdlQ8IQ_uokGxfHZEoAK4C6hmcixhd0EKc,14306
|
|
11
|
+
diffsvg/tl/clustering.py,sha256=rlN3DENhNvfMlv4TmOiCnyAdagh9CaNiEN_1X9oe0cM,6219
|
|
12
|
+
diffsvg/tl/model.py,sha256=9yQlkRLW3bmx177u0TnKUQldGjc6jF7d4wcUg3vmjss,81850
|
|
13
|
+
diffsvg/tl/stability.py,sha256=em2N03RyvxiyZgkSr2waFXCIlTABmWMrj8LEAsH5XHE,13188
|
|
14
|
+
diffsvg-0.1.0.dist-info/METADATA,sha256=-XbQHAGdm-978ZSV-OmzDF6X6EaHp2HZU58nrV-YgVM,4952
|
|
15
|
+
diffsvg-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
16
|
+
diffsvg-0.1.0.dist-info/licenses/LICENSE,sha256=NJjfrbWBi828T_GS40kAzIG9L3hqVmg0xD_zmOPCsoc,1517
|
|
17
|
+
diffsvg-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Tim Treis
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|