[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M73H5DA8KVGDMC86MHNPMVXH":3,"skill-same:01M73H5DA8KVGDMC86MHNPMVXH":48},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":29,"ownerHtmlUrl":30,"ownerBlogUrl":31,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M73H5DA8KVGDMC86MHNPMVXH","matchms-spectral-matching",null,"MS spectral matching and metabolite ID with matchms. Import spectra (mzML, MGF, MSP, JSON), filter\u002Fnormalize peaks, score similarity (cosine, modified cosine, fingerprint), build reproducible pipelines, identify unknowns vs spectral libraries. Use pyopenms for full LC-MS\u002FMS proteomics.","bg-szy\u002FTOP-SKILLS","bg-szy","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F106576190?v=4","01M73A4E8KPV67SFXRY8XZGNBX","ShuZhiyong","01M73A4E96MQ294MHM74TZBVQD",6,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fawesome-skills\u002Fmatchms-spectral-matching\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: matchms-spectral-matching\ndescription: MS spectral matching and metabolite ID with matchms. Import spectra (mzML, MGF, MSP, JSON), filter\u002Fnormalize peaks, score similarity (cosine, modified cosine, fingerprint), build reproducible pipelines, identify unknowns vs spectral libraries. Use pyopenms for full LC-MS\u002FMS proteomics.\nlicense: Apache-2.0\n---\n\n# Matchms — Spectral Matching & Metabolite Identification\n\n## Overview\n\nMatchms is a Python library for mass spectrometry data processing focused on spectral similarity calculation and compound identification. It provides multi-format I\u002FO, 50+ spectrum filters for metadata harmonization and peak processing, 8 similarity scoring functions, and a pipeline framework for reproducible analytical workflows.\n\n## When to Use\n\n- Identifying unknown metabolites by matching MS\u002FMS spectra against reference libraries\n- Computing spectral similarity scores (cosine, modified cosine, fingerprint-based)\n- Processing and standardizing mass spectral data from multiple formats (mzML, MGF, MSP, JSON)\n- Building reproducible spectral processing pipelines for quality control\n- Harmonizing metadata across spectral databases (compound names, SMILES, InChI, adducts)\n- Large-scale spectral library comparisons and duplicate detection\n- For full LC-MS\u002FMS proteomics workflows (feature detection, protein ID), use **pyopenms** instead\n- For chemical structure similarity without mass spectra, use **rdkit** fingerprint comparison\n\n## Prerequisites\n\n```bash\nuv pip install matchms numpy pandas\n# For chemical structure processing (SMILES, InChI, fingerprints):\nuv pip install matchms[chemistry]\n```\n\n- Python 3.8+; NumPy for peak array operations\n- Input: spectral data in MGF, MSP, mzML, mzXML, JSON, or pickle format\n- Reference library in any supported format for matching\n\n## Pre-flight Interview\n\nSettle these with the user before writing any analysis code.\n\n```yaml\ndecisions:\n  - id: D1\n    param: referenceLibrary\n    kind: required\n    source: user\n    ask: \"Which spectral library should queries be matched against?\"\n    default: null\n\n  - id: D2\n    param: similarityMeasure\n    kind: required\n    source: user\n    ask: \"Should matching require the same precursor mass, or allow a mass shift so modified analogues still match?\"\n    default: \"cosine on matched peaks, precursor mass fixed\"\n\n  - id: D3\n    param: peakMatchTolerance\n    kind: required\n    source: data\n    ask: \"How close must two peaks be in m\u002Fz to count as the same fragment?\"\n    default: \"0.1 Da - tighten toward 0.005 for high-resolution data\"\n\n  - id: D4\n    param: spectrumPreprocessing\n    kind: required\n    source: user\n    ask: \"Which peaks should be discarded before scoring - low-intensity noise, peaks near the precursor, or spectra with too few peaks to be informative?\"\n    default: \"drop peaks under 1% relative intensity, require at least 10 peaks\"\n\n  - id: D5\n    param: scoreCutoff\n    kind: required\n    source: user\n    ask: \"What similarity score, and how many matched peaks, should a hit need before it is reported as an identification?\"\n    default: null\n\n  - id: D6\n    param: peakWeighting\n    kind: optional\n    source: user\n    ask: \"Should the score weight fragment m\u002Fz as well as intensity, favouring informative high-mass fragments?\"\n    default: \"intensity only\"\n\n  - id: D7\n    param: fingerprintSettings\n    kind: optional_conditional\n    source: user\n    ask: \"Which molecular fingerprint should structural similarity use, when comparing hits to known structures?\"\n    default: \"not computed\"\n```\n\nD3 and D5 have no safe shared default because they trade off directly: a loose\ntolerance with a low score cutoff returns an identification for every query\nspectrum, all of them plausible-looking. Set the tolerance from the\ninstrument's actual accuracy and the cutoff from what the library supports.\n\n## Quick Start\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\nfrom matchms.filtering import select_by_relative_intensity, require_minimum_number_of_peaks\nfrom matchms import calculate_scores\nfrom matchms.similarity import CosineGreedy\n\n# Load and process query spectra\nqueries = list(load_from_mgf(\"queries.mgf\"))\nqueries = [default_filters(s) for s in queries]\nqueries = [normalize_intensities(s) for s in queries if s is not None]\nqueries = [require_minimum_number_of_peaks(s, n_required=5) for s in queries if s is not None]\n\n# Load reference library\nrefs = list(load_from_mgf(\"library.mgf\"))\nrefs = [default_filters(s) for s in refs]\nrefs = [normalize_intensities(s) for s in refs if s is not None]\n\n# Calculate similarity scores\nscores = calculate_scores(references=refs, queries=queries,\n                          similarity_function=CosineGreedy(tolerance=0.1))\n# Get best matches for first query\nbest = scores.scores_by_query(queries[0], sort=True)[:5]\nfor match, score_tuple in best:\n    print(f\"Score: {score_tuple['score']:.3f}, Matches: {score_tuple['matches']}\")\n```\n\n## Core API\n\n### Module 1: Spectrum I\u002FO\n\nImport spectra from multiple file formats and export processed data.\n\n```python\nfrom matchms.importing import (load_from_mgf, load_from_mzml, load_from_msp,\n                                load_from_json, load_from_mzxml, load_from_pickle,\n                                load_from_usi)\nfrom matchms.exporting import save_as_mgf, save_as_msp, save_as_json, save_as_pickle\n\n# Import from various formats (returns generators)\nspectra_mgf = list(load_from_mgf(\"library.mgf\"))\nspectra_mzml = list(load_from_mzml(\"data.mzML\"))\nspectra_msp = list(load_from_msp(\"nist_library.msp\"))\nspectra_json = list(load_from_json(\"gnps_spectra.json\"))\nprint(f\"Loaded: MGF={len(spectra_mgf)}, mzML={len(spectra_mzml)}\")\n\n# Export processed spectra\nsave_as_mgf(spectra_mgf, \"processed.mgf\")\nsave_as_json(spectra_mgf, \"processed.json\")\nsave_as_pickle(spectra_mgf, \"spectra.pickle\")  # Fast for intermediate results\n\n# Pickle for large datasets (fastest I\u002FO)\nfrom matchms.importing import load_from_pickle\nspectra = list(load_from_pickle(\"spectra.pickle\"))\n```\n\n```python\nfrom matchms import Spectrum\nimport numpy as np\n\n# Create spectrum manually\nmz = np.array([100.0, 150.0, 200.0, 250.0, 300.0])\nintensities = np.array([0.1, 0.5, 0.9, 0.3, 0.7])\nmetadata = {\n    \"precursor_mz\": 325.5,\n    \"ionmode\": \"positive\",\n    \"compound_name\": \"Caffeine\",\n    \"smiles\": \"CN1C=NC2=C1C(=O)N(C(=O)N2C)C\"\n}\nspectrum = Spectrum(mz=mz, intensities=intensities, metadata=metadata)\n\n# Access spectrum data\nprint(f\"Peaks: {spectrum.peaks.mz}\")\nprint(f\"Precursor: {spectrum.get('precursor_mz')}\")\nprint(f\"Name: {spectrum.get('compound_name')}\")\n\n# Visualize\nspectrum.plot()\n```\n\n### Module 2: Spectrum Filtering & Processing\n\nApply metadata harmonization and peak processing filters. Matchms provides 50+ filters.\n\n```python\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, select_by_mz,\n    require_minimum_number_of_peaks, reduce_to_number_of_peaks,\n    remove_peaks_around_precursor_mz, add_losses\n)\n\n# default_filters applies: metadata cleanup, charge correction, adduct parsing\nspectrum = default_filters(spectrum)\n\n# Peak normalization (max intensity → 1.0)\nspectrum = normalize_intensities(spectrum)\n\n# Filter peaks by relative intensity (remove noise below 1%)\nspectrum = select_by_relative_intensity(spectrum, intensity_from=0.01, intensity_to=1.0)\n\n# Filter peaks by m\u002Fz range\nspectrum = select_by_mz(spectrum, mz_from=50.0, mz_to=500.0)\n\n# Keep top N peaks only\nspectrum = reduce_to_number_of_peaks(spectrum, n_max=50)\n\n# Remove peaks near precursor (common contaminants)\nspectrum = remove_peaks_around_precursor_mz(spectrum, mz_tolerance=17.0)\n\n# Require minimum peaks for matching\nspectrum = require_minimum_number_of_peaks(spectrum, n_required=5)\n\n# Add neutral losses (useful for NeutralLossesCosine)\nspectrum = add_losses(spectrum)\n\nif spectrum is not None:\n    print(f\"After filtering: {len(spectrum.peaks.mz)} peaks\")\n```\n\n```python\n# Chemical annotation filters (require matchms[chemistry])\nfrom matchms.filtering import (\n    derive_inchi_from_smiles, derive_inchikey_from_inchi,\n    derive_smiles_from_inchi, add_fingerprint,\n    repair_inchi_inchikey_smiles, require_valid_annotation\n)\n\n# Derive chemical identifiers from SMILES\nspectrum = derive_inchi_from_smiles(spectrum)\nspectrum = derive_inchikey_from_inchi(spectrum)\n\n# Add molecular fingerprint for structural similarity\nspectrum = add_fingerprint(spectrum, fingerprint_type=\"morgan\", nbits=2048)\n\n# Validate annotations\nspectrum = require_valid_annotation(spectrum)\nif spectrum is not None:\n    print(f\"InChIKey: {spectrum.get('inchikey')}\")\n```\n\n### Module 3: Similarity Scoring\n\nCompare spectra using multiple similarity metrics.\n\n```python\nfrom matchms import calculate_scores\nfrom matchms.similarity import (\n    CosineGreedy, CosineHungarian, ModifiedCosine,\n    NeutralLossesCosine, FingerprintSimilarity,\n    MetadataMatch, PrecursorMzMatch\n)\n\n# CosineGreedy — fast peak matching (greedy algorithm)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=CosineGreedy(tolerance=0.1))\n\n# ModifiedCosine — accounts for precursor mass differences (best for analog search)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=ModifiedCosine(tolerance=0.1))\n\n# CosineHungarian — optimal peak matching (slower but more accurate)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=CosineHungarian(tolerance=0.1))\n\n# NeutralLossesCosine — similarity based on neutral loss patterns\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=NeutralLossesCosine(tolerance=0.1))\n\n# Access results\nfor i, query in enumerate(unknowns[:3]):\n    best_matches = scores.scores_by_query(query, sort=True)[:3]\n    print(f\"\\nQuery {i}: precursor_mz={query.get('precursor_mz')}\")\n    for ref, score_tuple in best_matches:\n        print(f\"  {ref.get('compound_name', 'Unknown')}: \"\n              f\"score={score_tuple['score']:.3f}, matches={score_tuple['matches']}\")\n```\n\n```python\n# FingerprintSimilarity — structural similarity (requires fingerprints)\nfrom matchms.similarity import FingerprintSimilarity\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=FingerprintSimilarity(\n                              similarity_measure=\"jaccard\"))\n\n# PrecursorMzMatch — fast mass-based pre-filtering\nfrom matchms.similarity import PrecursorMzMatch\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=PrecursorMzMatch(tolerance=0.1))\n\n# Multi-metric scoring: combine peak + structural similarity\ncosine_scores = calculate_scores(references=library, queries=unknowns,\n                                  similarity_function=CosineGreedy(tolerance=0.1))\nfp_scores = calculate_scores(references=library, queries=unknowns,\n                              similarity_function=FingerprintSimilarity())\n```\n\n### Module 4: Processing Pipelines\n\nBuild reusable, reproducible multi-step processing workflows.\n\n```python\nfrom matchms import SpectrumProcessor\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, require_minimum_number_of_peaks,\n    remove_peaks_around_precursor_mz, add_losses\n)\n\n# Define reusable pipeline\npipeline = SpectrumProcessor([\n    default_filters,\n    normalize_intensities,\n    lambda s: select_by_relative_intensity(s, intensity_from=0.01),\n    lambda s: remove_peaks_around_precursor_mz(s, mz_tolerance=17.0),\n    lambda s: require_minimum_number_of_peaks(s, n_required=5),\n    add_losses\n])\n\n# Apply to all spectra (filters returning None remove the spectrum)\nprocessed = [pipeline(s) for s in raw_spectra]\nprocessed = [s for s in processed if s is not None]\nprint(f\"Processed: {len(processed)}\u002F{len(raw_spectra)} spectra retained\")\n```\n\n## Key Concepts\n\n### Similarity Function Comparison\n\n| Function | Speed | Accuracy | Best For |\n|----------|-------|----------|----------|\n| `CosineGreedy` | Fast | Good | General library matching |\n| `CosineHungarian` | Slow | Best | Small comparisons, validation |\n| `ModifiedCosine` | Fast | Good | Analog search (different precursors) |\n| `NeutralLossesCosine` | Medium | Good | Structural class identification |\n| `FingerprintSimilarity` | Fast | Moderate | Structure-based pre-filtering |\n| `PrecursorMzMatch` | Fastest | N\u002FA | Mass-based pre-filtering |\n\n### Filter Categories\n\n| Category | Examples | Purpose |\n|----------|----------|---------|\n| **Metadata cleanup** | `default_filters`, `clean_compound_name`, `clean_adduct` | Standardize metadata fields |\n| **Chemical derivation** | `derive_inchi_from_smiles`, `add_fingerprint` | Compute chemical identifiers |\n| **Mass\u002Fcharge** | `add_precursor_mz`, `correct_charge`, `add_parent_mass` | Fix and validate mass info |\n| **Peak normalization** | `normalize_intensities`, `select_by_relative_intensity` | Scale and filter peaks |\n| **Peak reduction** | `reduce_to_number_of_peaks`, `remove_peaks_around_precursor_mz` | Remove noise\u002Fartifacts |\n| **Quality control** | `require_minimum_number_of_peaks`, `require_precursor_mz` | Enforce minimum quality |\n| **Neutral losses** | `add_losses` | Compute precursor-fragment losses |\n\n### Score Tuple Structure\n\nAll similarity functions return `(score, matches)`:\n- `score`: float 0.0–1.0 (cosine similarity value)\n- `matches`: int (number of matched peaks between query and reference)\n\nHigher scores and more matched peaks indicate better matches. Typical thresholds: score > 0.7 and matches > 6 for confident identifications.\n\n## Common Workflows\n\n### Workflow 1: Library Matching for Metabolite Identification\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\nfrom matchms.filtering import select_by_relative_intensity, require_minimum_number_of_peaks\nfrom matchms import calculate_scores\nfrom matchms.similarity import ModifiedCosine\nimport pandas as pd\n\n# Load and process both queries and library identically\ndef process_spectra(spectra):\n    processed = []\n    for s in spectra:\n        s = default_filters(s)\n        if s is None: continue\n        s = normalize_intensities(s)\n        s = select_by_relative_intensity(s, intensity_from=0.01)\n        s = require_minimum_number_of_peaks(s, n_required=5)\n        if s is not None:\n            processed.append(s)\n    return processed\n\nqueries = process_spectra(load_from_mgf(\"unknowns.mgf\"))\nlibrary = process_spectra(load_from_mgf(\"reference_library.mgf\"))\nprint(f\"Queries: {len(queries)}, Library: {len(library)}\")\n\n# Score all query-reference pairs\nscores = calculate_scores(references=library, queries=queries,\n                          similarity_function=ModifiedCosine(tolerance=0.1))\n\n# Extract best matches\nresults = []\nfor query in queries:\n    best = scores.scores_by_query(query, sort=True)[:1]\n    if best:\n        ref, score_tuple = best[0]\n        results.append({\n            \"query_precursor_mz\": query.get(\"precursor_mz\"),\n            \"match_name\": ref.get(\"compound_name\", \"Unknown\"),\n            \"match_smiles\": ref.get(\"smiles\", \"\"),\n            \"score\": score_tuple[\"score\"],\n            \"matched_peaks\": score_tuple[\"matches\"]\n        })\n\ndf = pd.DataFrame(results)\nconfident = df[df[\"score\"] > 0.7]\nprint(f\"Confident matches (score>0.7): {len(confident)}\u002F{len(df)}\")\ndf.to_csv(\"identification_results.csv\", index=False)\n```\n\n### Workflow 2: Quality Control and Data Cleaning\n\n```python\nfrom matchms.importing import load_from_msp\nfrom matchms.exporting import save_as_mgf\nfrom matchms import SpectrumProcessor\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, require_minimum_number_of_peaks,\n    require_precursor_mz, add_parent_mass\n)\n\n# Define QC pipeline\nqc_pipeline = SpectrumProcessor([\n    default_filters,\n    require_precursor_mz,\n    add_parent_mass,\n    normalize_intensities,\n    lambda s: select_by_relative_intensity(s, intensity_from=0.001),\n    lambda s: require_minimum_number_of_peaks(s, n_required=3)\n])\n\n# Process and filter\nraw = list(load_from_msp(\"raw_library.msp\"))\ncleaned = [qc_pipeline(s) for s in raw]\ncleaned = [s for s in cleaned if s is not None]\n\nprint(f\"Input: {len(raw)}, Output: {len(cleaned)} ({len(cleaned)\u002Flen(raw)*100:.1f}% retained)\")\nsave_as_mgf(cleaned, \"cleaned_library.mgf\")\n```\n\n### Workflow 3: Format Conversion\n\n1. Load spectra from source format (Core API Module 1 — `load_from_mzml`)\n2. Apply `default_filters` for metadata harmonization (Core API Module 2)\n3. Export to target format (Core API Module 1 — `save_as_mgf`)\n\n## Key Parameters\n\n| Parameter | Function\u002FModule | Default | Range\u002FOptions | Effect |\n|-----------|----------------|---------|---------------|--------|\n| `tolerance` | CosineGreedy\u002FModifiedCosine | 0.1 | 0.005–0.5 Da | m\u002Fz tolerance for peak matching |\n| `mz_power` | CosineGreedy | 0.0 | 0.0–2.0 | Weight of m\u002Fz in scoring (0=ignore) |\n| `intensity_power` | CosineGreedy | 1.0 | 0.0–2.0 | Weight of intensity in scoring |\n| `intensity_from` | select_by_relative_intensity | 0.0 | 0.0–1.0 | Minimum relative intensity to keep |\n| `n_required` | require_minimum_number_of_peaks | 10 | 1–100 | Minimum peaks to retain spectrum |\n| `n_max` | reduce_to_number_of_peaks | 100 | 10–500 | Maximum peaks to retain |\n| `mz_tolerance` | remove_peaks_around_precursor_mz | 17.0 | 0.5–50 Da | Window around precursor to remove |\n| `fingerprint_type` | add_fingerprint | \"daylight\" | \"daylight\"\u002F\"morgan\"\u002F\"maccs\" | Molecular fingerprint type |\n| `nbits` | add_fingerprint | 2048 | 256–4096 | Fingerprint bit vector length |\n\n## Best Practices\n\n1. **Always process queries and references identically** — apply the same filtering pipeline to both sets to avoid systematic bias in similarity scores\n2. **Save intermediate results in pickle** — pickle format is fastest for re-loading; use MGF\u002FMSP for sharing with other tools\n3. **Pre-filter by precursor mass** — for large libraries, use `PrecursorMzMatch` first to reduce the comparison space, then score with `CosineGreedy`\n4. **Combine multiple metrics** — use both CosineGreedy (peak matching) and FingerprintSimilarity (structure) for more robust identification\n5. **Check for None after filtering** — filters return `None` when a spectrum fails quality requirements. Always filter: `[s for s in processed if s is not None]`\n6. **Use ModifiedCosine for analog search** — when querying against libraries that may not have the exact compound, ModifiedCosine handles precursor mass differences\n\n## Common Recipes\n\n### Recipe 1: Precursor-Filtered Library Search (Efficient)\n\n```python\nfrom matchms import calculate_scores\nfrom matchms.similarity import PrecursorMzMatch, CosineGreedy\n\n# Step 1: Fast mass filter\nmass_scores = calculate_scores(references=library, queries=unknowns,\n                                similarity_function=PrecursorMzMatch(tolerance=0.5))\n\n# Step 2: Detailed scoring only for mass-matched pairs\ncosine = CosineGreedy(tolerance=0.1)\nfor query in unknowns:\n    candidates = mass_scores.scores_by_query(query, sort=True)\n    mass_matched = [ref for ref, score in candidates if score[\"score\"] > 0]\n    if mass_matched:\n        detailed = calculate_scores(references=mass_matched, queries=[query],\n                                     similarity_function=cosine)\n        best = detailed.scores_by_query(query, sort=True)[:3]\n        for ref, s in best:\n            print(f\"{ref.get('compound_name')}: {s['score']:.3f}\")\n```\n\n### Recipe 2: Ion Mode-Specific Processing\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\n\nspectra = list(load_from_mgf(\"mixed_library.mgf\"))\nspectra = [default_filters(s) for s in spectra]\nspectra = [s for s in spectra if s is not None]\n\n# Separate by ion mode\npositive = [s for s in spectra if s.get(\"ionmode\") == \"positive\"]\nnegative = [s for s in spectra if s.get(\"ionmode\") == \"negative\"]\nprint(f\"Positive: {len(positive)}, Negative: {len(negative)}\")\n\n# Process each mode with mode-specific filtering\npositive = [normalize_intensities(s) for s in positive]\nnegative = [normalize_intensities(s) for s in negative]\n```\n\n### Recipe 3: Metadata Enrichment Report\n\n```python\nimport pandas as pd\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters\n\nspectra = [default_filters(s) for s in load_from_mgf(\"library.mgf\")]\nspectra = [s for s in spectra if s is not None]\n\n# Extract metadata summary\nrows = []\nfor s in spectra:\n    rows.append({\n        \"compound_name\": s.get(\"compound_name\", \"\"),\n        \"precursor_mz\": s.get(\"precursor_mz\"),\n        \"ionmode\": s.get(\"ionmode\", \"\"),\n        \"smiles\": s.get(\"smiles\", \"\"),\n        \"inchikey\": s.get(\"inchikey\", \"\"),\n        \"num_peaks\": len(s.peaks.mz)\n    })\n\ndf = pd.DataFrame(rows)\nprint(f\"Library: {len(df)} spectra\")\nprint(f\"Named: {(df.compound_name != '').sum()}\")\nprint(f\"With SMILES: {(df.smiles != '').sum()}\")\nprint(f\"Ion modes: {df.ionmode.value_counts().to_dict()}\")\n```\n\n## Troubleshooting\n\n| Problem | Cause | Solution |\n|---------|-------|----------|\n| All scores are 0.0 | No matching peaks within tolerance | Increase `tolerance` (try 0.2–0.5 Da); verify both spectra have peaks |\n| Low scores despite same compound | Different fragmentation conditions | Use `ModifiedCosine` instead of `CosineGreedy`; check ion mode consistency |\n| Many spectra filtered to None | Too strict quality filters | Lower `n_required` in `require_minimum_number_of_peaks`; relax intensity thresholds |\n| `KeyError` on metadata field | Field name not harmonized | Apply `default_filters` first to harmonize metadata keys |\n| Memory error with large library | All-vs-all comparison | Pre-filter by precursor mass (`PrecursorMzMatch`) before detailed scoring |\n| `add_fingerprint` fails | RDKit not installed | Install chemistry extras: `pip install matchms[chemistry]` |\n| Import returns empty list | Wrong file format or path | Verify format matches loader (MGF for `.mgf`, MSP for `.msp`); check file is not empty |\n| Inconsistent scores between runs | Different processing pipelines | Use `SpectrumProcessor` to ensure identical processing for queries and references |\n\n## Bundled Resources\n\n- **references\u002Ffiltering_catalog.md** — Complete catalog of 50+ matchms filter functions organized by category (metadata processing, chemical structure, mass\u002Fcharge, peak processing, quality control).\n  - Covers: all filter function signatures with parameters and brief descriptions, common filter combinations\n  - Relocated inline: key filters (default_filters, normalize_intensities, select_by_relative_intensity, reduce_to_number_of_peaks, remove_peaks_around_precursor_mz, add_fingerprint) in Core API Module 2; filter category summary in Key Concepts\n  - Omitted: individual filter examples duplicating Core API patterns\n\n- **references\u002Fworkflows_similarity.md** — Extended workflows and detailed similarity function documentation consolidated from two original references.\n  - Covers: all 8 similarity functions with parameter tables, large-scale comparison strategies, multi-metric scoring patterns, 7 extended workflows (library matching, QC, multi-metric, format conversion, metadata enrichment, large-scale comparison, automated identification report), performance considerations\n  - Relocated inline: CosineGreedy\u002FModifiedCosine\u002FCosineHungarian\u002FFingerprintSimilarity usage in Core API Module 3; similarity comparison table in Key Concepts; library matching + QC workflows in Common Workflows\n  - Omitted: network-based spectral clustering workflow — requires external tool (spec2vec); spectra visualization tutorial — covered by `spectrum.plot()` in Core API Module 1\n\n**Disposition of original reference files:**\n- importing_exporting.md (417 lines) → fully consolidated into Core API Module 1 (I\u002FO functions, format list, Spectrum creation, pickle usage). Retained: all 7 import functions, 4 export functions, Spectrum class creation, format selection guidance. Omitted: USI detailed examples — niche use case\n- filtering.md (289 lines) → migrated as references\u002Ffiltering_catalog.md with key filters relocated to Core API Module 2\n- similarity.md (381 lines) → consolidated into references\u002Fworkflows_similarity.md with core functions in Core API Module 3\n- workflows.md (648 lines) → consolidated into references\u002Fworkflows_similarity.md with top workflows in Common Workflows\n\n## Related Skills\n\n- **pyopenms-mass-spectrometry** — full LC-MS\u002FMS proteomics and metabolomics pipelines (feature detection, protein ID)\n- **rdkit-cheminformatics** — molecular fingerprint generation and chemical structure similarity\n\n## References\n\n- matchms documentation: https:\u002F\u002Fmatchms.readthedocs.io\n- Huber et al. (2020) matchms — processing and similarity scoring of mass spectrometry data. *Journal of Open Source Software*, DOI: 10.21105\u002Fjoss.02411\n- GitHub: https:\u002F\u002Fgithub.com\u002Fmatchms\u002Fmatchms\n","# Matchms — Spectral Matching & Metabolite Identification\n\n## Overview\n\nMatchms is a Python library for mass spectrometry data processing focused on spectral similarity calculation and compound identification. It provides multi-format I\u002FO, 50+ spectrum filters for metadata harmonization and peak processing, 8 similarity scoring functions, and a pipeline framework for reproducible analytical workflows.\n\n## When to Use\n\n- Identifying unknown metabolites by matching MS\u002FMS spectra against reference libraries\n- Computing spectral similarity scores (cosine, modified cosine, fingerprint-based)\n- Processing and standardizing mass spectral data from multiple formats (mzML, MGF, MSP, JSON)\n- Building reproducible spectral processing pipelines for quality control\n- Harmonizing metadata across spectral databases (compound names, SMILES, InChI, adducts)\n- Large-scale spectral library comparisons and duplicate detection\n- For full LC-MS\u002FMS proteomics workflows (feature detection, protein ID), use **pyopenms** instead\n- For chemical structure similarity without mass spectra, use **rdkit** fingerprint comparison\n\n## Prerequisites\n\n```bash\nuv pip install matchms numpy pandas\n# For chemical structure processing (SMILES, InChI, fingerprints):\nuv pip install matchms[chemistry]\n```\n\n- Python 3.8+; NumPy for peak array operations\n- Input: spectral data in MGF, MSP, mzML, mzXML, JSON, or pickle format\n- Reference library in any supported format for matching\n\n## Pre-flight Interview\n\nSettle these with the user before writing any analysis code.\n\n```yaml\ndecisions:\n  - id: D1\n    param: referenceLibrary\n    kind: required\n    source: user\n    ask: \"Which spectral library should queries be matched against?\"\n    default: null\n\n  - id: D2\n    param: similarityMeasure\n    kind: required\n    source: user\n    ask: \"Should matching require the same precursor mass, or allow a mass shift so modified analogues still match?\"\n    default: \"cosine on matched peaks, precursor mass fixed\"\n\n  - id: D3\n    param: peakMatchTolerance\n    kind: required\n    source: data\n    ask: \"How close must two peaks be in m\u002Fz to count as the same fragment?\"\n    default: \"0.1 Da - tighten toward 0.005 for high-resolution data\"\n\n  - id: D4\n    param: spectrumPreprocessing\n    kind: required\n    source: user\n    ask: \"Which peaks should be discarded before scoring - low-intensity noise, peaks near the precursor, or spectra with too few peaks to be informative?\"\n    default: \"drop peaks under 1% relative intensity, require at least 10 peaks\"\n\n  - id: D5\n    param: scoreCutoff\n    kind: required\n    source: user\n    ask: \"What similarity score, and how many matched peaks, should a hit need before it is reported as an identification?\"\n    default: null\n\n  - id: D6\n    param: peakWeighting\n    kind: optional\n    source: user\n    ask: \"Should the score weight fragment m\u002Fz as well as intensity, favouring informative high-mass fragments?\"\n    default: \"intensity only\"\n\n  - id: D7\n    param: fingerprintSettings\n    kind: optional_conditional\n    source: user\n    ask: \"Which molecular fingerprint should structural similarity use, when comparing hits to known structures?\"\n    default: \"not computed\"\n```\n\nD3 and D5 have no safe shared default because they trade off directly: a loose\ntolerance with a low score cutoff returns an identification for every query\nspectrum, all of them plausible-looking. Set the tolerance from the\ninstrument's actual accuracy and the cutoff from what the library supports.\n\n## Quick Start\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\nfrom matchms.filtering import select_by_relative_intensity, require_minimum_number_of_peaks\nfrom matchms import calculate_scores\nfrom matchms.similarity import CosineGreedy\n\n# Load and process query spectra\nqueries = list(load_from_mgf(\"queries.mgf\"))\nqueries = [default_filters(s) for s in queries]\nqueries = [normalize_intensities(s) for s in queries if s is not None]\nqueries = [require_minimum_number_of_peaks(s, n_required=5) for s in queries if s is not None]\n\n# Load reference library\nrefs = list(load_from_mgf(\"library.mgf\"))\nrefs = [default_filters(s) for s in refs]\nrefs = [normalize_intensities(s) for s in refs if s is not None]\n\n# Calculate similarity scores\nscores = calculate_scores(references=refs, queries=queries,\n                          similarity_function=CosineGreedy(tolerance=0.1))\n# Get best matches for first query\nbest = scores.scores_by_query(queries[0], sort=True)[:5]\nfor match, score_tuple in best:\n    print(f\"Score: {score_tuple['score']:.3f}, Matches: {score_tuple['matches']}\")\n```\n\n## Core API\n\n### Module 1: Spectrum I\u002FO\n\nImport spectra from multiple file formats and export processed data.\n\n```python\nfrom matchms.importing import (load_from_mgf, load_from_mzml, load_from_msp,\n                                load_from_json, load_from_mzxml, load_from_pickle,\n                                load_from_usi)\nfrom matchms.exporting import save_as_mgf, save_as_msp, save_as_json, save_as_pickle\n\n# Import from various formats (returns generators)\nspectra_mgf = list(load_from_mgf(\"library.mgf\"))\nspectra_mzml = list(load_from_mzml(\"data.mzML\"))\nspectra_msp = list(load_from_msp(\"nist_library.msp\"))\nspectra_json = list(load_from_json(\"gnps_spectra.json\"))\nprint(f\"Loaded: MGF={len(spectra_mgf)}, mzML={len(spectra_mzml)}\")\n\n# Export processed spectra\nsave_as_mgf(spectra_mgf, \"processed.mgf\")\nsave_as_json(spectra_mgf, \"processed.json\")\nsave_as_pickle(spectra_mgf, \"spectra.pickle\")  # Fast for intermediate results\n\n# Pickle for large datasets (fastest I\u002FO)\nfrom matchms.importing import load_from_pickle\nspectra = list(load_from_pickle(\"spectra.pickle\"))\n```\n\n```python\nfrom matchms import Spectrum\nimport numpy as np\n\n# Create spectrum manually\nmz = np.array([100.0, 150.0, 200.0, 250.0, 300.0])\nintensities = np.array([0.1, 0.5, 0.9, 0.3, 0.7])\nmetadata = {\n    \"precursor_mz\": 325.5,\n    \"ionmode\": \"positive\",\n    \"compound_name\": \"Caffeine\",\n    \"smiles\": \"CN1C=NC2=C1C(=O)N(C(=O)N2C)C\"\n}\nspectrum = Spectrum(mz=mz, intensities=intensities, metadata=metadata)\n\n# Access spectrum data\nprint(f\"Peaks: {spectrum.peaks.mz}\")\nprint(f\"Precursor: {spectrum.get('precursor_mz')}\")\nprint(f\"Name: {spectrum.get('compound_name')}\")\n\n# Visualize\nspectrum.plot()\n```\n\n### Module 2: Spectrum Filtering & Processing\n\nApply metadata harmonization and peak processing filters. Matchms provides 50+ filters.\n\n```python\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, select_by_mz,\n    require_minimum_number_of_peaks, reduce_to_number_of_peaks,\n    remove_peaks_around_precursor_mz, add_losses\n)\n\n# default_filters applies: metadata cleanup, charge correction, adduct parsing\nspectrum = default_filters(spectrum)\n\n# Peak normalization (max intensity → 1.0)\nspectrum = normalize_intensities(spectrum)\n\n# Filter peaks by relative intensity (remove noise below 1%)\nspectrum = select_by_relative_intensity(spectrum, intensity_from=0.01, intensity_to=1.0)\n\n# Filter peaks by m\u002Fz range\nspectrum = select_by_mz(spectrum, mz_from=50.0, mz_to=500.0)\n\n# Keep top N peaks only\nspectrum = reduce_to_number_of_peaks(spectrum, n_max=50)\n\n# Remove peaks near precursor (common contaminants)\nspectrum = remove_peaks_around_precursor_mz(spectrum, mz_tolerance=17.0)\n\n# Require minimum peaks for matching\nspectrum = require_minimum_number_of_peaks(spectrum, n_required=5)\n\n# Add neutral losses (useful for NeutralLossesCosine)\nspectrum = add_losses(spectrum)\n\nif spectrum is not None:\n    print(f\"After filtering: {len(spectrum.peaks.mz)} peaks\")\n```\n\n```python\n# Chemical annotation filters (require matchms[chemistry])\nfrom matchms.filtering import (\n    derive_inchi_from_smiles, derive_inchikey_from_inchi,\n    derive_smiles_from_inchi, add_fingerprint,\n    repair_inchi_inchikey_smiles, require_valid_annotation\n)\n\n# Derive chemical identifiers from SMILES\nspectrum = derive_inchi_from_smiles(spectrum)\nspectrum = derive_inchikey_from_inchi(spectrum)\n\n# Add molecular fingerprint for structural similarity\nspectrum = add_fingerprint(spectrum, fingerprint_type=\"morgan\", nbits=2048)\n\n# Validate annotations\nspectrum = require_valid_annotation(spectrum)\nif spectrum is not None:\n    print(f\"InChIKey: {spectrum.get('inchikey')}\")\n```\n\n### Module 3: Similarity Scoring\n\nCompare spectra using multiple similarity metrics.\n\n```python\nfrom matchms import calculate_scores\nfrom matchms.similarity import (\n    CosineGreedy, CosineHungarian, ModifiedCosine,\n    NeutralLossesCosine, FingerprintSimilarity,\n    MetadataMatch, PrecursorMzMatch\n)\n\n# CosineGreedy — fast peak matching (greedy algorithm)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=CosineGreedy(tolerance=0.1))\n\n# ModifiedCosine — accounts for precursor mass differences (best for analog search)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=ModifiedCosine(tolerance=0.1))\n\n# CosineHungarian — optimal peak matching (slower but more accurate)\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=CosineHungarian(tolerance=0.1))\n\n# NeutralLossesCosine — similarity based on neutral loss patterns\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=NeutralLossesCosine(tolerance=0.1))\n\n# Access results\nfor i, query in enumerate(unknowns[:3]):\n    best_matches = scores.scores_by_query(query, sort=True)[:3]\n    print(f\"\\nQuery {i}: precursor_mz={query.get('precursor_mz')}\")\n    for ref, score_tuple in best_matches:\n        print(f\"  {ref.get('compound_name', 'Unknown')}: \"\n              f\"score={score_tuple['score']:.3f}, matches={score_tuple['matches']}\")\n```\n\n```python\n# FingerprintSimilarity — structural similarity (requires fingerprints)\nfrom matchms.similarity import FingerprintSimilarity\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=FingerprintSimilarity(\n                              similarity_measure=\"jaccard\"))\n\n# PrecursorMzMatch — fast mass-based pre-filtering\nfrom matchms.similarity import PrecursorMzMatch\nscores = calculate_scores(references=library, queries=unknowns,\n                          similarity_function=PrecursorMzMatch(tolerance=0.1))\n\n# Multi-metric scoring: combine peak + structural similarity\ncosine_scores = calculate_scores(references=library, queries=unknowns,\n                                  similarity_function=CosineGreedy(tolerance=0.1))\nfp_scores = calculate_scores(references=library, queries=unknowns,\n                              similarity_function=FingerprintSimilarity())\n```\n\n### Module 4: Processing Pipelines\n\nBuild reusable, reproducible multi-step processing workflows.\n\n```python\nfrom matchms import SpectrumProcessor\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, require_minimum_number_of_peaks,\n    remove_peaks_around_precursor_mz, add_losses\n)\n\n# Define reusable pipeline\npipeline = SpectrumProcessor([\n    default_filters,\n    normalize_intensities,\n    lambda s: select_by_relative_intensity(s, intensity_from=0.01),\n    lambda s: remove_peaks_around_precursor_mz(s, mz_tolerance=17.0),\n    lambda s: require_minimum_number_of_peaks(s, n_required=5),\n    add_losses\n])\n\n# Apply to all spectra (filters returning None remove the spectrum)\nprocessed = [pipeline(s) for s in raw_spectra]\nprocessed = [s for s in processed if s is not None]\nprint(f\"Processed: {len(processed)}\u002F{len(raw_spectra)} spectra retained\")\n```\n\n## Key Concepts\n\n### Similarity Function Comparison\n\n| Function | Speed | Accuracy | Best For |\n|----------|-------|----------|----------|\n| `CosineGreedy` | Fast | Good | General library matching |\n| `CosineHungarian` | Slow | Best | Small comparisons, validation |\n| `ModifiedCosine` | Fast | Good | Analog search (different precursors) |\n| `NeutralLossesCosine` | Medium | Good | Structural class identification |\n| `FingerprintSimilarity` | Fast | Moderate | Structure-based pre-filtering |\n| `PrecursorMzMatch` | Fastest | N\u002FA | Mass-based pre-filtering |\n\n### Filter Categories\n\n| Category | Examples | Purpose |\n|----------|----------|---------|\n| **Metadata cleanup** | `default_filters`, `clean_compound_name`, `clean_adduct` | Standardize metadata fields |\n| **Chemical derivation** | `derive_inchi_from_smiles`, `add_fingerprint` | Compute chemical identifiers |\n| **Mass\u002Fcharge** | `add_precursor_mz`, `correct_charge`, `add_parent_mass` | Fix and validate mass info |\n| **Peak normalization** | `normalize_intensities`, `select_by_relative_intensity` | Scale and filter peaks |\n| **Peak reduction** | `reduce_to_number_of_peaks`, `remove_peaks_around_precursor_mz` | Remove noise\u002Fartifacts |\n| **Quality control** | `require_minimum_number_of_peaks`, `require_precursor_mz` | Enforce minimum quality |\n| **Neutral losses** | `add_losses` | Compute precursor-fragment losses |\n\n### Score Tuple Structure\n\nAll similarity functions return `(score, matches)`:\n- `score`: float 0.0–1.0 (cosine similarity value)\n- `matches`: int (number of matched peaks between query and reference)\n\nHigher scores and more matched peaks indicate better matches. Typical thresholds: score > 0.7 and matches > 6 for confident identifications.\n\n## Common Workflows\n\n### Workflow 1: Library Matching for Metabolite Identification\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\nfrom matchms.filtering import select_by_relative_intensity, require_minimum_number_of_peaks\nfrom matchms import calculate_scores\nfrom matchms.similarity import ModifiedCosine\nimport pandas as pd\n\n# Load and process both queries and library identically\ndef process_spectra(spectra):\n    processed = []\n    for s in spectra:\n        s = default_filters(s)\n        if s is None: continue\n        s = normalize_intensities(s)\n        s = select_by_relative_intensity(s, intensity_from=0.01)\n        s = require_minimum_number_of_peaks(s, n_required=5)\n        if s is not None:\n            processed.append(s)\n    return processed\n\nqueries = process_spectra(load_from_mgf(\"unknowns.mgf\"))\nlibrary = process_spectra(load_from_mgf(\"reference_library.mgf\"))\nprint(f\"Queries: {len(queries)}, Library: {len(library)}\")\n\n# Score all query-reference pairs\nscores = calculate_scores(references=library, queries=queries,\n                          similarity_function=ModifiedCosine(tolerance=0.1))\n\n# Extract best matches\nresults = []\nfor query in queries:\n    best = scores.scores_by_query(query, sort=True)[:1]\n    if best:\n        ref, score_tuple = best[0]\n        results.append({\n            \"query_precursor_mz\": query.get(\"precursor_mz\"),\n            \"match_name\": ref.get(\"compound_name\", \"Unknown\"),\n            \"match_smiles\": ref.get(\"smiles\", \"\"),\n            \"score\": score_tuple[\"score\"],\n            \"matched_peaks\": score_tuple[\"matches\"]\n        })\n\ndf = pd.DataFrame(results)\nconfident = df[df[\"score\"] > 0.7]\nprint(f\"Confident matches (score>0.7): {len(confident)}\u002F{len(df)}\")\ndf.to_csv(\"identification_results.csv\", index=False)\n```\n\n### Workflow 2: Quality Control and Data Cleaning\n\n```python\nfrom matchms.importing import load_from_msp\nfrom matchms.exporting import save_as_mgf\nfrom matchms import SpectrumProcessor\nfrom matchms.filtering import (\n    default_filters, normalize_intensities,\n    select_by_relative_intensity, require_minimum_number_of_peaks,\n    require_precursor_mz, add_parent_mass\n)\n\n# Define QC pipeline\nqc_pipeline = SpectrumProcessor([\n    default_filters,\n    require_precursor_mz,\n    add_parent_mass,\n    normalize_intensities,\n    lambda s: select_by_relative_intensity(s, intensity_from=0.001),\n    lambda s: require_minimum_number_of_peaks(s, n_required=3)\n])\n\n# Process and filter\nraw = list(load_from_msp(\"raw_library.msp\"))\ncleaned = [qc_pipeline(s) for s in raw]\ncleaned = [s for s in cleaned if s is not None]\n\nprint(f\"Input: {len(raw)}, Output: {len(cleaned)} ({len(cleaned)\u002Flen(raw)*100:.1f}% retained)\")\nsave_as_mgf(cleaned, \"cleaned_library.mgf\")\n```\n\n### Workflow 3: Format Conversion\n\n1. Load spectra from source format (Core API Module 1 — `load_from_mzml`)\n2. Apply `default_filters` for metadata harmonization (Core API Module 2)\n3. Export to target format (Core API Module 1 — `save_as_mgf`)\n\n## Key Parameters\n\n| Parameter | Function\u002FModule | Default | Range\u002FOptions | Effect |\n|-----------|----------------|---------|---------------|--------|\n| `tolerance` | CosineGreedy\u002FModifiedCosine | 0.1 | 0.005–0.5 Da | m\u002Fz tolerance for peak matching |\n| `mz_power` | CosineGreedy | 0.0 | 0.0–2.0 | Weight of m\u002Fz in scoring (0=ignore) |\n| `intensity_power` | CosineGreedy | 1.0 | 0.0–2.0 | Weight of intensity in scoring |\n| `intensity_from` | select_by_relative_intensity | 0.0 | 0.0–1.0 | Minimum relative intensity to keep |\n| `n_required` | require_minimum_number_of_peaks | 10 | 1–100 | Minimum peaks to retain spectrum |\n| `n_max` | reduce_to_number_of_peaks | 100 | 10–500 | Maximum peaks to retain |\n| `mz_tolerance` | remove_peaks_around_precursor_mz | 17.0 | 0.5–50 Da | Window around precursor to remove |\n| `fingerprint_type` | add_fingerprint | \"daylight\" | \"daylight\"\u002F\"morgan\"\u002F\"maccs\" | Molecular fingerprint type |\n| `nbits` | add_fingerprint | 2048 | 256–4096 | Fingerprint bit vector length |\n\n## Best Practices\n\n1. **Always process queries and references identically** — apply the same filtering pipeline to both sets to avoid systematic bias in similarity scores\n2. **Save intermediate results in pickle** — pickle format is fastest for re-loading; use MGF\u002FMSP for sharing with other tools\n3. **Pre-filter by precursor mass** — for large libraries, use `PrecursorMzMatch` first to reduce the comparison space, then score with `CosineGreedy`\n4. **Combine multiple metrics** — use both CosineGreedy (peak matching) and FingerprintSimilarity (structure) for more robust identification\n5. **Check for None after filtering** — filters return `None` when a spectrum fails quality requirements. Always filter: `[s for s in processed if s is not None]`\n6. **Use ModifiedCosine for analog search** — when querying against libraries that may not have the exact compound, ModifiedCosine handles precursor mass differences\n\n## Common Recipes\n\n### Recipe 1: Precursor-Filtered Library Search (Efficient)\n\n```python\nfrom matchms import calculate_scores\nfrom matchms.similarity import PrecursorMzMatch, CosineGreedy\n\n# Step 1: Fast mass filter\nmass_scores = calculate_scores(references=library, queries=unknowns,\n                                similarity_function=PrecursorMzMatch(tolerance=0.5))\n\n# Step 2: Detailed scoring only for mass-matched pairs\ncosine = CosineGreedy(tolerance=0.1)\nfor query in unknowns:\n    candidates = mass_scores.scores_by_query(query, sort=True)\n    mass_matched = [ref for ref, score in candidates if score[\"score\"] > 0]\n    if mass_matched:\n        detailed = calculate_scores(references=mass_matched, queries=[query],\n                                     similarity_function=cosine)\n        best = detailed.scores_by_query(query, sort=True)[:3]\n        for ref, s in best:\n            print(f\"{ref.get('compound_name')}: {s['score']:.3f}\")\n```\n\n### Recipe 2: Ion Mode-Specific Processing\n\n```python\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters, normalize_intensities\n\nspectra = list(load_from_mgf(\"mixed_library.mgf\"))\nspectra = [default_filters(s) for s in spectra]\nspectra = [s for s in spectra if s is not None]\n\n# Separate by ion mode\npositive = [s for s in spectra if s.get(\"ionmode\") == \"positive\"]\nnegative = [s for s in spectra if s.get(\"ionmode\") == \"negative\"]\nprint(f\"Positive: {len(positive)}, Negative: {len(negative)}\")\n\n# Process each mode with mode-specific filtering\npositive = [normalize_intensities(s) for s in positive]\nnegative = [normalize_intensities(s) for s in negative]\n```\n\n### Recipe 3: Metadata Enrichment Report\n\n```python\nimport pandas as pd\nfrom matchms.importing import load_from_mgf\nfrom matchms.filtering import default_filters\n\nspectra = [default_filters(s) for s in load_from_mgf(\"library.mgf\")]\nspectra = [s for s in spectra if s is not None]\n\n# Extract metadata summary\nrows = []\nfor s in spectra:\n    rows.append({\n        \"compound_name\": s.get(\"compound_name\", \"\"),\n        \"precursor_mz\": s.get(\"precursor_mz\"),\n        \"ionmode\": s.get(\"ionmode\", \"\"),\n        \"smiles\": s.get(\"smiles\", \"\"),\n        \"inchikey\": s.get(\"inchikey\", \"\"),\n        \"num_peaks\": len(s.peaks.mz)\n    })\n\ndf = pd.DataFrame(rows)\nprint(f\"Library: {len(df)} spectra\")\nprint(f\"Named: {(df.compound_name != '').sum()}\")\nprint(f\"With SMILES: {(df.smiles != '').sum()}\")\nprint(f\"Ion modes: {df.ionmode.value_counts().to_dict()}\")\n```\n\n## Troubleshooting\n\n| Problem | Cause | Solution |\n|---------|-------|----------|\n| All scores are 0.0 | No matching peaks within tolerance | Increase `tolerance` (try 0.2–0.5 Da); verify both spectra have peaks |\n| Low scores despite same compound | Different fragmentation conditions | Use `ModifiedCosine` instead of `CosineGreedy`; check ion mode consistency |\n| Many spectra filtered to None | Too strict quality filters | Lower `n_required` in `require_minimum_number_of_peaks`; relax intensity thresholds |\n| `KeyError` on metadata field | Field name not harmonized | Apply `default_filters` first to harmonize metadata keys |\n| Memory error with large library | All-vs-all comparison | Pre-filter by precursor mass (`PrecursorMzMatch`) before detailed scoring |\n| `add_fingerprint` fails | RDKit not installed | Install chemistry extras: `pip install matchms[chemistry]` |\n| Import returns empty list | Wrong file format or path | Verify format matches loader (MGF for `.mgf`, MSP for `.msp`); check file is not empty |\n| Inconsistent scores between runs | Different processing pipelines | Use `SpectrumProcessor` to ensure identical processing for queries and references |\n\n## Bundled Resources\n\n- **references\u002Ffiltering_catalog.md** — Complete catalog of 50+ matchms filter functions organized by category (metadata processing, chemical structure, mass\u002Fcharge, peak processing, quality control).\n  - Covers: all filter function signatures with parameters and brief descriptions, common filter combinations\n  - Relocated inline: key filters (default_filters, normalize_intensities, select_by_relative_intensity, reduce_to_number_of_peaks, remove_peaks_around_precursor_mz, add_fingerprint) in Core API Module 2; filter category summary in Key Concepts\n  - Omitted: individual filter examples duplicating Core API patterns\n\n- **references\u002Fworkflows_similarity.md** — Extended workflows and detailed similarity function documentation consolidated from two original references.\n  - Covers: all 8 similarity functions with parameter tables, large-scale comparison strategies, multi-metric scoring patterns, 7 extended workflows (library matching, QC, multi-metric, format conversion, metadata enrichment, large-scale comparison, automated identification report), performance considerations\n  - Relocated inline: CosineGreedy\u002FModifiedCosine\u002FCosineHungarian\u002FFingerprintSimilarity usage in Core API Module 3; similarity comparison table in Key Concepts; library matching + QC workflows in Common Workflows\n  - Omitted: network-based spectral clustering workflow — requires external tool (spec2vec); spectra visualization tutorial — covered by `spectrum.plot()` in Core API Module 1\n\n**Disposition of original reference files:**\n- importing_exporting.md (417 lines) → fully consolidated into Core API Module 1 (I\u002FO functions, format list, Spectrum creation, pickle usage). Retained: all 7 import functions, 4 export functions, Spectrum class creation, format selection guidance. Omitted: USI detailed examples — niche use case\n- filtering.md (289 lines) → migrated as references\u002Ffiltering_catalog.md with key filters relocated to Core API Module 2\n- similarity.md (381 lines) → consolidated into references\u002Fworkflows_similarity.md with core functions in Core API Module 3\n- workflows.md (648 lines) → consolidated into references\u002Fworkflows_similarity.md with top workflows in Common Workflows\n\n## Related Skills\n\n- **pyopenms-mass-spectrometry** — full LC-MS\u002FMS proteomics and metabolomics pipelines (feature detection, protein ID)\n- **rdkit-cheminformatics** — molecular fingerprint generation and chemical structure similarity\n\n## References\n\n- matchms documentation: https:\u002F\u002Fmatchms.readthedocs.io\n- Huber et al. (2020) matchms — processing and similarity scoring of mass spectrometry data. *Journal of Open Source Software*, DOI: 10.21105\u002Fjoss.02411\n- GitHub: https:\u002F\u002Fgithub.com\u002Fmatchms\u002Fmatchms\n",{"name":5,"license":15,"description":7},"master","fd76f882a04e445e899d5cfb2732f2105ea054ee","skills\u002Fawesome-skills\u002Fmatchms-spectral-matching",0,"https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS","https:\u002F\u002Fbg-szy.github.io\u002FTOP-SKILLS\u002F","https:\u002F\u002Fgithub.com\u002Fbg-szy","https:\u002F\u002Fwww.ecust.edu.cn\u002F","a student of East China University of Science and Technology","2026-11-12 05:55:03",{"source":8,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add bg-szy\u002FTOP-SKILLS --skill matchms-spectral-matching",{"tool":40,"command":41},"bunx","bunx skills add bg-szy\u002FTOP-SKILLS --skill matchms-spectral-matching",{"tool":43,"command":44},"pnpm","pnpm dlx skills add bg-szy\u002FTOP-SKILLS --skill matchms-spectral-matching","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fawesome-skills\u002Fmatchms-spectral-matching\u002FSKILL.md",true,[],[49,57,64,72,80,88],{"skillNo":50,"name":51,"nameZh":6,"description":52,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":53,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M748C5DS5BQBVBMG22FCGG34","branding","When the user wants to define, audit, or apply brand strategy—purpose, values, positioning, storytelling, voice, narrative (not only visuals). Also use when the user mentions \"brand strategy\", \"brand story\", \"brand storytelling\" or \"brand voice\". For typography, colors, design tokens, and frontend visuals, use brand-visual-generator.","MIT","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fsuperskills\u002Fbranding\u002FSKILL.md",[],[],{"skillNo":58,"name":59,"nameZh":6,"description":60,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":53,"licenseFileUrl":6,"sourceHtmlUrl":61,"categories":62,"keywords":63,"publishedAt":19},"01M748C1GXJ9WRKP18X45DKP6G","brand-visual-generator","When the user wants to define, audit, or apply visual identity (typography, colors, spacing, design tokens, frontend aesthetics). Also use when the user mentions \"brand style guide\", \"visual identity\", \"design system\" or \"typography\". For brand story, positioning, and voice, use branding.","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fsuperskills\u002Fbrand-visual-generator\u002FSKILL.md",[],[],{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":68,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":19},"01M747T565TFEP3RAVWM0Y899Y","mcp-builder","Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node\u002FTypeScript (MCP SDK).","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Fmcp-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Fmcp-builder\u002FSKILL.md",[],[],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":76,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":19},"01M747RAJGMBZ2QN4563STB5SH","frontend-design","Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Ffrontend-design\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Ffrontend-design\u002FSKILL.md",[],[],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":84,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":19},"01M747NHAY5BE9GXEB31H255R1","claude-api","Reference for the Claude API \u002F Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude\u002FAnthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing\u002Fmodel choice\u002Flimits\u002Fcaching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent\u002FMCP\u002Ftool-definition\u002Fmulti-agent\u002FRAG\u002FLLM-judge\u002Fcomputer-use; generate\u002Fsummarize\u002Fextract\u002Fclassify\u002Frewrite\u002Fconverse over NL; debugging refusals\u002Fcutoffs\u002Fstreaming\u002Ftool-calls\u002Ftokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI\u002FGPT\u002FGemini\u002FLlama\u002FMistral\u002FCohere\u002FOllama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Fclaude-api\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fskills\u002Fclaude-api\u002FSKILL.md",[],[],{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":53,"licenseFileUrl":92,"sourceHtmlUrl":93,"categories":94,"keywords":95,"publishedAt":19},"01M73WM4TPHEJ57D7G2AZ0ABVT","inbox-guardian-for-gmail","Review a personal Gmail inbox with local, owner-approved spam rules. Use for audit-first quarantine, Trash, repeated-evidence sender learning, and header review. Do not use for automatic unsubscribe requests or unreviewed mailbox actions.","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fmarketplace\u002Finbox-guardian-for-gmail\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fbg-szy\u002FTOP-SKILLS\u002Fblob\u002Fmaster\u002Fskills\u002Fmarketplace\u002Finbox-guardian-for-gmail\u002FSKILL.md",[],[]]