Repository: matchms/matchms
Pull request #664 (2024-05-27 13:28:05)

Add require number of peaks below maximum


Patch:
diff --git a/matchms/filtering/__init__.py b/matchms/filtering/__init__.py
index 6fe479aa..66126bd3 100644
--- a/matchms/filtering/__init__.py
+++ b/matchms/filtering/__init__.py
@@ -159,6 +159,8 @@ from matchms.filtering.peak_processing.remove_peaks_outside_top_k import \
     remove_peaks_outside_top_k
 from matchms.filtering.peak_processing.remove_profiled_spectra import \
     remove_profiled_spectra
+from matchms.filtering.peak_processing.require_maximum_number_of_peaks import \
+    require_maximum_number_of_peaks
 from matchms.filtering.peak_processing.require_minimum_number_of_high_peaks import \
     require_minimum_number_of_high_peaks
 from matchms.filtering.peak_processing.require_minimum_number_of_peaks import \
@@ -217,6 +219,7 @@ __all__ = [
     "require_matching_adduct_and_ionmode",
     "require_minimum_number_of_peaks",
     "require_minimum_number_of_high_peaks",
+    "require_maximum_number_of_peaks",
     "require_precursor_below_mz",
     "require_precursor_mz",
     "require_compound_name",
diff --git a/matchms/filtering/default_pipelines.py b/matchms/filtering/default_pipelines.py
index a4cc937b..e0bcab1f 100644
--- a/matchms/filtering/default_pipelines.py
+++ b/matchms/filtering/default_pipelines.py
@@ -61,6 +61,7 @@ OTHER_FILTERS = [matchms.filtering.metadata_processing.require_precursor_mz.requ
                  msfilters.add_fingerprint,
                  msfilters.add_losses,
                  msfilters.repair_parent_mass_match_smiles_wrapper,
+                 msfilters.require_maximum_number_of_peaks,
                  (msfilters.repair_adduct_and_parent_mass_based_on_smiles, {'mass_tolerance': 0.1}),
                  ]
 
diff --git a/matchms/filtering/filter_order.py b/matchms/filtering/filter_order.py
index 143c9a21..d5703c67 100644
--- a/matchms/filtering/filter_order.py
+++ b/matchms/filtering/filter_order.py
@@ -56,6 +56,7 @@ ALL_FILTERS = [msfilters.require_correct_ms_level,
                msfilters.remove_peaks_outside_top_k,
                msfilters.reduce_to_number_of_peaks,
                msfilters.require_minimum_number_of_peaks,
+               msfilters.require_maximum_number_of_peaks,
                msfilters.require_minimum_number_of_high_peaks,
                msfilters.add_fingerprint,
                msfilters.add_losses,
diff --git a/matchms/filtering/peak_processing/require_maximum_number_of_peaks.py b/matchms/filtering/peak_processing/require_maximum_number_of_peaks.py
new file mode 100644
index 00000000..f3f9e959
--- /dev/null
+++ b/matchms/filtering/peak_processing/require_maximum_number_of_peaks.py
@@ -0,0 +1,29 @@
+import logging
+from typing import Optional
+from matchms.Spectrum import Spectrum
+
+
+logger = logging.getLogger("matchms")
+
+
+def require_maximum_number_of_peaks(spectrum_in: Spectrum,
+                                    maximum_number_of_fragments: int = 1000) -> Optional[Spectrum]:
+    """Spectrum will be set to None when it has more peaks than maximum_number_of_fragments.
+
+    Parameters
+    ----------
+    spectrum_in:
+        Input spectrum.
+    maximum_number_of_fragments:
+        Number of minimum required peaks. Spectra with fewer peaks will be set
+        to 'None'.
+    """
+    if spectrum_in is None:
+        return None
+
+    if spectrum_in.peaks.intensities.size > maximum_number_of_fragments:
+        logger.info("Spectrum with %s (>%s) peaks was set to None.",
+                    str(spectrum_in.peaks.intensities.size), str(maximum_number_of_fragments))
+        return None
+
+    return spectrum_in


Test patch:
diff --git a/tests/filtering/test_all_filter_order.py b/tests/filtering/test_all_filter_order.py
index f7b56789..b10f7b33 100644
--- a/tests/filtering/test_all_filter_order.py
+++ b/tests/filtering/test_all_filter_order.py
@@ -62,7 +62,7 @@ DERIVE_ANNOTATION_FILTERS = [msfilters.derive_smiles_from_inchi,
                                                   msfilters.require_parent_mass_match_smiles,
                                                   msfilters.require_matching_adduct_precursor_mz_parent_mass,
                                                   ]],
-    [[msfilters.require_valid_annotation], [msfilters.repair_parent_mass_from_smiles]]
+    [[msfilters.require_valid_annotation], [msfilters.repair_parent_mass_from_smiles]]])
 def test_all_filter_order(early_filters: List[Callable], later_filters: List[Callable]):
     """Tests if early_filter is run before later_filter"""
     for early_filter in early_filters:
diff --git a/tests/filtering/test_require_maximum_number_of_peaks.py b/tests/filtering/test_require_maximum_number_of_peaks.py
new file mode 100644
index 00000000..5f62563a
--- /dev/null
+++ b/tests/filtering/test_require_maximum_number_of_peaks.py
@@ -0,0 +1,18 @@
+import numpy as np
+from matchms.filtering.peak_processing.require_maximum_number_of_peaks import \
+    require_maximum_number_of_peaks
+from matchms.Spectrum import Spectrum
+
+
+def test_require_number_of_peaks_below_maximum():
+    mz = np.array([10, 20, 30, 40], dtype="float")
+    intensities = np.array([0, 1, 10, 100], dtype="float")
+    spectrum = Spectrum(mz, intensities)
+    assert require_maximum_number_of_peaks(spectrum, 3) is None
+
+
+def test_require_number_of_peaks_below_maximum_not_removed():
+    mz = np.array([10, 20, 30, 40], dtype="float")
+    intensities = np.array([0, 1, 10, 100], dtype="float")
+    spectrum = Spectrum(mz, intensities)
+    assert require_maximum_number_of_peaks(spectrum, 10) == spectrum