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,279 @@
1
+ # Features and graphs
2
+
3
+ Select features, normalize, reduce with PCA and build the neighbourhood graph as an explicit
4
+ chain of artifacts, then branch or subcluster it safely. Docs: <https://scarf.readthedocs.io/en/latest/tutorials/feature_selection.html>,
5
+ <https://scarf.readthedocs.io/en/latest/tutorials/graph_construction.html>, <https://scarf.readthedocs.io/en/latest/tutorials/dimensionality_reduction.html> and
6
+ <https://scarf.readthedocs.io/en/latest/reference/api/graph_construction.html>.
7
+
8
+ ## When to use
9
+
10
+ - A pipeline default (`hvg_count=1000`, `pca_dims=21`, `neighbors_k=11`) needs to change, or two
11
+ settings need comparing side by side.
12
+ - Building a graph on a custom cell selection (QC variant, subset) or feature list.
13
+ - Subclustering one lineage of a coarse partition with its own HVGs, PCA and graph.
14
+ - Diagnosing a graph before clustering or embedding.
15
+
16
+ ## Key concepts
17
+
18
+ - RNA chain, each step returning an `ArtifactRef` that the next step consumes:
19
+ cell selection + `select_hvgs` (`feature_selection`) -> `run_normalization` (`normalized`) ->
20
+ `run_pca` (`reduction`) -> optional `run_harmony` -> `build_ann_index` (`ann_index`) ->
21
+ `query_neighbors` (`neighbors`) -> `build_connectivity_map` (`connectivity_map`).
22
+ `build_embedding_initialization(pca)` is a side branch that seeds UMAP and t-SNE only.
23
+ - Identity is operation + scientific parameters + exact inputs. An identical call returns the
24
+ existing ref without recomputing; a changed `k` reuses normalization, PCA and the ANN index.
25
+ Changing cells or features invalidates every downstream stage.
26
+ - `ds.pipeline.run(hvg_count=, pca_dims=, neighbors_k=)` forwards to `select_hvgs(top_n=)`,
27
+ `run_pca(dims=)` and `query_neighbors(k=)`. Explicit calls with the same arguments return the
28
+ very refs in `run[...]`. `pca_dims=0` skips PCA and builds the graph on normalized HVG values.
29
+ - RNA normalization: counts scaled to 1000 over the selected features, then `log1p`. PCA
30
+ standardizes features. HVGs are an artifact, not a feature column; the chain writes no metadata.
31
+ - Count passes: a new cell selection costs one pass for the feature summary (`select_hvgs`,
32
+ `select_detected_features`) and one per `run_normalization`; later stages read local artifacts.
33
+
34
+ ## Recipes
35
+
36
+ ### Rebuild the chain explicitly from a run
37
+
38
+ Start from any frozen cell selection: `run["analysis_cell_selection"]`, a QC ref, or
39
+ `ds.snapshot_cell_selection("I")`.
40
+
41
+ ```python
42
+ import numpy as np
43
+ import pandas as pd
44
+ import scarf
45
+ import scarf.plotting as splt
46
+
47
+ ds = scarf.DataStore("analysis.zarr", min_features_per_cell=-1) # reopen; see data-access.md
48
+ run = ds.pipeline.open(label="baseline")
49
+ cells = run["analysis_cell_selection"]
50
+
51
+ hvg = ds.select_hvgs(cells, top_n=1000, show_plot=False)
52
+ normalized = ds.run_normalization(cells, hvg)
53
+ pca = ds.run_pca(normalized, dims=21)
54
+ ann = ds.build_ann_index(pca)
55
+ neighbors = ds.query_neighbors(ann, k=11)
56
+ graph = ds.build_connectivity_map(neighbors)
57
+ init = ds.build_embedding_initialization(pca)
58
+ print({key: ref == run[key] for key, ref in {
59
+ "highly_variable_features": hvg, "normalized": normalized, "pca": pca, "ann_index": ann,
60
+ "neighbors": neighbors, "connectivity_map": graph, "embedding_initialization": init}.items()})
61
+ ```
62
+
63
+ ### Feature-selection diagnostics
64
+
65
+ The HVG artifact stores `values` (selected mask) and `corrected_variance` for every feature.
66
+
67
+ ```python
68
+ from scarf.features.gene_families import GENE_FAMILY_PATTERNS
69
+
70
+ hvg_group = ds.load_artifact(hvg)
71
+ selected = np.asarray(hvg_group["values"][:], dtype=bool)
72
+ corrected = np.asarray(hvg_group["corrected_variance"][:])
73
+ names = ds.RNA.feats.fetch_all("names").astype(str)
74
+ print(int(selected.sum()), pd.Series(corrected[selected], index=names[selected]).nlargest(15).round(1).to_dict())
75
+ panel = ["CD3D", "MS4A1", "LYZ", "NKG7", "PPBP", "JCHAIN"]
76
+ print({gene: bool(selected[names == gene].any()) for gene in panel})
77
+ print({family: len(ds.RNA.feats.grep(p)) for family, p in GENE_FAMILY_PATTERNS.items()})
78
+
79
+ # Headless mean-variance plot. show_plot=True only displays, then closes, its figure.
80
+ summary = ds.load_artifact(ds.inspect_artifact(hvg).input_ref("feature_summary"))
81
+ n_cells = np.asarray(summary["normed_n"][:], dtype=float)
82
+ total = np.asarray(summary["normed_tot"][:], dtype=float)
83
+ splt.highly_variable_features(
84
+ mean_nonzero=np.divide(total, n_cells, out=np.zeros_like(total), where=n_cells > 0),
85
+ corrected_variance=corrected, n_cells=n_cells, selected=selected, show=False,
86
+ ).save("hvg_mean_variance.png")
87
+ ```
88
+
89
+ (10x 5K PBMC docs dataset) The top HVGs were platelet (`PPBP`, `NRGN`), light-chain (`IGLC2`,
90
+ `IGKC`) and `IGHM` genes, which the default blacklist keeps. What to exclude: `gene-blacklists.md`.
91
+
92
+ ### Alternative feature sets and a custom blacklist
93
+
94
+ `blacklist` is one regex, matched (`re.match`, ignoring case) against every feature name. It
95
+ replaces the default entirely, so pass a complete string: the default plus add-ons, or a full
96
+ string from `gene-blacklists.md`.
97
+
98
+ ```python
99
+ from scarf.features.variability import DEFAULT_HVG_BLACKLIST
100
+
101
+ base = DEFAULT_HVG_BLACKLIST # or a complete string chosen with gene-blacklists.md
102
+ extra_patterns = [] # add-on regexes chosen with gene-blacklists.md; empty adds nothing
103
+ blacklist = "|".join([base, *extra_patterns])
104
+ new, old = set(ds.RNA.feats.grep(blacklist)), set(ds.RNA.feats.grep(DEFAULT_HVG_BLACKLIST))
105
+ print("added:", sorted(new - old)[:20], "released:", sorted(old - new)[:20]) # check both
106
+ hvg_2k = ds.select_hvgs(cells, top_n=2000, show_plot=False, blacklist=blacklist)
107
+ detected = ds.select_detected_features(cells, min_cells=20) # inclusive detection threshold
108
+ everything = ds.select_all_features(from_assay="RNA") # canonical all-true universe
109
+ custom = ds.set_feature_selection(from_assay="RNA", mask=np.isin(names, panel)) # or feature_indexes=
110
+ shared = np.asarray(ds.load_artifact(hvg_2k)["values"][:], dtype=bool) & selected
111
+ print(int(shared.sum()), "of the 1000 baseline HVGs kept")
112
+ ```
113
+
114
+ `blacklist=""` disables the name filter; `max_cells=np.inf` disables the ubiquitous-gene filter.
115
+ The blacklist affects HVG selection only; marker search still tests every gene.
116
+
117
+ ### Choose PCA dimensions
118
+
119
+ ```python
120
+ pca_30 = ds.run_pca(normalized, dims=30) # more PCs than needed; local, no count pass
121
+ scores = np.asarray(ds.load_artifact(pca_30)["data"]) # cells x dims; also "loadings", "center"
122
+ share = scores.var(axis=0, ddof=1)
123
+ share = share / share.sum()
124
+ print(np.round(np.cumsum(share)[[9, 14, 20, 29]], 3)) # share of the 30-PC variance at 10/15/21/30
125
+ splt.elbow(share, show=False).save("pca_elbow.png")
126
+ ```
127
+
128
+ An elbow is a hint, not a rule. Too few PCs merge populations; too many restore noise.
129
+
130
+ ### Graph diagnostics and a k branch
131
+
132
+ `load_graph` returns a SciPy CSR matrix over the graph's cells, in cell-selection order. The default
133
+ is directed: each row holds exactly `k` weighted edges. Keep every returned ref; downstream calls
134
+ take one graph explicitly, so branches never overwrite each other.
135
+
136
+ ```python
137
+ in_graph = np.asarray(ds.load_artifact(cells)["values"][:], dtype=bool)
138
+ G = ds.load_graph(graph)
139
+ S = ds.load_graph(graph, symmetric=True)
140
+ degree = np.asarray((S != 0).sum(axis=1)).ravel()
141
+ print(G.shape, G.nnz, "degree min/median/max", degree.min(), np.median(degree), degree.max())
142
+ qc = ds.cells.to_pandas_dataframe(["RNA_nCounts", "RNA_nFeatures"])[in_graph]
143
+ print(qc.assign(degree=degree).corr(method="spearman").round(2)["degree"])
144
+ splt.graph_qc(G, show=False).save("graph_qc.png")
145
+
146
+ graph_21 = ds.build_connectivity_map(ds.query_neighbors(ann, k=21)) # reuses the ANN index
147
+ leiden = {name: ds.run_leiden_clustering(g, resolution=1.0)
148
+ for name, g in {"k11": graph, "k21": graph_21}.items()}
149
+ labels = {name: np.asarray(ds.load_artifact(ref)["values"][:]) for name, ref in leiden.items()}
150
+ print(pd.crosstab(labels["k11"], labels["k21"]))
151
+ ```
152
+
153
+ (10x 5K PBMC docs dataset) Degree vs `RNA_nCounts` Spearman 0.10; 16 clusters at k=11 versus 15
154
+ at k=21 (ARI 0.81). A larger `k` usually absorbs small clusters; the crosstab shows which.
155
+
156
+ ### Same knobs through the pipeline
157
+
158
+ `params` forwards other stage settings, including the blacklist. Giving a setting both ways (for
159
+ example `hvg_count` and `params["hvg"]["top_n"]`) raises before anything is written. A completed
160
+ label cannot be run again, even unchanged: reopen it with `ds.pipeline.open` or use a new label.
161
+
162
+ ```python
163
+ run_alt = ds.pipeline.run(
164
+ label="hvg2k_pc30_k21_blacklist", hvg_count=2000, pca_dims=30, neighbors_k=21,
165
+ params={"hvg": {"blacklist": blacklist}, "connectivity": {"bandwidth": 1.5}},
166
+ )
167
+ print(run_alt.status, ds.inspect_artifact(run_alt["neighbors"]).parameters["k"])
168
+ print(run_alt["highly_variable_features"] == hvg_2k) # same cells and blacklist: same artifact
169
+ ```
170
+
171
+ ### Subcluster one lineage
172
+
173
+ Fix a coarse lineage partition first (`clustering-and-embedding.md`, "Choose the partition"), then
174
+ rebuild HVGs, normalization, PCA and the graph inside the lineage. `ds.cells.insert` writes to the
175
+ store itself; prefix and version column names on shared stores.
176
+
177
+ ```python
178
+ from itertools import combinations
179
+ from sklearn.metrics import adjusted_rand_score
180
+
181
+ def on_cell_axis(ref):
182
+ """Spread Leiden labels over every store cell (-1 outside the ref's selection)."""
183
+ mask = ds.load_artifact(ds.inspect_artifact(ref).input_ref("cell_selection"))["values"][:]
184
+ full = np.full(ds.cells.N, -1)
185
+ full[np.asarray(mask, dtype=bool)] = ds.load_artifact(ref)["values"][:]
186
+ return full
187
+
188
+ def build_graph(cell_selection, top_n=1000, dims=21, k=11):
189
+ hvg = ds.select_hvgs(cell_selection, top_n=top_n, show_plot=False) # summary: one count pass
190
+ pca = ds.run_pca(ds.run_normalization(cell_selection, hvg), dims=dims) # one count pass
191
+ return ds.build_connectivity_map(ds.query_neighbors(ds.build_ann_index(pca), k=k))
192
+
193
+ parent, parent_markers = run["clusters"], run["markers"] # or your chosen Leiden ref and its table
194
+ table = ds.get_markers(marker=parent_markers, min_score=-1, min_frac_exp=-1)
195
+ cd3e = table[table["feature_name"] == "CD3E"].set_index("group_id")["frac_exp"]
196
+ lineage_ids = sorted(int(g) for g in cd3e.index[cd3e > 0.5]) # a T gate; confirm with markers
197
+ sub_cells = ds.select_cells(parent, include=lineage_ids) # frozen, over every store cell
198
+ ds.cells.insert("t_lineage_v1", np.isin(on_cell_axis(parent), lineage_ids), overwrite=True)
199
+ alt_cells = ds.snapshot_cell_selection("t_lineage_v1") # same cells from any boolean column
200
+ assert np.array_equal(*(ds.load_artifact(r)["values"][:] for r in (sub_cells, alt_cells)))
201
+
202
+ sub_graph, sub_leiden, rows = build_graph(sub_cells), {}, []
203
+ for resolution in (0.3, 0.5, 0.8):
204
+ refs = [ds.run_leiden_clustering(sub_graph, resolution=resolution, random_seed=seed)
205
+ for seed in (4444, 1, 2)]
206
+ seeds = [np.asarray(ds.load_artifact(ref)["values"][:]) for ref in refs]
207
+ sub_leiden[resolution] = refs[0]
208
+ rows.append({"resolution": resolution, "n_clusters": len(np.unique(seeds[0])),
209
+ "min_seed_ari": min(adjusted_rand_score(a, b) for a, b in combinations(seeds, 2))})
210
+ print(pd.DataFrame(rows).round(3))
211
+
212
+ # Choose as for a global partition; then check markers, covariates, HVG count, parent overlap.
213
+ sub_resolution = 0.3 # the finest resolution that passed the seed check above
214
+ sub = sub_leiden[sub_resolution]
215
+ sub_full, parent_full = on_cell_axis(sub), on_cell_axis(parent)
216
+ in_sub = sub_full >= 0
217
+ sub_markers = ds.run_marker_search(sub, features=run["feature_universe"]) # one count pass
218
+ print(ds.get_markers(marker=sub_markers).groupby("group_id", sort=False)["feature_name"]
219
+ .agg(n="size", top=lambda s: ", ".join(s.head(6)))) # groups missing here have 0 markers
220
+ print(pd.crosstab(sub_full[in_sub], parent_full[in_sub]))
221
+ print(pd.DataFrame({"sub": sub_full, "counts": ds.cells.fetch_all("RNA_nCounts"), "doublet":
222
+ run.cells.fetch_all("doublet_score")})[in_sub].groupby("sub").median().round(2))
223
+ sub_2k = ds.run_leiden_clustering(build_graph(sub_cells, top_n=2000), resolution=sub_resolution)
224
+ print("ARI vs 2000 HVGs", round(ds.metric_label_concordance(sub, sub_2k), 3))
225
+
226
+ combined = np.where(in_sub, np.char.add("T.", sub_full.astype(str)), parent_full.astype(str))
227
+ combined = np.where(parent_full < 0, "not analyzed", combined).astype(object)
228
+ ds.cells.insert("clusters_tsub_v1", combined, overwrite=True) # new suffix for each new version
229
+ ```
230
+
231
+ (10x 5K PBMC docs dataset) The T subgraph at 0.3 split naive and memory CD4, naive CD8,
232
+ cytotoxic and FOXP3/CTLA4 groups, with ARI 0.94 at 2,000 HVGs. The parent had no FOXP3 group.
233
+
234
+ ## Parameters that matter
235
+
236
+ | Parameter | Default | Change when |
237
+ | --- | --- | --- |
238
+ | `select_hvgs(top_n=)` / `hvg_count` | `1000` | Weak or rare populations (raise); noisy programs dominate (lower). |
239
+ | `select_hvgs(blacklist=)` | `DEFAULT_HVG_BLACKLIST` | A family dominates the HVGs or is the question; what to add: `gene-blacklists.md`. |
240
+ | `select_hvgs(min_cells=, max_cells=)` | `20`, `None` (= selected cells - 20) | Tiny subsets; `max_cells=np.inf` keeps ubiquitous genes. |
241
+ | `run_pca(dims=)` / `pca_dims` | `21` | After a variance and stability check; `0` in the pipeline means no PCA. |
242
+ | `run_pca(pca_cell_selection=)` | `None` | Fit on a subset (for example one batch) and project all cells. |
243
+ | `query_neighbors(k=)` / `neighbors_k` | `11` (clamped to cells - 1) | Larger data or smoother graphs (15 to 30); smaller to resolve rare groups. |
244
+ | `load_graph(symmetric=, use_k=)` | `False`, all `k` | `True` for degree and connectivity checks; `use_k` to thin edges. |
245
+
246
+ ## Check before moving on
247
+
248
+ - HVGs: count equals `top_n`; expected lineage markers are selected; the top of the list is not
249
+ one contaminant or technical program. If canonical lineage genes are missing, or one family
250
+ (Ig, haemoglobin, platelet) leads the list, branch with more HVGs and an explicit blacklist
251
+ (`gene-blacklists.md`) and compare partitions.
252
+ - PCA and `k`: a second setting gives a similar partition, or the differences are explained by
253
+ markers. Boundaries that move with `k` or `dims` are provisional.
254
+ - Graph: symmetric degree min is at least `k`; degree barely tracks counts. A strong correlation
255
+ means depth is shaping the graph.
256
+ - Subclusters: keep one only when it is seed-stable, has its own named markers, is not explained
257
+ by counts or doublet score, and survives the HVG change. Merge the rest into the parent label.
258
+ Write labels to a versioned column and keep the parent labels.
259
+
260
+ ## Pitfalls
261
+
262
+ - Reading in-degree from the directed graph as isolation. `(G != 0).sum(axis=0)` is zero for 85
263
+ cells (10x 5K PBMC docs dataset), yet every cell has `k` out-edges; use `symmetric=True`.
264
+ - Passing a feature selection to UMAP, clustering or markers on a graph: graph consumers take the
265
+ graph ref. `query_neighbors(coordinates=)` only checks equality; it cannot retarget an index.
266
+ - `show_elbow_plot=True` draws only on a new PCA fit. More than 4096 features or several narrow
267
+ blocks switch PCA to IncrementalPCA, and `batch_size` then joins the artifact identity.
268
+ - Subclustering on the parent graph, or carrying subcluster IDs across artifacts. Rebuild inside
269
+ the subset, join on the cell axis, and keep the parent labels. `select_cells(include=[])`
270
+ raises; integer cluster artifacts need integer `include` values.
271
+ - `select_hvgs` is RNA only. ATAC uses `select_prevalent_peaks` then `run_lsi`.
272
+
273
+ ## See also
274
+
275
+ - `gene-blacklists.md`: which gene families to pass as `blacklist=`, and why.
276
+ - `quality-control.md`: building the cell selection this chain starts from.
277
+ - `clustering-and-embedding.md`: Leiden, Paris, UMAP and t-SNE on the graph; choosing a partition.
278
+ - `integration-and-comparisons.md`: `run_harmony` between PCA and the ANN index.
279
+ - `pipeline-runs-and-artifacts.md`: `inspect_artifact`, lineage and reuse.
@@ -0,0 +1,182 @@
1
+ # Gene blacklists for HVG selection
2
+
3
+ What `select_hvgs(blacklist=...)` removes, how to check it on your store, the corrected strings to
4
+ pass today, and when to add the clonotype or haemoglobin add-ons. Docs:
5
+ <https://scarf.readthedocs.io/en/latest/tutorials/feature_selection.html>, <https://scarf.readthedocs.io/en/latest/reference/api/pipeline.html>.
6
+
7
+ ## When to use
8
+
9
+ - Before the baseline run on any new store, to see what the default blacklist matches there.
10
+ - The store uses current HGNC or MGI histone symbols (`H2AC*`, `H3C*`, `H4C*`, `H1-*`), as Cytebase
11
+ datasets do. The default `^HIST` then matches nothing; only older `HIST1H*` references match.
12
+ - Fibroblast, smooth-muscle, endothelial or tumour data, where `CCN1`/`CCN2` (CYR61/CTGF) or
13
+ `CCND1`/`CCND2` carry biology that the default `^CCN` hides.
14
+ - Plasma-cell-rich or B-cell-rich samples, mouse data, or haemoglobin genes among the top HVGs.
15
+
16
+ ## Key concepts
17
+
18
+ - The blacklist is one regular expression, matched case-insensitively with `re.match` against
19
+ feature `names` (never `ids`). Each `|` alternative is anchored at the start of the name.
20
+ - It only removes HVG candidates. Markers, plots, gene scores and QC still see every gene.
21
+ - The string and the matched-gene fingerprint are part of the HVG artifact identity. Changing the
22
+ string creates new HVG, normalization, PCA and graph artifacts; the same string reuses them.
23
+ - HVG selection also drops ubiquitous genes (`max_cells` = selected cells - 20), so most `MT-` and
24
+ cytosolic ribosomal genes never compete. On the 10x 5K PBMC docs dataset, unblocking either
25
+ family added no gene to the top 1000.
26
+ - `RNA_percentMito` and `RNA_percentRibo` were fixed at first open from the `^MT-` and
27
+ `^RPS|^RPL|^MRPS|^MRPL` patterns; `blacklist=` never changes them. The RPS6K kinases and other
28
+ off-targets added under 1% of `percentRibo` counts in testing, so the column needs no correction.
29
+ - Names that are Ensembl IDs match nothing: no blacklist effect, no error, and no
30
+ `percentMito`/`percentRibo` columns (only a warning at first open). Build the string from the
31
+ family genes' IDs instead (`^ENSG00000198888$|...`), and percentages with `run_feature_percentage`.
32
+ - Default families and verdicts:
33
+
34
+ | Family | Default | Problem | Recommended |
35
+ | --- | --- | --- | --- |
36
+ | mitochondrial | `^MT-` | none; mouse `mt-` matches | keep |
37
+ | ribosomal | `^RPS\|^RPL\|^MRPS\|^MRPL` | also RPS6KA/B kinases, `RPS19BP1`, `L` paralogs, -AS1/-DT RNAs | exact protein symbols |
38
+ | cellCycleCcn | `^CCN` | hits CCN1-6 (CTGF, CYR61, NOV, WISP), CCND1-3, CCNT/K/H/L; misses MKI67, TOP2A | drop; score the cell cycle instead |
39
+ | hla / h2 | `^HLA-` / `^H2-` | whole MHC I and II; removal did not create donor clusters in testing | keep |
40
+ | histone | `^HIST` | matches only pre-2020 symbols | old and current replication-dependent names |
41
+ | sexLinked | 10 human names | mouse `Eif2s3y`, `Zfy1/2` missed | add mouse genes |
42
+
43
+ ## Recipes
44
+
45
+ ### Recommended strings
46
+
47
+ ```python
48
+ import re
49
+
50
+ import numpy as np
51
+ import pandas as pd
52
+ import scarf
53
+ from scarf.features.gene_families import GENE_FAMILY_PATTERNS
54
+ from scarf.features.variability import DEFAULT_HVG_BLACKLIST
55
+
56
+ MITO = r"^MT-"
57
+ RIBO = r"^RP[SL]\d+[AXY]?\d*$|^RPLP[0-2]$|^RPSA$|^MRP[SL]\d+[A-C]?$"
58
+ HISTONE = (r"^HIST[1-4]H|^H1-[1-6]$|^H1F[1-6]$|^H2AC\d+$|^H2BC\d+$|^H3C\d+$|^H4C\d+$"
59
+ r"|^H2AW$|^H3-4$|^H4-16$") # replication-dependent; replacement histones kept
60
+ HUMAN_BLACKLIST = "|".join([MITO, RIBO, r"^HLA-", HISTONE,
61
+ r"^XIST$|^DDX3Y$|^USP9Y$|^EIF1AY$|^KDM5D$|^SRY$|^ZFY$|^UTY$|^TMSB4Y$|^NLGN4Y$"])
62
+ MOUSE_BLACKLIST = "|".join([MITO, RIBO, r"^H2-", HISTONE,
63
+ r"^XIST$|^TSIX$|^DDX3Y$|^EIF2S3Y$|^KDM5D$|^UTY$|^ZFY[12]$|^SRY$"])
64
+ CLONOTYPE_ADDON = r"^IG[HKL]V|^IG[HKL]J\d|^IGHD\d" # V, J, D segments; not IGHD or IGKC/IGLC
65
+ HAEMOGLOBIN_ADDON = r"^HB[ABDEGMQZ]\d*$|^HB[AB]-" # spares HBEGF, HBP1, HBS1L
66
+ ```
67
+
68
+ Add-ons are appended: `HUMAN_BLACKLIST + "|" + CLONOTYPE_ADDON`. Patterns are case-insensitive,
69
+ so the human string also works on title-case names; prefer the species string anyway.
70
+
71
+ ### Inspect what the default removes on this store
72
+
73
+ ```python
74
+ scarf.configure_output(level="WARNING", progress=False)
75
+ ds = scarf.DataStore("analysis.zarr", min_features_per_cell=-1) # reopen; see data-access.md
76
+ names = ds.RNA.feats.fetch_all("names").astype(str)
77
+ print("Ensembl-like names:", int(pd.Series(names).str.match(r"ENS[A-Z]*G\d").sum()), "of", len(names))
78
+ for family, pattern in GENE_FAMILY_PATTERNS.items():
79
+ hits = ds.RNA.feats.grep(pattern) # upper-cased names
80
+ print(f"{family:13s} {len(hits):4d}", hits[:6])
81
+ print("current histone symbols the default misses:",
82
+ len(ds.RNA.feats.grep(r"^H2AC\d|^H2BC\d|^H3C\d|^H4C\d|^H1-[1-6]$")))
83
+ print("default-only matches:",
84
+ sorted(set(ds.RNA.feats.grep(DEFAULT_HVG_BLACKLIST)) - set(ds.RNA.feats.grep(HUMAN_BLACKLIST))))
85
+
86
+ run = ds.pipeline.open(label="baseline")
87
+ cells = run["analysis_cell_selection"]
88
+
89
+
90
+ def selected(ref):
91
+ return set(names[np.asarray(ds.load_artifact(ref)["values"][:], dtype=bool)])
92
+
93
+
94
+ hvg_default = run["highly_variable_features"] # same as select_hvgs(cells, top_n=1000)
95
+ hvg_open = ds.select_hvgs(cells, top_n=1000, blacklist="", show_plot=False) # no name filter
96
+ print("removed by the default:", sorted(selected(hvg_open) - selected(hvg_default)))
97
+ ```
98
+
99
+ The last line is what the default actually costs. 10x 5K PBMC docs dataset: 17 genes, namely 9
100
+ MHC class II, 6 histones, `CCND3` and `CCNL1`.
101
+
102
+ ### Apply a string and check the HVGs
103
+
104
+ ```python
105
+ hvg_fixed = ds.select_hvgs(cells, top_n=1000, blacklist=HUMAN_BLACKLIST, show_plot=False)
106
+ print("entered:", sorted(selected(hvg_fixed) - selected(hvg_default)))
107
+ print("left:", sorted(selected(hvg_default) - selected(hvg_fixed)))
108
+ blocked = re.compile(HUMAN_BLACKLIST, re.IGNORECASE)
109
+ assert not any(blocked.match(gene) for gene in selected(hvg_fixed))
110
+
111
+ run_fixed = ds.pipeline.run(label="baseline_blacklist_fixed",
112
+ params={"hvg": {"blacklist": HUMAN_BLACKLIST}})
113
+ print(run_fixed["highly_variable_features"] == hvg_fixed,
114
+ ds.inspect_artifact(run_fixed["highly_variable_features"]).parameters["blacklist"]
115
+ == HUMAN_BLACKLIST)
116
+ ```
117
+
118
+ The pipeline returns the very ref of the explicit call when its cells match. Compare the two
119
+ runs' partitions with `clustering-and-embedding.md`. 10x 5K PBMC docs dataset: `CCND3` and `CCNL1`
120
+ entered, and the Leiden 1.0 partitions agreed (ARI 0.82) about as well as two Leiden seeds of the
121
+ baseline (0.85).
122
+
123
+ ### Decide on the add-ons with marker evidence
124
+
125
+ ```python
126
+ top = ds.get_markers(marker=run_fixed["markers"]).groupby("group_id", sort=False).head(10)
127
+ clonal = top[top["feature_name"].str.match(CLONOTYPE_ADDON, case=False)]
128
+ print("V/J genes among HVGs:", sum(bool(re.match(CLONOTYPE_ADDON, g, re.I)) for g in selected(hvg_fixed)))
129
+ print(clonal.groupby("group_id")["feature_name"].agg(", ".join)) # clusters led by V genes
130
+ panel = ["JCHAIN", "MZB1", "IGKC", "IGLC2", "IGHA1", "IGHG1", "IGHM", "MKI67",
131
+ "HBB", "HBA1", "ALAS2", "CA1", "AHSP", "SLC4A1"]
132
+ markers = ds.get_markers(marker=run_fixed["markers"], min_score=-1, min_frac_exp=-1)
133
+ frac = markers[markers["feature_name"].isin(panel)].pivot_table(
134
+ index="group_id", columns="feature_name", values="frac_exp")
135
+ print(frac.round(2).to_string())
136
+ ```
137
+
138
+ - Clonotype add-on: use it when two or more clusters share plasma or B markers (`JCHAIN`, `MZB1`,
139
+ `CD79A`) and differ mainly by `IGKC` versus `IGLC*` or by V genes in their top markers. Rerun
140
+ with `HUMAN_BLACKLIST + "|" + CLONOTYPE_ADDON` under a new label and confirm that the plasma
141
+ clusters now differ by isotype (`IGHA1`, `IGHG1`, `IGHM`) or state (`MKI67`), not light chain.
142
+ Skip it when expanded clones are the question; use VDJ data then.
143
+ - Haemoglobin add-on: use it when `HBB`/`HBA1` are among the HVGs and are detected at low
144
+ fractions across clusters that lack `ALAS2`, `CA1`, `AHSP` and `SLC4A1` (ambient RNA). Do not
145
+ use it when a cluster expresses those erythroid markers; remove contaminating red cells at QC.
146
+
147
+ ## Parameters that matter
148
+
149
+ | Parameter | Default | Change when |
150
+ | --- | --- | --- |
151
+ | `select_hvgs(blacklist=)` | `DEFAULT_HVG_BLACKLIST` | Always pass a species string on current-symbol stores. |
152
+ | `params={"hvg": {"blacklist": ...}}` | default regex | Same string through `ds.pipeline.run`; new label per string. |
153
+ | `blacklist=""` | | Diagnostics only: shows what the name filter removes. |
154
+ | `select_hvgs(max_cells=)` | selected cells - 20 | `np.inf` lets ubiquitous genes compete; the blacklist then matters more. |
155
+
156
+ ## Check before moving on
157
+
158
+ - The store has symbol names; every family you rely on matches the genes you expect.
159
+ - `inspect_artifact(hvg).parameters["blacklist"]` equals the string you recorded in the log.
160
+ - No HVG matches the string; the entered and left genes are listed in the analysis log.
161
+ - With the clonotype add-on, no cluster's top markers are V genes and plasma clusters are not
162
+ split by `IGKC` versus `IGLC*`.
163
+
164
+ ## Pitfalls
165
+
166
+ - Trusting the default on current symbols: replication-dependent histones become HVGs unnoticed.
167
+ - Writing `^IGHD` for D segments: it also removes the IgD constant gene. Use `^IGHD\d`.
168
+ - `^TR[ABDG]D` also matches `TRADD`; TCR V genes such as `TRDV2`, `TRGV9` and `TRAV1-2` mark
169
+ gamma-delta and MAIT cells. T-cell clusters did not follow TRBV usage in testing, so no TCR
170
+ add-on is recommended; consider `^TR[AB]V` only when a cluster is led by one TRAV/TRBV pair.
171
+ - `^HB` also removes `HBEGF`. Small clusters (under about 50 cells) move with any HVG change;
172
+ compare across Leiden seeds before attributing a change to the blacklist.
173
+ - Run `groupby(...).head` on default-filtered marker tables; on a full table (`min_score=-1`) it
174
+ crashed pandas 3.0 with a segmentation fault in testing.
175
+ - A blacklist never fixes QC: high `percentMito` cells or red-cell contamination need filtering.
176
+
177
+ ## See also
178
+
179
+ - `features-and-graphs.md`: HVG diagnostics and branching the graph.
180
+ - `quality-control.md`: `percentMito`, `percentRibo` and `run_feature_percentage`.
181
+ - `markers-and-annotation.md`: marker tables and panels used for the add-on decisions.
182
+ - `pipeline-runs-and-artifacts.md`: `params`, labels and artifact identity.