opencode-skills-collection 4.1.7 → 4.1.9

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 opencode-skills-collection might be problematic. Click here for more details.

Files changed (32) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +7 -1
  2. package/bundled-skills/court/SKILL.md +141 -0
  3. package/bundled-skills/court/references/jurors.json +14 -0
  4. package/bundled-skills/court/scripts/court.py +348 -0
  5. package/bundled-skills/darkmoon-pentest/SKILL.md +123 -0
  6. package/bundled-skills/lognorm/SKILL.md +86 -0
  7. package/bundled-skills/lognorm/references/playbooks.md +76 -0
  8. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +6 -6
  9. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +1 -1
  10. package/bundled-skills/parallel-search-mcp/SKILL.md +12 -0
  11. package/bundled-skills/scarf-single-cell/SKILL.md +326 -0
  12. package/bundled-skills/scarf-single-cell/references/LICENSE.md +29 -0
  13. package/bundled-skills/scarf-single-cell/references/clustering-and-embedding.md +270 -0
  14. package/bundled-skills/scarf-single-cell/references/data-access.md +269 -0
  15. package/bundled-skills/scarf-single-cell/references/features-and-graphs.md +279 -0
  16. package/bundled-skills/scarf-single-cell/references/gene-blacklists.md +182 -0
  17. package/bundled-skills/scarf-single-cell/references/integration-and-comparisons.md +295 -0
  18. package/bundled-skills/scarf-single-cell/references/markers-and-annotation.md +280 -0
  19. package/bundled-skills/scarf-single-cell/references/performance-and-export.md +291 -0
  20. package/bundled-skills/scarf-single-cell/references/pipeline-runs-and-artifacts.md +273 -0
  21. package/bundled-skills/scarf-single-cell/references/plotting.md +260 -0
  22. package/bundled-skills/scarf-single-cell/references/quality-control.md +290 -0
  23. package/bundled-skills/session-handoff/SKILL.md +112 -0
  24. package/bundled-skills/shark/SKILL.md +134 -0
  25. package/bundled-skills/shark/references/investors.json +12 -0
  26. package/bundled-skills/shark/scripts/shark.py +302 -0
  27. package/bundled-skills/telegram/assets/boilerplate/nodejs/package-lock.json +6 -6
  28. package/bundled-skills/telegram/assets/boilerplate/nodejs/package.json +1 -1
  29. package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/package-lock.json +6 -6
  30. package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/package.json +1 -1
  31. package/package.json +1 -1
  32. package/skills_index.json +341 -0
@@ -0,0 +1,270 @@
1
+ # Clustering and embedding
2
+
3
+ Partition a cell graph with Leiden or Paris, choose a partition with evidence, build UMAP or t-SNE
4
+ layouts, and extract labels and coordinates as cell-aligned arrays. Docs:
5
+ <https://scarf.readthedocs.io/en/latest/tutorials/clustering.html>, <https://scarf.readthedocs.io/en/latest/tutorials/dimensionality_reduction.html>,
6
+ <https://scarf.readthedocs.io/en/latest/reference/api/datastore.html>, <https://scarf.readthedocs.io/en/latest/reference/api/integration.html>.
7
+
8
+ ## When to use
9
+
10
+ - A completed run or an explicit graph exists, and you need other resolutions, a hierarchy or a
11
+ new layout on that same graph.
12
+ - You must justify a partition (resolution, seed, representation) before naming clusters.
13
+ - You need cluster labels or layout coordinates as a NumPy array or a DataFrame indexed by cell ID.
14
+
15
+ ## Key concepts
16
+
17
+ - Every producer takes an exact graph `ArtifactRef` (`run["connectivity_map"]` or an integrated
18
+ graph) and returns a new immutable `ArtifactRef`. None of them writes to `ds.cells`. Repeating a
19
+ call reuses the stored artifact; `invalidate_cache=True` creates another one.
20
+ - Payloads: Leiden `kind="cluster_labels"` stores `values` (int, numbered from 1); Paris
21
+ `kind="cluster_cut"` stores `labels`; UMAP and t-SNE `kind="embedding"` store `values` with shape
22
+ `(n_cells, dims)`.
23
+ - Payload rows follow the graph's frozen cell selection in stored-axis order, not every cell in
24
+ the store (3,847 of 5,025 cells in the 10x 5K PBMC docs dataset baseline). Recover that
25
+ selection with `ds.inspect_artifact(ref).input_ref("cell_selection")`.
26
+ - The default pipeline stores `leiden_0.5`, `leiden_0.75`, `leiden_1.0`, `leiden_1.25` and `paris`.
27
+ `run["clusters"]` is the Leiden candidate with the best PCA silhouette, recorded in
28
+ `run["cluster_selection"]`. Silhouette favours few compact clusters and can merge distinct
29
+ lineages or states. Treat that pick as a reproducible baseline only.
30
+ - Leiden is seeded (`random_seed=4444`), so a single run says nothing about stability. Agreement
31
+ across seeds, nested resolutions, markers and representations is the evidence.
32
+ - Leiden, Paris, layouts, membership strength and separability use only the graph or PCA and take
33
+ seconds. Marker search is the only step on this page that reads counts.
34
+
35
+ ## Recipes
36
+
37
+ ### Open the graph and baseline refs
38
+
39
+ ```python
40
+ import numpy as np
41
+ import pandas as pd
42
+ import scarf
43
+
44
+ scarf.configure_output(level="WARNING", progress=False)
45
+ ds = scarf.DataStore("analysis.zarr", nthreads=4, min_features_per_cell=-1) # writable
46
+ run = ds.pipeline.open(label="baseline")
47
+ graph = run["connectivity_map"]
48
+ print([key for key in run if key.startswith(("leiden", "paris", "cluster"))])
49
+
50
+ def labels_of(ref):
51
+ """Return a cluster payload as an array aligned to the ref's cell selection."""
52
+ key = "labels" if ref.kind == "cluster_cut" else "values"
53
+ return np.asarray(ds.load_artifact(ref)[key][:])
54
+ ```
55
+
56
+ ### Build the partition evidence table
57
+
58
+ One row per candidate: stability over seeds, nesting in the next coarser candidate, separability,
59
+ graph support and marker support. Each marker search streams counts once.
60
+
61
+ ```python
62
+ from itertools import combinations
63
+ from sklearn.metrics import adjusted_rand_score
64
+
65
+ leiden, values, rows = {}, {}, []
66
+ for resolution in (0.5, 1.0, 1.5):
67
+ refs = [ds.run_leiden_clustering(graph, resolution=resolution, random_seed=seed)
68
+ for seed in (4444, 1, 2)]
69
+ name = f"leiden_{resolution}"
70
+ leiden[name], values[name] = refs[0], labels_of(refs[0]) # seed 4444 = the pipeline's
71
+ seeds = [labels_of(ref) for ref in refs]
72
+ rows.append({"candidate": name, "n_clusters": len(np.unique(seeds[0])),
73
+ "smallest": pd.Series(seeds[0]).value_counts().min(),
74
+ "min_seed_ari": min(adjusted_rand_score(a, b) for a, b in combinations(seeds, 2))})
75
+ evidence = pd.DataFrame(rows).set_index("candidate")
76
+ names = list(leiden)
77
+ evidence["nested_frac"] = [np.nan] + [ # share of cells inside one cluster of the coarser one
78
+ pd.crosstab(values[fine], values[coarse]).max(axis=1).sum() / len(values[fine])
79
+ for coarse, fine in zip(names, names[1:])]
80
+ sep = ds.metric_cluster_separability(run["pca"], leiden) # coordinates the graph was built on
81
+ evidence = evidence.join(sep.clustering_scores.set_index("clustering")[
82
+ ["macro_f1_mean", "silhouette_score"]])
83
+ for name, ref in leiden.items():
84
+ strength = np.asarray(ds.load_artifact(ds.calc_membership_strength(ref, graph))["values"][:])
85
+ evidence.loc[name, "weak_cells"] = (strength < 0.5).mean()
86
+ per_cluster_strength = pd.Series(strength).groupby(values[name]).mean()
87
+ evidence.loc[name, "min_cluster_strength"] = per_cluster_strength.min()
88
+ found = ds.get_markers(marker=ds.run_marker_search(ref, features=run["feature_universe"]))
89
+ n_markers = found["group_id"].value_counts().reindex(
90
+ [str(g) for g in np.unique(values[name])], fill_value=0)
91
+ evidence.loc[name, "clusters_lt3_markers"] = int((n_markers < 3).sum())
92
+ print(evidence.round(3).to_string())
93
+ print(sep.cluster_scores.sort_values("f1_score").head(5)) # weakest individual clusters
94
+ nesting = ds.smart_label(leiden["leiden_1.0"], leiden["leiden_0.5"]) # "2a", "2b": splits of 2
95
+ print(pd.Series(labels_of(nesting)).value_counts().head(10))
96
+ selection = ds.load_artifact(run["cluster_selection"]) # the pipeline's silhouette choice
97
+ print(dict(zip(selection.attrs["candidateKeys"], np.asarray(selection["scores"][:]).round(3))))
98
+ print("selected:", selection.attrs["selectedKey"], "invalid:", selection.attrs["invalidReasons"])
99
+ ```
100
+
101
+ Membership strength is the fraction of a cell's graph neighbours sharing its most common label.
102
+ Pass the Harmony ref instead of `run["pca"]` when the graph was built on Harmony coordinates.
103
+
104
+ ### QC and doublet covariates per cluster
105
+
106
+ ```python
107
+ in_run = run.cells.fetch_all("I").astype(bool)
108
+ per_cell = pd.DataFrame({c: ds.cells.fetch_all(c)[in_run]
109
+ for c in ("RNA_nCounts", "RNA_nFeatures", "RNA_percentMito")})
110
+ per_cell["doublet_score"] = run.cells.fetch("doublet_score")
111
+ per_cluster = per_cell.groupby(values["leiden_1.0"]).median()
112
+ print(per_cluster.round(2), per_cell.median().round(2), sep="\n") # each cluster vs all cells
113
+ ```
114
+
115
+ A cluster that stands out only on counts, mito or doublet score is technical until markers say
116
+ otherwise. Doublet scores run high in high-RNA types (plasma cells), so compare within a lineage.
117
+
118
+ ### Representation sensitivity
119
+
120
+ Repeat the evidence on one branch (more HVGs, other PCs or `k`; `features-and-graphs.md`). The
121
+ cells must match, so labels compare directly.
122
+
123
+ ```python
124
+ alt = ds.pipeline.run(label="hvg2k_pc30_k21", hvg_count=2000, pca_dims=30, neighbors_k=21)
125
+ assert alt["analysis_cell_selection"] == run["analysis_cell_selection"]
126
+ alt_refs = [ds.run_leiden_clustering(alt["connectivity_map"], resolution=resolution)
127
+ for resolution in (0.5, 1.0, 1.5)]
128
+ for name, ref in leiden.items(): # best match over branch resolutions; k shifts the scale
129
+ evidence.loc[name, "ari_vs_branch"] = max(ds.metric_label_concordance(ref, other)
130
+ for other in alt_refs)
131
+ print(evidence.round(3).to_string())
132
+ ```
133
+
134
+ ### Choose the partition
135
+
136
+ `run["clusters"]` (the silhouette pick) is a baseline candidate, never the answer by itself.
137
+ Combine the evidence; no single score decides.
138
+
139
+ 1. Stability: keep candidates whose `min_seed_ari` stays near the coarsest one (about 0.9 or
140
+ more is a rule of thumb). Below that, the optimizer moves boundaries between seeds.
141
+ 2. Nesting: prefer a candidate that refines the coarser one (`nested_frac` near 1, `smart_label`
142
+ shows "2a", "2b"). A non-nested jump, where many cells change parent, is a reason to reject it.
143
+ 3. Markers: every cluster a finer candidate adds needs its own specific markers (about 3 or more
144
+ at default `get_markers` filters, named genes, not only ribosomal, mitochondrial or ambient).
145
+ 4. Graph support: added clusters keep high mean membership strength and few weak cells.
146
+ 5. QC and doublets: a split explained by counts, mito or doublet score is not a cell type. Keep it
147
+ as `unresolved` (`markers-and-annotation.md`) instead of naming it.
148
+ 6. Representation: boundaries that survive an HVG, PC or `k` branch are robust; those that move
149
+ are provisional and need markers to stand.
150
+
151
+ Choose the finest candidate whose added splits all pass 1 to 5, and report 6. If the silhouette
152
+ pick merges populations that markers separate, a finer candidate that passes wins. Prefer a coarser
153
+ global partition plus targeted subclustering (`features-and-graphs.md`, "Subcluster one lineage")
154
+ when the lineage split is stable at a low resolution but finer global resolutions lose seed
155
+ stability, break nesting, or split only one lineage. The usual case is T and NK subsets in blood:
156
+ global HVGs are dominated by between-lineage genes, so a within-lineage graph resolves states
157
+ (naive, memory, regulatory, cytotoxic) that a finer global cut either misses or splits unstably.
158
+
159
+ (10x 5K PBMC docs dataset) Minimum seed ARI was 0.97, 0.82 and 0.60 at 0.5, 1.0 and 1.5; at 1.0,
160
+ four clusters had fewer than 3 markers; ARI with the branch fell from 0.86 to 0.61. The evidence
161
+ kept 0.5. A T-cell subgraph then gave a FOXP3/CTLA4 group (FOXP3 in 0.43 of cells); no global
162
+ cluster up to resolution 2.0 exceeded 0.22.
163
+
164
+ ### Build a Paris hierarchy
165
+
166
+ ```python
167
+ from dataclasses import asdict
168
+
169
+ paris = ds.run_paris_clustering(graph) # n_clusters="auto"; reuses run["paris"] when identical
170
+ result = ds.load_paris_clustering(paris)
171
+ diag = pd.DataFrame([asdict(item) for item in result.diagnostics])
172
+ print(result.n_clusters, diag[["label", "size", "persistence", "decision_margin", "forced"]])
173
+ paris_8 = ds.run_paris_clustering(graph, n_clusters=8)
174
+ print(ds.metric_label_concordance(paris, leiden["leiden_0.5"]))
175
+ ```
176
+
177
+ High `persistence` means a branch survives over a wide range of the hierarchy. `forced=True` groups
178
+ satisfy a structural constraint and are not, by themselves, biological evidence.
179
+
180
+ ### Build UMAP and t-SNE layouts
181
+
182
+ ```python
183
+ init = run["embedding_initialization"] # K-means initialization built on run["pca"]
184
+ umap_tight = ds.run_umap(graph, init, min_dist=0.5)
185
+ tsne = ds.run_tsne(graph, init, verbose=False)
186
+ # For new coordinates, build a matching init first:
187
+ # init = ds.build_embedding_initialization(new_pca_ref)
188
+ print(ds.load_artifact(umap_tight)["values"].shape, ds.load_artifact(tsne)["values"].shape)
189
+ ```
190
+
191
+ ### Get labels and coordinates from a pipeline run
192
+
193
+ ```python
194
+ cells = run.cells.to_pandas_dataframe(
195
+ ["ids", "clusters", "leiden_1.0", "paris", "umap_1", "umap_2", "doublet_score"])
196
+ print(cells.head()) # one row per run cell, in stored-axis order
197
+ full = run.cells.fetch_all("clusters") # length ds.cells.N; -1 for cells outside the run
198
+ print(len(full), (full == -1).sum())
199
+ ```
200
+
201
+ `run.cells.columns` lists the frozen fields. `fetch(...)` returns run cells only. `fetch_all(...)`
202
+ fills cells outside the run with -1 (integers), NaN (floats) or "" (strings).
203
+
204
+ ### Get labels and coordinates from explicit artifacts
205
+
206
+ ```python
207
+ def cell_ids_of(ref):
208
+ selection = ds.inspect_artifact(ref).input_ref("cell_selection")
209
+ mask = np.asarray(ds.load_artifact(selection)["values"][:], dtype=bool)
210
+ return ds.cells.fetch_all("ids")[mask]
211
+
212
+ ids = cell_ids_of(umap_tight)
213
+ assert np.array_equal(ids, cell_ids_of(leiden["leiden_1.5"])) # same frozen selection
214
+ xy = np.asarray(ds.load_artifact(umap_tight)["values"][:])
215
+ frame = pd.DataFrame({"leiden_1.5": labels_of(leiden["leiden_1.5"]),
216
+ "x": xy[:, 0], "y": xy[:, 1]},
217
+ index=pd.Index(ids, name="cell_id"))
218
+ print(frame.head())
219
+ ```
220
+
221
+ The same helper works for membership strength, doublet scores, cell cycle and enrichment scores.
222
+
223
+ ## Parameters that matter
224
+
225
+ | Parameter | Default | Change when |
226
+ |---|---|---|
227
+ | `resolution` (Leiden) | `1.0` (pipeline: 0.5, 0.75, 1.0, 1.25) | Raise for finer splits, lower to merge; always compare several |
228
+ | `random_seed` (Leiden) | `4444` | Rerun with 2 or more other seeds to test stability |
229
+ | `backend` (Leiden) | `"igraph"` | `"leidenalg"` only to match an external result |
230
+ | `n_clusters` (Paris) | `"auto"` | An integer to match another partition; it must be cuttable at one height, else `ValueError` |
231
+ | `min_cluster_size` (Paris) | `None` (resolved automatically) | Raise to suppress tiny branches in the auto cut |
232
+ | `min_dist` / `spread` (UMAP) | `1.0` / `2.0` | Lower `min_dist` (0.1 to 0.5) for tighter packing; appearance only |
233
+ | `use_density_map` (UMAP) | `False` | `True` for densMAP when relative density matters |
234
+ | `parallel` (UMAP, t-SNE) | `False` | `True` is faster but not reproducible |
235
+
236
+ ## Check before moving on
237
+
238
+ - The evidence table exists for at least three candidates, and the chosen one is justified by
239
+ the six criteria above, not by silhouette alone. Record why coarser and finer were rejected.
240
+ - Every cluster under about 20 cells, or with mean membership strength well below the rest, has
241
+ been reviewed: doublet group, transition, contaminant or over-split.
242
+ - No cluster is named whose only distinction is a QC metric or doublet score.
243
+ - Lineages that need finer structure are listed for targeted subclustering, with the parent
244
+ partition kept for audit.
245
+
246
+ ## Pitfalls
247
+
248
+ - Paris stores `labels`, not `values`. `ds.load_artifact(paris)["values"]` raises `KeyError`.
249
+ - Payload length is the selection size, not `ds.cells.N`. Align through the cell selection before
250
+ inserting into `ds.cells` or joining with other metadata.
251
+ - Cluster IDs are arbitrary per artifact. Never carry annotations from one partition to another by
252
+ ID; join on cell IDs or use `smart_label`.
253
+ - `metric_label_concordance` requires both artifacts to share the exact cell-selection ref. For
254
+ labels from different selections or from metadata, use sklearn on aligned arrays.
255
+ - Comparing branches at the same resolution only: `k` and HVG count shift the resolution scale.
256
+ - UMAP and t-SNE distances, empty space and island size are not biological measurements. Never
257
+ choose a resolution from a layout alone.
258
+ - A read-only store (`zarr_mode="r"`) raises `PermissionError` unless an identical artifact exists.
259
+ - `run_tsne` uses an `sgtsne` executable on `PATH` when present, otherwise the `sgtsnepi` Python
260
+ package. The docs state that t-SNE is unsupported on macOS and Windows.
261
+
262
+ ## See also
263
+
264
+ - `features-and-graphs.md`: building graphs, HVG and `k` branches, subclustering one lineage.
265
+ - `markers-and-annotation.md`: marker tables and labels for the chosen partition.
266
+ - `quality-control.md`: doublet scores and QC covariates behind technical clusters.
267
+ - `plotting.md`: `ds.plots.embedding(layout=umap_ref, color_by=cluster_ref)`,
268
+ `cluster_connectivity(graph=, groups=, layout=)` and `cluster_tree(graph=, clusters=paris_ref)`.
269
+ - `pipeline-runs-and-artifacts.md`: run keys, `leiden=` pipeline settings, lineage.
270
+ - `integration-and-comparisons.md`: clustering on integrated graphs and batch metrics.
@@ -0,0 +1,269 @@
1
+ # Data access
2
+
3
+ Open, inspect, import, and connect to Scarf DataStores, including Cytebase remote and mounted
4
+ stores. Docs: <https://scarf.readthedocs.io/en/latest/tutorials/data_organization.html>,
5
+ <https://scarf.readthedocs.io/en/latest/tutorials/import_and_export.html>, <https://scarf.readthedocs.io/en/latest/tutorials/cytebase.html>,
6
+ <https://scarf.readthedocs.io/en/latest/tutorials/remote_stores.html>, <https://scarf.readthedocs.io/en/latest/reference/api/datastore.html>, `import_export.html`, `cytebase.html` (same folder).
7
+
8
+ ## When to use
9
+
10
+ - First contact with any dataset: choose read-only or writable, then inspect before computing.
11
+ - Converting 10x, Matrix Market, H5AD, or Seurat RDS input into a Scarf Zarr store.
12
+ - Finding a Cytebase dataset, exploring it remotely, or mounting it for analysis that saves results.
13
+ - Reopening a store or a mount from an earlier session.
14
+
15
+ ## Key concepts
16
+
17
+ - A store is one Zarr directory: `cellData` (shared cell table, `ds.cells`), one group per assay
18
+ (`ds.RNA`; feature table `ds.RNA.feats`; counts; assay artifacts), datastore-scoped artifacts,
19
+ and `pipeline/runs`. Cell QC columns are assay-prefixed: `RNA_nCounts`, `RNA_nFeatures`,
20
+ `RNA_percentMito`, `RNA_percentRibo`. Tables are `MetaData` objects, not DataFrames.
21
+ - `I` is the live Boolean cell key (feature tables have one too). `fetch(col)` returns rows where
22
+ `I` is true; `fetch_all(col)` returns every row. Analytical filtering returns a cell-selection
23
+ artifact and never edits `I`.
24
+ - `zarr_mode="r+"` is the default. A writable open WRITES: first-open preparation of a freshly
25
+ written store (QC columns, feature `nCells`/`dropOuts`), the `min_features_per_cell` filter
26
+ applied to `I`, and the `defaultAssay`/`assayTypes` attributes. `zarr_mode="r"` writes nothing;
27
+ producers return a matching existing artifact but raise `PermissionError` instead of computing.
28
+ - RNA assays hold cell-major `counts` plus gene-major `countsT`; other assays hold `counts` only.
29
+ Stores from older releases (Zarr v2, no `countsT`, an `{assay}/state` group) fail to open:
30
+ re-import the source.
31
+ - A mount is a local writable target whose counts resolve from a separate source recorded in the
32
+ root `matrixSource` attribute (absolute path or URI). Metadata is copied once at mount time; new
33
+ artifacts are written only to the target.
34
+ - Cytebase has three layers: `Catalog` (verified local DuckDB copy of the catalog, no counts),
35
+ `open_datastore` (read-only, everything remote), `mount_datastore` (local writable target,
36
+ counts remote, plus a `<target>.cytebase.json` receipt beside it).
37
+ - `workspace` names a group inside one Zarr with its own `cellData`, assays, and artifacts (counts
38
+ under `matrices/`). Writers default to `None` (root layout); keep `None` unless the store was
39
+ written with a workspace. A mount records one source workspace and rejects another.
40
+
41
+ ## Recipes
42
+
43
+ ### Open and inspect a store
44
+
45
+ Design and annotation inference starts with `scripts/inspect_store.py STORE.zarr`, which ships with
46
+ the maintained copy of this skill in the Scarf repository (this catalogue copy does not include
47
+ it). Read-only, it flags annotation-like columns (values hidden unless
48
+ `--show-annotation-values`), design-like, author-derived and unflagged columns, and checks
49
+ whether counts look raw, corrected or pre-filtered. Flags are name-based hints: review every
50
+ column. By hand, open read-only until you need to write; `summary()` is metadata-only.
51
+
52
+ ```python
53
+ import scarf
54
+
55
+ scarf.configure_output(level="WARNING", progress=False)
56
+ ds = scarf.DataStore("analysis.zarr", zarr_mode="r")
57
+ print(ds) # "N_active (N_total) cells", assays, column names
58
+ snap = ds.summary().to_dict() # JSON-safe
59
+ print(snap["default_assay"], snap["total_cells"], snap["active_cells"], snap["resources"])
60
+ print(snap["pipeline_run_counts"], snap["labeled_pipeline_runs"])
61
+ print([(a["name"], a["assay_type"], a["total_features"], len(a["artifacts"])) for a in snap["assays"]])
62
+ print(ds.pipeline.list_runs()) # newest first
63
+ print(ds.list_artifacts(complete_only=True)) # default-assay artifacts
64
+ print(ds.list_artifacts(scope="datastore", complete_only=True)) # cell selections, snapshots
65
+ ds.show_zarr_tree(start="RNA", depth=1)
66
+
67
+ # Metadata: request only the columns you need
68
+ meta = ds.cells.to_pandas_dataframe(["ids", "RNA_nCounts", "RNA_nFeatures"], key="I").set_index("ids")
69
+ active_ids = ds.cells.fetch("ids") # rows where I is True
70
+ all_ids = ds.cells.fetch_all("ids") # every row
71
+ genes = ds.RNA.feats.to_pandas_dataframe(["ids", "names", "nCells"])
72
+ by_upper = {str(n).upper(): str(n) for n in ds.RNA.feats.fetch_all("names")} # case-safe lookup
73
+ ```
74
+
75
+ ### Convert inputs to Zarr
76
+
77
+ Every writer follows reader then `*ToZarr(...).dump()`. Open each new store writable once.
78
+
79
+ ```python
80
+ # 10x HDF5: assays inferred from feature types (RNA, ADT, ATAC)
81
+ reader = scarf.CrH5Reader("filtered_feature_bc_matrix.h5")
82
+ print(reader.assayFeats) # assays and feature ranges that will be written
83
+ scarf.CrToZarr(reader, zarr_loc="pbmc.zarr", mem_budget="4G").dump()
84
+ # A default count layout that does not fit mem_budget raises MemoryError before writing and
85
+ # names a smaller policy=CountMatrixPolicy(...) (scarf.storage.count_matrix); raise
86
+ # mem_budget or pass that policy.
87
+ ds = scarf.DataStore("pbmc.zarr", default_assay="RNA") # required when there are 2+ assays
88
+
89
+ # 10x directory (matrix.mtx, genes/features.tsv, barcodes.tsv; .gz accepted)
90
+ scarf.CrToZarr(scarf.CrDirReader("filtered_feature_bc_matrix"), zarr_loc="dir.zarr").dump()
91
+
92
+ # Matrix Market: inspect, then pick one candidate explicitly
93
+ candidates = scarf.inspect_mtx("mtx_dir")
94
+ reader = scarf.MtxReader(candidates[0])
95
+ print(reader.assayFeats)
96
+ scarf.MtxToZarr(reader, zarr_loc="mtx.zarr").dump()
97
+
98
+ # H5AD: inspect first; selected obsm/obs values become artifacts, not live columns
99
+ import h5py
100
+
101
+ insp = scarf.inspect_h5ad("data.h5ad")
102
+ print(insp.matrixKey, insp.matrixCandidates, insp.integerLike, insp.layers, insp.suggestedAssays)
103
+ with h5py.File("data.h5ad", "r") as h5: # pass only keys the file has; a missing one is a KeyError
104
+ obsm = set(h5["obsm"]) if "obsm" in h5 else set()
105
+ obs = h5["obs"]
106
+ obs_cols = set(obs) if isinstance(obs, h5py.Group) else set(obs.dtype.names) # old files
107
+ print(sorted(obsm), sorted(obs_cols))
108
+ roles = {k: r for k, r in {"X_umap": "umap", "X_tsne": "tsne"}.items() if k in obsm}
109
+ kw = dict(embedding_roles=roles, cluster_keys=tuple(c for c in ("clusters",) if c in obs_cols))
110
+ reader = scarf.H5adReader.from_inspect(insp, **kw)
111
+ res = scarf.H5adToZarr(reader, zarr_loc="h5ad.zarr").dump()
112
+ print(dict(res.embeddingArtifacts), dict(res.clusterArtifacts))
113
+
114
+ # Seurat RDS (an on-disk .rds; .h5seurat is not read)
115
+ si = scarf.inspect_seurat("pbmc.rds")
116
+ reductions = [r.name for r in si.reductions if r.importable]
117
+ with scarf.SeuratReader("pbmc.rds", assays=[si.activeAssay], reductions=reductions) as sr:
118
+ out = scarf.SeuratToZarr(sr, zarr_loc="seurat.zarr").dump()
119
+ print(out.defaultAssay, dict(out.reductionArtifacts), out.activeIdentity, len(out.notices))
120
+
121
+ for path in ("dir.zarr", "mtx.zarr", "h5ad.zarr", "seurat.zarr"):
122
+ scarf.DataStore(path) # writable first open prepares the store
123
+ ```
124
+
125
+ `CSVReader`/`CSVtoZarr` (small dense CSV) and `SparseToZarr` (SciPy CSR plus IDs) also exist.
126
+
127
+ Every converter refuses this way from 1.0.0rc18 on (1.0.0rc17 shrinks the layout to fit). The
128
+ error is a `MemoryError`; releases after 1.0.0rc19 raise its subclass `CountLayoutMemoryError`, so
129
+ catch `MemoryError`. Prefer a larger `mem_budget` when the host has
130
+ the memory. Otherwise copy the policy numbers from the message exactly and record that choice:
131
+ smaller layouts make every later gene-major read slower.
132
+
133
+ ```python
134
+ from scarf.storage.count_matrix import CountMatrixPolicy
135
+
136
+ policy = CountMatrixPolicy(unitBytes=3906250, chunkBytes=390625) # copied from the message
137
+ scarf.CrToZarr(reader, zarr_loc="pbmc.zarr", mem_budget="128M", policy=policy).dump()
138
+ ```
139
+
140
+ The need follows genes per cell relative to the gene count, not the number of cells. The 1K PBMC
141
+ CITE-seq file refuses at `512M` and imports at `1G`.
142
+
143
+ ### Find a Cytebase dataset
144
+
145
+ ```python
146
+ from scarf import cytebase
147
+
148
+ catalog = cytebase.Catalog() # public Nygen/cytebase, no credentials
149
+ hits = catalog.search("bladder immune", limit=5) # every word, case-insensitive
150
+ ids = [row["cytebase_id"] for row in hits] # take IDs from rows, not printed tables
151
+ blood = catalog.find_datasets(tissue="blood", organism="Homo sapiens") # exact labels
152
+ assay_labels = [row["label"] for row in catalog.list_terms("assay")] # exact facet labels
153
+ small = catalog.query(
154
+ "SELECT cytebase_id, cell_count FROM datasets "
155
+ "WHERE status = ? AND cell_count < ? ORDER BY cell_count LIMIT 3",
156
+ parameters=["ready", 5000],
157
+ )
158
+ entry = catalog.dataset(ids[0])
159
+ print(entry.describe()) # Markdown summary; does not open the store
160
+ print(entry.cell_count, entry.source_embeddings()) # source obsm keys, imported or not
161
+ ```
162
+
163
+ ### Explore a Cytebase dataset read-only
164
+
165
+ ```python
166
+ ds = catalog.open_datastore(entry.id) # zarr_mode="r", min_features_per_cell=-1
167
+ print(cytebase.embeddings(ds)) # {"X_umap": ArtifactRef}: keys actually imported
168
+ umap_ref = cytebase.embedding(ds, "X_umap")
169
+ coords = cytebase.embedding_coordinates(ds, umap_ref) # DataFrame indexed by cell id
170
+ meta = ds.cells.to_pandas_dataframe(["ids", "donor_id"], key="I").set_index("ids") # a design column
171
+ frame = coords.join(meta) # join on ids, never on row order
172
+ ```
173
+
174
+ ### Mount a Cytebase dataset for writable analysis
175
+
176
+ The target must be a new local path. Mounting copies metadata and verifies the source; expect a
177
+ few minutes for a few thousand cells.
178
+
179
+ ```python
180
+ analysis = catalog.mount_datastore(entry.id, at="work/analysis.zarr")
181
+ # Writes work/analysis.zarr (copied metadata, future artifacts) and work/analysis.zarr.cytebase.json
182
+ print(analysis.zarr_mode, int(analysis.cells.fetch_all("I").sum()), analysis.cells.N)
183
+ print(cytebase.embeddings(analysis)) # {}: source artifacts are not copied into a mount
184
+ ```
185
+
186
+ ### Reopen a mount, or mount any store that owns its counts
187
+
188
+ ```python
189
+ # Verify against the current published build (needs the .cytebase.json sidecar beside it)
190
+ analysis = catalog.mount_datastore(entry.id, at="work/analysis.zarr")
191
+ # Or open it like any store: counts resolve from matrixSource; no catalog or sidecar needed
192
+ analysis = scarf.DataStore("work/analysis.zarr", min_features_per_cell=-1)
193
+ # Generic mount; for s3:// or gs:// sources also pass storage_options (not executed here)
194
+ mounted = scarf.mount_datastore("shared/data.zarr", at="my_analysis.zarr", default_assay="RNA")
195
+ ```
196
+
197
+ ### Download a documentation dataset
198
+
199
+ ```python
200
+ repo = scarf.cytebase.connect("scarf_docs")
201
+ print(repo.list_datasets())
202
+ path = repo.download_dataset("tenx_5K_pbmc_rnaseq", destination="scarf_datasets", zarr=True)
203
+ ds = scarf.DataStore(f"{path}/data.zarr", zarr_mode="r") # prepared; run label "docs_default"
204
+ raw = repo.download_dataset("xin_1K_pancreas_rnaseq", destination="scarf_datasets") # source files
205
+ ```
206
+
207
+ ## Parameters that matter
208
+
209
+ | Parameter | Default | Change when |
210
+ |---|---|---|
211
+ | `zarr_mode` | `"r+"` | `"r"` to inspect, share, or protect a store; a fresh store needs one `"r+"` open |
212
+ | `default_assay` | stored value, or the only assay | first open of a multi-assay store (otherwise `ValueError`) |
213
+ | `min_features_per_cell` | `10` | `-1` to leave `I` untouched on writable opens |
214
+ | `mito_pattern` / `ribo_pattern` | `None` (`^MT-`, `^RPS\|^RPL\|^MRPS\|^MRPL`) | non-human genes (`^mt-`), only on the FIRST writable open |
215
+ | `assay_types` | inferred from assay names | custom names, e.g. `{"GEX": "RNA"}` |
216
+ | `nthreads`, `mem_budget` | env `SCARF_WORKERS`, `SCARF_MEM_BUDGET`, else detected | see `performance-and-export.md` |
217
+ | `workspace` | `None` | store written into a named workspace |
218
+ | `storage_options` | `None` | object-store credentials or endpoints (read from env vars) |
219
+ | `zarrProfile` | from location (`fast_local`/`cloud`) | affects newly written arrays only |
220
+ | `Catalog(bucket=, token=)` | `Nygen/cytebase`, `None` (HF login if present) | private bucket; `token=False` forces anonymous |
221
+ | `search(limit=)`, `ready_only=` | `50`, `True` | `limit=None` for all; `ready_only=False` for unfinished |
222
+
223
+ ## Check before moving on
224
+
225
+ - `print(ds)`: active versus total cells, expected assays, QC columns present.
226
+ - `snap["default_assay"]` is the assay you intend; `snap["labeled_pipeline_runs"]` and
227
+ `list_artifacts` show existing work to reuse (see `pipeline-runs-and-artifacts.md`).
228
+ - Imports: `reader.assayFeats` lists the assays to be written; afterwards check
229
+ `ds.RNA.rawData.shape` and dtype. For H5AD, `insp.integerLike` should be `True` for raw counts,
230
+ but SCT-corrected counts are integers too: run "Check the matrix first" in `quality-control.md`.
231
+ - Cytebase: `entry.row["status"] == "ready"`; check `cytebase.embeddings(ds)` before plotting an
232
+ imported layout.
233
+ - Mounts: `<target>.cytebase.json` exists beside the target; `ds.RNA.rawData.shape` resolves.
234
+
235
+ ## Pitfalls
236
+
237
+ - A writable open with `min_features_per_cell=k` silently removes cells with at most `k` features
238
+ from `I` and persists it. Reopening with a lower value does not restore cells;
239
+ `ds.cells.reset_key("I")` does.
240
+ - Stores just written by a writer or `SubsetZarr` raise `Assay 'RNA' is not prepared. Rebuild ...`
241
+ when opened with `zarr_mode="r"`. Open once with `"r+"`; no rebuild is needed.
242
+ - Percent patterns are fixed at first preparation; a different pattern later raises `ValueError`.
243
+ Use `run_feature_percentage` (see `quality-control.md`) for another gene set.
244
+ - `open_datastore` rejects writes and any `min_features_per_cell` other than `-1`. Both mount
245
+ functions reject `zarr_mode="r"`; `scarf.mount_datastore` refuses an existing target, while
246
+ `catalog.mount_datastore` reopens one only when its matching sidecar is present.
247
+ - Catalog reopen needs the sidecar: copying a mount without `<name>.cytebase.json` makes
248
+ `catalog.mount_datastore` raise `FileExistsError`; plain `scarf.DataStore` still opens it.
249
+ - The mount source must stay at its recorded path or URI. Mounting a mount raises; repack first.
250
+ A changed Cytebase build requires a new mount directory.
251
+ - Printed `CatalogResults` tables HTML-escape text (`_` as `&#95;`, `'` as `&#39;`). Read IDs and
252
+ facet labels from the row dicts (`row["cytebase_id"]`, `row["label"]`), never from the table.
253
+ - A fresh mount has no imported source embeddings. Plot them from the read-only `ds` and join by id.
254
+ - `to_pandas_dataframe(columns)` defaults to `key=None` (all rows); pass `key="I"` for active cells.
255
+ - `to_mtx(..., compress=True)` writes the feature `feature_type` column as the 10x feature type.
256
+ CELLxGENE stores keep gene biotypes there, so re-import split one assay into thousands. Inspect
257
+ `reader.assayFeats` before `dump()` or export with `compress=False`.
258
+ - Source column names containing `/` or `\` are stored with `_`. Reserved columns `ids`, `names`,
259
+ `I` from a source are skipped. H5AD multi-assay import needs `assay_split_key` on `H5adToZarr`.
260
+ - `download_dataset(..., zarr=True)` keeps `data.zarr.tar.gz` beside `data.zarr` (double disk).
261
+ - Public atlas matrices (CELLxGENE, Seurat submissions) can hold SCT-corrected counts in `raw.X`,
262
+ with genes such as rRNA removed and cells already filtered. Check before QC or count models.
263
+
264
+ ## See also
265
+
266
+ - `performance-and-export.md`: budgets, remote I/O cost, repacking a mount, exports.
267
+ - `pipeline-runs-and-artifacts.md`: runs, artifact inspection, lineage.
268
+ - `quality-control.md`: filtering cells without editing `I`.
269
+ - `plotting.md`: `ds.plots.embedding(layout=umap_ref, ...)` for imported layouts.