Back to Claude Scientific Skills

Similarity and Scores (matchms 0.33.1)

skills/matchms/references/similarity.md

2.55.011.3 KB
Original Source

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.

Core Calculation

python
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.

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:

python
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.

Top hits for one query

python
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.

Iterate stored pairs

python
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.

Peak-Based Cosine Methods

All cosine classes below use:

text
tolerance=0.1, mz_power=0.0, intensity_power=1.0

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

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.

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.

CosineLinear

Added in 0.33.0 as a linear-scaling cosine implementation.

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.

Modified Cosine

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

ModifiedCosineGreedy

python
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.

ModifiedCosineHungarian

python
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.

Neutral-Loss Cosine

python
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.

Fast Matrix-Oriented Methods

BlinkCosine

BLINK-style approximate cosine:

python
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. Output contains a float32 score and matched-peak count. Validate approximation behavior against CosineGreedy on a subset before changing production workflows.

FlashSimilarity

Fast matrix scoring based on the Flash Entropy approach:

python
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:

  • score_type: spectral_entropy or cosine
  • matching_mode: fragment, neutral_loss, or hybrid
  • optional preprocessing: precursor removal, noise cutoff, peak merging, dtype, and identity precursor tolerance

pair() exists but emits a warning because it is not the optimized use. Call calculate_scores() so matchms uses the matrix path. Flash output is a scalar field named FlashSimilarity, not a score/matches pair.

BinnedEmbeddingSimilarity

python
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. The class also supports approximate-neighbor indexing through the current PyNNDescent backend; use it only after checking recall against exact neighbors.

Candidate and Metadata Matches

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

PrecursorMzMatch

python
from matchms.similarity import PrecursorMzMatch

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

Output field: PrecursorMzMatch (boolean).

ParentMassMatch

python
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.

MetadataMatch

python
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.

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.

Molecular Fingerprint Similarity

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

python
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.

Filter score coordinates before calculating an expensive spectral metric:

python
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)
scores.calculate(
    ModifiedCosineGreedy(tolerance=0.02),
    array_type="sparse",
    join_type="left",
)

After filter_by_range, the second calculation can operate on retained coordinates. Confirm scores.score_names and stored-coordinate counts after each stage. An overly narrow precursor gate can remove valid analogs or different adducts.

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

Filtering and Exporting Scores

python
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.

Combining Metrics

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

python
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.

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.