Skill Tracer
Skills › Scientific Agent Skills › matchms › similarity.md

similarity.md

448 lines · skills/matchms/references/similarity.md at 5f2bc03

Open on GitHub
1# Similarity and Scores (matchms 0.33.1)
2
3Use this reference to select a similarity class, interpret its output, extract
4top hits, or scale a comparison. The authoritative API is
5[`matchms.similarity`](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html).
6

Similarity and Scores (matchms 0.33.1)

Use this reference to select a similarity class, interpret its output, extract top hits, or scale a comparison. The authoritative API is [matchms.similarity](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html).

Raw lines 1 to 6
7## Core Calculation
8
9```python
10from matchms import calculate_scores
11from matchms.similarity import CosineGreedy
13metric = CosineGreedy(tolerance=0.02)
14scores = calculate_scores(
15 references=reference_spectra,
16 queries=query_spectra,
17 similarity_function=metric,
18 array_type="numpy",
19 is_symmetric=False,
21```
23Set `is_symmetric=True` only when references and queries are the same collection
24in the same order and the metric is symmetric.

Core Calculation

from matchms import calculate_scores
from matchms.similarity import CosineGreedy

metric = CosineGreedy(tolerance=0.02)
scores = calculate_scores(
    references=reference_spectra,
    queries=query_spectra,
    similarity_function=metric,
    array_type="numpy",
    is_symmetric=False,
)

Set is_symmetric=True only when references and queries are the same collection in the same order and the metric is symmetric.

Raw lines 7 to 25
26## Understand the Output Before Indexing
28`Scores.scores` is a `sparsestack.StackedSparseArray`, not a plain two-dimensional
29NumPy array. Its shape is `(n_references, n_queries, n_score_fields)`.
31Cosine-family methods produce two fields:
33```python
34print(scores.score_names)
35# ('CosineGreedy_score', 'CosineGreedy_matches')
37similarities = scores.to_array("CosineGreedy_score")
38matched_peaks = scores.to_array("CosineGreedy_matches")
39```
41Scalar methods such as `FlashSimilarity`, `PrecursorMzMatch`, and
42`FingerprintSimilarity` produce one field named after the class.

Understand the Output Before Indexing

Scores.scores is a sparsestack.StackedSparseArray, not a plain two-dimensional NumPy array. Its shape is (n_references, n_queries, n_score_fields).

Cosine-family methods produce two fields:

print(scores.score_names)
# ('CosineGreedy_score', 'CosineGreedy_matches')

similarities = scores.to_array("CosineGreedy_score")
matched_peaks = scores.to_array("CosineGreedy_matches")

Scalar methods such as FlashSimilarity, PrecursorMzMatch, and FingerprintSimilarity produce one field named after the class.

Raw lines 26 to 43
44### Top hits for one query
46```python
47score_name = "CosineGreedy_score"
48matches_name = "CosineGreedy_matches"
50ranked = scores.scores_by_query(query_spectrum, name=score_name, sort=True)
51for reference, value in ranked[:10]:
52 print(
53 reference.get("spectrum_id"),
54 float(value[score_name]),
55 int(value[matches_name]),
56 )
57```
59The first tuple item is the actual reference `Spectrum`, not an integer index.
60The second item can be a structured NumPy record containing every score field.

Top hits for one query

score_name = "CosineGreedy_score"
matches_name = "CosineGreedy_matches"

ranked = scores.scores_by_query(query_spectrum, name=score_name, sort=True)
for reference, value in ranked[:10]:
    print(
        reference.get("spectrum_id"),
        float(value[score_name]),
        int(value[matches_name]),
    )

The first tuple item is the actual reference Spectrum, not an integer index. The second item can be a structured NumPy record containing every score field.

Raw lines 44 to 61
62### Iterate stored pairs
64```python
65for reference, query, values in scores:
66 print(reference.get("id"), query.get("id"), values)
67```
69Iteration covers stored coordinates. After sparse filtering, that may be only a
70subset of the Cartesian product.

Iterate stored pairs

for reference, query, values in scores:
    print(reference.get("id"), query.get("id"), values)

Iteration covers stored coordinates. After sparse filtering, that may be only a subset of the Cartesian product.

Raw lines 62 to 71
72## Peak-Based Cosine Methods
74All cosine classes below use:
76```text
77tolerance=0.1, mz_power=0.0, intensity_power=1.0
78```
80unless noted otherwise. Tolerance is in daltons for these classes.

Peak-Based Cosine Methods

All cosine classes below use:

tolerance=0.1, mz_power=0.0, intensity_power=1.0

unless noted otherwise. Tolerance is in daltons for these classes.

Raw lines 72 to 81
82### `CosineGreedy`
84Greedily assigns candidate peak pairs within tolerance.
86Use for:
88- routine spectral-library comparisons;
89- a transparent baseline;
90- moderate collections where exact assignment is not essential.
92Output fields: `CosineGreedy_score`, `CosineGreedy_matches`.

CosineGreedy

Greedily assigns candidate peak pairs within tolerance.

Use for:

  • routine spectral-library comparisons;
  • a transparent baseline;
  • moderate collections where exact assignment is not essential.

Output fields: CosineGreedy_score, CosineGreedy_matches.

Raw lines 82 to 93
94### `CosineHungarian`
96Uses optimal assignment rather than greedy assignment.
98Use for:
100- benchmarking peak-assignment effects;
101- smaller datasets;
102- cases where greedy ambiguity materially affects results.
104It is computationally more expensive than `CosineGreedy`.

CosineHungarian

Uses optimal assignment rather than greedy assignment.

Use for:

  • benchmarking peak-assignment effects;
  • smaller datasets;
  • cases where greedy ambiguity materially affects results.

It is computationally more expensive than CosineGreedy.

Raw lines 94 to 105
106### `CosineLinear`
108Added in 0.33.0. It first greedily merges nearby peaks using a `2 * tolerance`
109separation criterion, then performs linear-time matching on the merged spectra.
110The score and matched-peak count therefore need not equal `CosineGreedy` on the
111original peaks, even with identical tolerance and intensity powers.
113Use when:
115- cosine is the intended metric;
116- matrix size makes assignment cost important; and
117- you have benchmarked agreement and runtime on representative spectra.
119Output fields: `CosineLinear_score`, `CosineLinear_matches`.

CosineLinear

Added in 0.33.0. It first greedily merges nearby peaks using a 2 * tolerance separation criterion, then performs linear-time matching on the merged spectra. The score and matched-peak count therefore need not equal CosineGreedy on the original peaks, even with identical tolerance and intensity powers.

Use when:

  • cosine is the intended metric;
  • matrix size makes assignment cost important; and
  • you have benchmarked agreement and runtime on representative spectra.

Output fields: CosineLinear_score, CosineLinear_matches.

Raw lines 106 to 120
121## Modified Cosine
123Modified cosine allows unshifted peak matches and matches shifted by the
124difference in precursor m/z. Both spectra need valid `precursor_mz`.

Modified Cosine

Modified cosine allows unshifted peak matches and matches shifted by the difference in precursor m/z. Both spectra need valid precursor_mz.

Raw lines 121 to 125
126### `ModifiedCosineGreedy`
128```python
129from matchms.similarity import ModifiedCosineGreedy
131metric = ModifiedCosineGreedy(tolerance=0.02)
132```
134This is the current name for the implementation formerly called
135`ModifiedCosine`. It uses greedy assignment.

ModifiedCosineGreedy

from matchms.similarity import ModifiedCosineGreedy

metric = ModifiedCosineGreedy(tolerance=0.02)

This is the current name for the implementation formerly called ModifiedCosine. It uses greedy assignment.

Raw lines 126 to 136
137### `ModifiedCosineHungarian`
139```python
140from matchms.similarity import ModifiedCosineHungarian
142metric = ModifiedCosineHungarian(tolerance=0.02)
143```
145Use for exact modified-cosine assignment in benchmarks or method development.
146It is slower than the greedy variant.
148Do not infer that a shifted match proves a specific chemical transformation.
149Inspect precursor delta, adduct/charge compatibility, shifted peaks, and
150orthogonal annotations.

ModifiedCosineHungarian

from matchms.similarity import ModifiedCosineHungarian

metric = ModifiedCosineHungarian(tolerance=0.02)

Use for exact modified-cosine assignment in benchmarks or method development. It is slower than the greedy variant.

Do not infer that a shifted match proves a specific chemical transformation. Inspect precursor delta, adduct/charge compatibility, shifted peaks, and orthogonal annotations.

Raw lines 137 to 151
152## Neutral-Loss Cosine
154```python
155from matchms.similarity import NeutralLossesCosine
157metric = NeutralLossesCosine(
158 tolerance=0.02,
159 ignore_peaks_above_precursor=True,
161```
163`NeutralLossesCosine` computes losses from `precursor_mz - fragment_mz`.
164Both spectra require precursor m/z. Do not call the removed `add_losses()`
165filter; losses are computed on demand in current matchms.
167Output fields: `NeutralLossesCosine_score`,
168`NeutralLossesCosine_matches`.

Neutral-Loss Cosine

from matchms.similarity import NeutralLossesCosine

metric = NeutralLossesCosine(
    tolerance=0.02,
    ignore_peaks_above_precursor=True,
)

NeutralLossesCosine computes losses from precursor_mz - fragment_mz. Both spectra require precursor m/z. Do not call the removed add_losses() filter; losses are computed on demand in current matchms.

Output fields: NeutralLossesCosine_score, NeutralLossesCosine_matches.

Raw lines 152 to 169
170## Fast Matrix-Oriented Methods

Fast Matrix-Oriented Methods

Raw lines 170 to 171
172### `BlinkCosine`
174BLINK-style approximate cosine:
176```python
177from matchms.similarity import BlinkCosine
179metric = BlinkCosine(
180 tolerance=0.01,
181 bin_width=0.001,
182 min_relative_intensity=0.01,
183 top_k=None,
184 batch_size=1024,
185 sparse_score_min=0.0,
187```
189Important parameters include peak preprocessing, precursor cropping, batch
190size, and sparse score minimum. By default `crop_above_precursor=True` crops
191where a precursor is available; record precursor coverage or disable this
192option when that extra step is inappropriate. Output contains a float32 score
193and matched-peak count. Validate approximation behavior against `CosineGreedy` on a subset
194before changing production workflows.

BlinkCosine

BLINK-style approximate cosine:

from matchms.similarity import BlinkCosine

metric = BlinkCosine(
    tolerance=0.01,
    bin_width=0.001,
    min_relative_intensity=0.01,
    top_k=None,
    batch_size=1024,
    sparse_score_min=0.0,
)

Important parameters include peak preprocessing, precursor cropping, batch size, and sparse score minimum. By default crop_above_precursor=True crops where a precursor is available; record precursor coverage or disable this option when that extra step is inappropriate. Output contains a float32 score and matched-peak count. Validate approximation behavior against CosineGreedy on a subset before changing production workflows.

Raw lines 172 to 195
196### `FlashSimilarity`
198Fast matrix scoring based on the Flash Entropy approach:
200```python
201from matchms.similarity import FlashSimilarity
203entropy = FlashSimilarity(
204 score_type="spectral_entropy",
205 matching_mode="fragment",
206 tolerance=0.02,
209fast_modified_cosine = FlashSimilarity(
210 score_type="cosine",
211 matching_mode="hybrid",
212 tolerance=0.02,
214```
216Current choices and behavior:
218- `score_type`: `spectral_entropy` or `cosine`
219- `matching_mode`: `fragment`, `neutral_loss`, or `hybrid`
220- `use_ppm=True` switches fragment tolerance to symmetric ppm; default is Da
221- `remove_precursor=False`, `noise_cutoff=0.01`, `merge_within=0`, and
222 `normalize_to_half=True` are defaults; entropy weighting is internal
223- neutral-loss/hybrid matching and an identity precursor gate need valid
224 precursor metadata
225- `identity_precursor_tolerance` and `identity_use_ppm` control a separate
226 precursor gate
228`pair()` exists but emits a warning because it is not the optimized use. Call
229`calculate_scores()` for multi-spectrum matrices. A 1-by-1 `Scores.calculate`
230call still uses `pair()`. Flash output is a scalar field named `FlashSimilarity`,
231not a score/matches pair. Hybrid matching gives unshifted fragment matches
232priority; do not assume equality with greedy or Hungarian modified cosine.
234In 0.33.1, `matrix()` allocates a dense output even for `array_type="sparse"`,
235and `is_symmetric=True` does not reduce Flash runtime. Its default worker count
236uses available CPUs on Unix; call `metric.matrix(..., n_jobs=1)` for an explicit
237serial calculation (this returns a raw array, not `Scores`). Do not assume a
238sparse output is a bounded-memory or bounded-CPU search.
240**Verified 0.33.1 defect:** `score_type="spectral_entropy"` with
241`matching_mode="neutral_loss"` adds fragment and loss contributions, producing
242a self-score near 2 on the tested three-peak spectrum. Do not use that
243combination as a normalized entropy score; choose fragment entropy or
244`NeutralLossesCosine` as appropriate. Do not silently clip or rescale it.

FlashSimilarity

Fast matrix scoring based on the Flash Entropy approach:

from matchms.similarity import FlashSimilarity

entropy = FlashSimilarity(
    score_type="spectral_entropy",
    matching_mode="fragment",
    tolerance=0.02,
)

fast_modified_cosine = FlashSimilarity(
    score_type="cosine",
    matching_mode="hybrid",
    tolerance=0.02,
)

Current choices and behavior:

  • score_type: spectral_entropy or cosine
  • matching_mode: fragment, neutral_loss, or hybrid
  • use_ppm=True switches fragment tolerance to symmetric ppm; default is Da
  • remove_precursor=False, noise_cutoff=0.01, merge_within=0, and normalize_to_half=True are defaults; entropy weighting is internal
  • neutral-loss/hybrid matching and an identity precursor gate need valid precursor metadata
  • identity_precursor_tolerance and identity_use_ppm control a separate precursor gate

pair() exists but emits a warning because it is not the optimized use. Call calculate_scores() for multi-spectrum matrices. A 1-by-1 Scores.calculate call still uses pair(). Flash output is a scalar field named FlashSimilarity, not a score/matches pair. Hybrid matching gives unshifted fragment matches priority; do not assume equality with greedy or Hungarian modified cosine.

In 0.33.1, matrix() allocates a dense output even for array_type="sparse", and is_symmetric=True does not reduce Flash runtime. Its default worker count uses available CPUs on Unix; call metric.matrix(..., n_jobs=1) for an explicit serial calculation (this returns a raw array, not Scores). Do not assume a sparse output is a bounded-memory or bounded-CPU search.

Verified 0.33.1 defect: score_type="spectral_entropy" with matching_mode="neutral_loss" adds fragment and loss contributions, producing a self-score near 2 on the tested three-peak spectrum. Do not use that combination as a normalized entropy score; choose fragment entropy or NeutralLossesCosine as appropriate. Do not silently clip or rescale it.

Raw lines 196 to 245
246### `BinnedEmbeddingSimilarity`
248```python
249from matchms.similarity import BinnedEmbeddingSimilarity
251metric = BinnedEmbeddingSimilarity(
252 similarity="cosine",
253 max_mz=1005,
254 bin_width=1.0,
255 intensity_power=1.0,
257```
259This creates fixed-width binned spectral embeddings. Bin width controls both
260resolution and dimensionality. Reject empty/all-zero embeddings, including
261spectra with no retained peak below `max_mz`, before scoring. The class also supports approximate-neighbor
262indexing through the current PyNNDescent backend; use it only after checking
263recall against exact neighbors. In 0.33.1 the matrix method accepts only dense
264output and `is_symmetric=True`; use it for all-vs-all input. A normal rectangular
265`calculate_scores` call with its default `is_symmetric=False` raises an error.
266For rectangular embedding comparisons, compute the embeddings explicitly and
267compare those with the intended vector metric.

BinnedEmbeddingSimilarity

from matchms.similarity import BinnedEmbeddingSimilarity

metric = BinnedEmbeddingSimilarity(
    similarity="cosine",
    max_mz=1005,
    bin_width=1.0,
    intensity_power=1.0,
)

This creates fixed-width binned spectral embeddings. Bin width controls both resolution and dimensionality. Reject empty/all-zero embeddings, including spectra with no retained peak below max_mz, before scoring. The class also supports approximate-neighbor indexing through the current PyNNDescent backend; use it only after checking recall against exact neighbors. In 0.33.1 the matrix method accepts only dense output and is_symmetric=True; use it for all-vs-all input. A normal rectangular calculate_scores call with its default is_symmetric=False raises an error. For rectangular embedding comparisons, compute the embeddings explicitly and compare those with the intended vector metric.

Raw lines 246 to 268
269## Candidate and Metadata Matches
271These methods are useful as gates or additional evidence. They are not
272substitutes for peak-pattern similarity.

Candidate and Metadata Matches

These methods are useful as gates or additional evidence. They are not substitutes for peak-pattern similarity.

Raw lines 269 to 273
274### `PrecursorMzMatch`
276```python
277from matchms.similarity import PrecursorMzMatch
279absolute = PrecursorMzMatch(tolerance=0.02, tolerance_type="Dalton")
280relative = PrecursorMzMatch(tolerance=10, tolerance_type="ppm")
281```
283Output field: `PrecursorMzMatch` (boolean).

PrecursorMzMatch

from matchms.similarity import PrecursorMzMatch

absolute = PrecursorMzMatch(tolerance=0.02, tolerance_type="Dalton")
relative = PrecursorMzMatch(tolerance=10, tolerance_type="ppm")

Output field: PrecursorMzMatch (boolean).

Raw lines 274 to 284
285### `ParentMassMatch`
287```python
288from matchms.similarity import ParentMassMatch
290metric = ParentMassMatch(tolerance=0.02)
291```
293Requires `parent_mass`. Output field: `ParentMassMatch` (boolean). The current
294constructor does not expose a ppm mode.

ParentMassMatch

from matchms.similarity import ParentMassMatch

metric = ParentMassMatch(tolerance=0.02)

Requires parent_mass. Output field: ParentMassMatch (boolean). The current constructor does not expose a ppm mode.

Raw lines 285 to 295
296### `MetadataMatch`
298```python
299from matchms.similarity import MetadataMatch
301same_mode = MetadataMatch(field="ionmode", matching_type="equal_match")
302near_rt = MetadataMatch(
303 field="retention_time",
304 matching_type="difference",
305 tolerance=0.2,
307```
309Current matching types are `equal_match` and `difference`. Older examples that
310use `matching_type="exact"` are invalid.

MetadataMatch

from matchms.similarity import MetadataMatch

same_mode = MetadataMatch(field="ionmode", matching_type="equal_match")
near_rt = MetadataMatch(
    field="retention_time",
    matching_type="difference",
    tolerance=0.2,
)

Current matching types are equal_match and difference. Older examples that use matching_type="exact" are invalid.

Raw lines 296 to 311
312### `IntersectMz`
314`IntersectMz(scaling=1.0)` is a simple m/z-intersection score useful in tests or
315specialized workflows. It is not a drop-in replacement for tolerance-aware,
316intensity-weighted spectral scoring.

IntersectMz

IntersectMz(scaling=1.0) is a simple m/z-intersection score useful in tests or specialized workflows. It is not a drop-in replacement for tolerance-aware, intensity-weighted spectral scoring.

Raw lines 312 to 317
318## Molecular Fingerprint Similarity
320`FingerprintSimilarity` compares molecular fingerprints derived from known
321structures. It therefore measures structure similarity, not spectral
322similarity.
324```python
325from matchms import Fingerprints, calculate_scores
326from matchms.similarity import FingerprintSimilarity
328fp_store = Fingerprints(
329 fingerprint_algorithm="morgan2",
330 fingerprint_method="bit",
331 nbits=2048,
333fp_store.compute_fingerprints(spectra)
335usable = []
336for spectrum in spectra:
337 fingerprint = fp_store.get_fingerprint_by_spectrum(spectrum)
338 if fingerprint is not None:
339 spectrum.set("fingerprint", fingerprint)
340 usable.append(spectrum)
342scores = calculate_scores(
343 usable,
344 usable,
345 FingerprintSimilarity(similarity_measure="jaccard"),
346 is_symmetric=True,
348```
350Similarity measures are `jaccard`, `dice`, and `cosine`.
352The old `add_fingerprint()` filter still satisfies the 0.33.1
353`FingerprintSimilarity` interface, but it is marked for removal in matchms 1.0.
354The `Fingerprints` bridge above avoids calling that deprecated filter while
355remaining compatible with the current similarity class.

Molecular Fingerprint Similarity

FingerprintSimilarity compares molecular fingerprints derived from known structures. It therefore measures structure similarity, not spectral similarity.

from matchms import Fingerprints, calculate_scores
from matchms.similarity import FingerprintSimilarity

fp_store = Fingerprints(
    fingerprint_algorithm="morgan2",
    fingerprint_method="bit",
    nbits=2048,
)
fp_store.compute_fingerprints(spectra)

usable = []
for spectrum in spectra:
    fingerprint = fp_store.get_fingerprint_by_spectrum(spectrum)
    if fingerprint is not None:
        spectrum.set("fingerprint", fingerprint)
        usable.append(spectrum)

scores = calculate_scores(
    usable,
    usable,
    FingerprintSimilarity(similarity_measure="jaccard"),
    is_symmetric=True,
)

Similarity measures are jaccard, dice, and cosine.

The old add_fingerprint() filter still satisfies the 0.33.1 FingerprintSimilarity interface, but it is marked for removal in matchms 1.0. The Fingerprints bridge above avoids calling that deprecated filter while remaining compatible with the current similarity class.

Raw lines 318 to 356
357## Efficient Precursor-Gated Search
359Filter score coordinates before calculating an expensive spectral metric:
361```python
362from matchms import calculate_scores
363from matchms.similarity import ModifiedCosineGreedy, PrecursorMzMatch
365scores = calculate_scores(
366 references,
367 queries,
368 PrecursorMzMatch(tolerance=10, tolerance_type="ppm"),
369 array_type="sparse",
371scores.filter_by_range(name="PrecursorMzMatch", low=0.5)
372if len(scores.scores.row) == 0:
373 raise ValueError("No candidates passed the precursor gate")
374scores.calculate(
375 ModifiedCosineGreedy(tolerance=0.02),
376 array_type="sparse",
377 join_type="left",
379```
381In 0.33.1, `Scores.calculate` selects its coordinate-only `sparse_array` path
382only with an existing score layer, `join_type="left"` or `"inner"`, and **fewer
383than half** of all coordinates retained. Otherwise it computes the full matrix
384then joins; setting `array_type="sparse"` alone does not change this. A 1-by-1
385input always uses `pair()`, even after a rejecting gate. Stop explicitly when
386no candidate coordinates remain. Confirm score names and coordinate counts
387after each stage. An overly narrow precursor gate removes analogs or different
388adducts; this example is for identity-oriented candidate search.
390The `Pipeline` class formalizes the same pattern and can persist a YAML
391workflow. See `workflows.md`.

Efficient Precursor-Gated Search

Filter score coordinates before calculating an expensive spectral metric:

from matchms import calculate_scores
from matchms.similarity import ModifiedCosineGreedy, PrecursorMzMatch

scores = calculate_scores(
    references,
    queries,
    PrecursorMzMatch(tolerance=10, tolerance_type="ppm"),
    array_type="sparse",
)
scores.filter_by_range(name="PrecursorMzMatch", low=0.5)
if len(scores.scores.row) == 0:
    raise ValueError("No candidates passed the precursor gate")
scores.calculate(
    ModifiedCosineGreedy(tolerance=0.02),
    array_type="sparse",
    join_type="left",
)

In 0.33.1, Scores.calculate selects its coordinate-only sparse_array path only with an existing score layer, join_type="left" or "inner", and fewer than half of all coordinates retained. Otherwise it computes the full matrix then joins; setting array_type="sparse" alone does not change this. A 1-by-1 input always uses pair(), even after a rejecting gate. Stop explicitly when no candidate coordinates remain. Confirm score names and coordinate counts after each stage. An overly narrow precursor gate removes analogs or different adducts; this example is for identity-oriented candidate search.

The Pipeline class formalizes the same pattern and can persist a YAML workflow. See workflows.md.

Raw lines 357 to 392
393## Filtering and Exporting Scores
395```python
396print(scores.score_names)
398scores.filter_by_range(
399 name="ModifiedCosineGreedy_score",
400 low=0.6,
401 above_operator=">=",
404dense = scores.to_array("ModifiedCosineGreedy_score")
405coo = scores.to_coo("ModifiedCosineGreedy_score")
406scores.to_json("scores.json")
407```
409`filter_by_range()` mutates the stored score coordinates. Preserve an unfiltered
410copy or serialize first when alternative thresholds must be compared.
412Dense arrays require memory proportional to
413`n_references * n_queries`. A sparse container is most useful only when a mask
414or score threshold leaves relatively few stored pairs.

Filtering and Exporting Scores

print(scores.score_names)

scores.filter_by_range(
    name="ModifiedCosineGreedy_score",
    low=0.6,
    above_operator=">=",
)

dense = scores.to_array("ModifiedCosineGreedy_score")
coo = scores.to_coo("ModifiedCosineGreedy_score")
scores.to_json("scores.json")

filter_by_range() mutates the stored score coordinates. Preserve an unfiltered copy or serialize first when alternative thresholds must be compared.

Dense arrays require memory proportional to n_references * n_queries. A sparse container is most useful only when a mask or score threshold leaves relatively few stored pairs.

Raw lines 393 to 415
416## Combining Metrics
418Do not directly index `scores.scores[j, i]` and assume a scalar. Extract named
419layers:
421```python
422cosine = scores.to_array("CosineGreedy_score")
423matches = scores.to_array("CosineGreedy_matches")
424```
426If combining metrics:
428- define whether missing/filtered pairs are zero, missing, or excluded;
429- normalize only when score semantics justify it;
430- fit or justify weights on an appropriate validation set;
431- avoid leaking query identities into tuning;
432- retain each component score in the output; and
433- report the exact formula and package version.

Combining Metrics

Do not directly index scores.scores[j, i] and assume a scalar. Extract named layers:

cosine = scores.to_array("CosineGreedy_score")
matches = scores.to_array("CosineGreedy_matches")

If combining metrics:

  • define whether missing/filtered pairs are zero, missing, or excluded;
  • normalize only when score semantics justify it;
  • fit or justify weights on an appropriate validation set;
  • avoid leaking query identities into tuning;
  • retain each component score in the output; and
  • report the exact formula and package version.
Raw lines 416 to 434
435## Interpretation Checklist
437- Was identical preprocessing applied to references and queries?
438- Is the tolerance appropriate for the instrument and calibration?
439- Are precursor m/z, charge, ion mode, and adduct metadata valid?
440- How many peaks matched, and what fraction of each spectrum do they represent?
441- Is the hit driven by one dominant/common fragment?
442- Are collision energy and acquisition conditions comparable?
443- Is the query actually present in the searched library?
444- Was the threshold validated on representative positives and negatives?
445- Has the top hit been inspected with a mirror plot?
447A high score ranks a candidate under a chosen metric. It does not, by itself,
448establish compound identity.

Interpretation Checklist

  • Was identical preprocessing applied to references and queries?
  • Is the tolerance appropriate for the instrument and calibration?
  • Are precursor m/z, charge, ion mode, and adduct metadata valid?
  • How many peaks matched, and what fraction of each spectrum do they represent?
  • Is the hit driven by one dominant/common fragment?
  • Are collision energy and acquisition conditions comparable?
  • Is the query actually present in the searched library?
  • Was the threshold validated on representative positives and negatives?
  • Has the top hit been inspected with a mirror plot?

A high score ranks a candidate under a chosen metric. It does not, by itself, establish compound identity.

Raw lines 435 to 448