Repository: matchms/matchms
Pull request #526 (2023-09-07 13:43:48)

Fix adduct missing charge
Still many adducts are not recognized since they do not have a charge e.g. [M+H+Na] would not be used, since the charge is unknown. Before some of these were repaired, because we stored common adduct types. In addition now the charge stored in spectrum.get("charge") is used to repair an adduct that misses the overall charge. 


Patch:
diff --git a/matchms/Fragments.py b/matchms/Fragments.py
index 52787605..41801dae 100644
--- a/matchms/Fragments.py
+++ b/matchms/Fragments.py
@@ -45,8 +45,6 @@ class Fragments:
         assert self._is_sorted(), "mz values are out of order."
 
     def __eq__(self, other):
-        if other is None:
-            return False
         return \
             self.mz.shape == other.mz.shape and \
             np.allclose(self.mz, other.mz) and \
diff --git a/matchms/data/known_adduct_conversions.csv b/matchms/data/known_adduct_conversions.csv
index e0df6923..423683f2 100644
--- a/matchms/data/known_adduct_conversions.csv
+++ b/matchms/data/known_adduct_conversions.csv
@@ -5,6 +5,7 @@ input_adduct,corrected_adduct
 [M-2H2O+H],[M-2H2O+H]+
 [M+H-2H2O],[M-2H2O+H]+
 [M+H-2H2O]+,[M-2H2O+H]+
+[M-H+2Na],[M-H+2Na]+
 [M+H+Na],[M+H+Na]2+
 [M+H-NH3],[M+H-NH3]+
 [M-H+2Na]+,[M+2Na-H]+
@@ -16,6 +17,7 @@ input_adduct,corrected_adduct
 [M+K],[M+K]+
 [2M+H],[2M+H]+
 [2M+Na],[2M+Na]+
+[M+2Na],[M+2Na]2+
 [2M+K],[2M+K]+
 [M+NH4],[M+NH4]+
 [M-H],[M-H]-
@@ -23,12 +25,15 @@ input_adduct,corrected_adduct
 [M-H-H2O]-,[M-H2O-H]-
 [M-H-H2O],[M-H2O-H]-
 [M+H-CH3NH2],[M+H-CH3NH2]+
+[2M-2H+Na],[2M-2H+Na]-
+[M+HCOO],[M+HCOO]-
 [M+Cl],[M+Cl]-
 [M+Br],[M+Br]-
 [3M-H],[3M-H]-
 [2M-H],[2M-H]-
 [M+CH3COO],[M+CH3COO]-
 [M+FA-H],[M+FA-H]-
+[2M+FA-H],[2M+FA-H]-
 [M+OAc],[M+OAc]-
 [M+Oac],[M+OAc]-
 [M+Oac]-,[M+OAc]-
@@ -38,3 +43,5 @@ input_adduct,corrected_adduct
 [M-H]-/[M-Ser],[M-H]-
 [M+CH3COOH-H],[M+CH3COO]-
 [M-H1],[M-H]-
+[M-e],[M-e]-
+[M-2H],[M-2H]2-
diff --git a/matchms/filtering/SpectrumProcessor.py b/matchms/filtering/SpectrumProcessor.py
index f504257c..f0a9ed1f 100644
--- a/matchms/filtering/SpectrumProcessor.py
+++ b/matchms/filtering/SpectrumProcessor.py
@@ -2,7 +2,7 @@ import inspect
 import logging
 from collections import defaultdict
 from functools import partial
-from typing import Callable, Dict, Optional, Tuple, Union
+from typing import Dict, Optional, Tuple, Union
 import numpy as np
 import pandas as pd
 from tqdm import tqdm
@@ -39,8 +39,7 @@ class SpectrumProcessor:
             for filter_name in PREDEFINED_PIPELINES[predefined_pipeline]:
                 self.add_matchms_filter(filter_name)
 
-    def add_filter(self,
-                   filter_function: Union[Tuple[str, Dict[str, any]], str, Tuple[Callable, Dict[str, any]], Callable]):
+    def add_filter(self, filter_function: Union[Tuple[str, Dict[str, any]], str]):
         """Add a filter to the processing pipeline. Takes both matchms filter names (and parameters)
         as well as custom-made functions.
         """
@@ -89,7 +88,7 @@ class SpectrumProcessor:
         # Sort filters according to their order in self.filter_order
         self.filters.sort(key=lambda f: self.filter_order.index(f.__name__))
 
-    def add_custom_filter(self, filter_function: Union[Tuple[Callable, Dict[str, any]], Callable], filter_params=None, filter_position=None):
+    def add_custom_filter(self, filter_function, filter_params=None, filter_position: Optional[int] = None):
         """
         Add a custom filter function to the processing pipeline.
 
@@ -254,9 +253,8 @@ class ProcessingReport:
     """Class to keep track of spectrum changes during filtering.
     """
     def __init__(self):
-        self.counter_changed_metadata = defaultdict(int)
+        self.counter_changed_spectrum = defaultdict(int)
         self.counter_removed_spectrums = defaultdict(int)
-        self.counter_changed_peaks = defaultdict(int)
         self.counter_number_processed = 0
 
     def add_to_report(self, spectrum_old, spectrum_new: Spectrum,
@@ -266,23 +264,18 @@ class ProcessingReport:
         if spectrum_new is None:
             self.counter_removed_spectrums[filter_function_name] += 1
         else:
-            # Add metadata changes
-            if spectrum_new.metadata != spectrum_old.metadata:
-                self.counter_changed_metadata[filter_function_name] += 1
-            # Add peak changes
-            if spectrum_new.peaks != spectrum_old.peaks or spectrum_new.losses != spectrum_old.losses:
-                self.counter_changed_peaks[filter_function_name] += 1
+            for field, value in spectrum_new.metadata.items():
+                if objects_differ(spectrum_old.get(field), value):
+                    self.counter_changed_spectrum[filter_function_name] += 1
+                    break
 
     def to_dataframe(self):
         """Create Pandas DataFrame Report of counted spectrum changes."""
-        metadata_changed = pd.DataFrame(self.counter_changed_metadata.items(),
-                                        columns=["filter", "changed metadata"])
+        changes = pd.DataFrame(self.counter_changed_spectrum.items(),
+                               columns=["filter", "changed spectra"])
         removed = pd.DataFrame(self.counter_removed_spectrums.items(),
                                columns=["filter", "removed spectra"])
-        peaks_changed = pd.DataFrame(self.counter_changed_peaks.items(),
-                                     columns=["filter", "changed mass spectrum"])
-        processing_report = pd.merge(removed, metadata_changed, how="outer", on="filter")
-        processing_report = pd.merge(processing_report, peaks_changed, how="outer", on="filter")
+        processing_report = pd.merge(removed, changes, how="outer", on="filter")
 
         processing_report = processing_report.set_index("filter").fillna(0)
         return processing_report.astype(int)
@@ -300,9 +293,7 @@ Changes during processing:
     def __repr__(self):
         return f"Report({self.counter_number_processed},\
         {self.counter_removed_spectrums},\
-        {dict(self.counter_removed_spectrums)},\
-        {dict(self.counter_changed_metadata)},\
-        {dict(self.counter_changed_peaks)})"
+        {dict(self.counter_changed_spectrum)})"
 
 
 def objects_differ(obj1, obj2):
diff --git a/matchms/filtering/filter_utils/interpret_unknown_adduct.py b/matchms/filtering/filter_utils/interpret_unknown_adduct.py
index b385b095..f28085d6 100644
--- a/matchms/filtering/filter_utils/interpret_unknown_adduct.py
+++ b/matchms/filtering/filter_utils/interpret_unknown_adduct.py
@@ -157,6 +157,7 @@ def get_charge_of_adduct(adduct) -> Optional[int]:
         return None
     charge = charge[0]
     if len(charge) == 1:
+        # The charge is + or -
         charge_size = "1"
         charge_sign = charge
     elif len(charge) == 2:
diff --git a/matchms/filtering/metadata_processing/clean_adduct.py b/matchms/filtering/metadata_processing/clean_adduct.py
index 57c6ca5c..cca4fdec 100644
--- a/matchms/filtering/metadata_processing/clean_adduct.py
+++ b/matchms/filtering/metadata_processing/clean_adduct.py
@@ -1,5 +1,7 @@
 import logging
 import re
+from matchms.filtering.filter_utils.interpret_unknown_adduct import \
+    get_charge_of_adduct
 from matchms.filtering.filter_utils.load_known_adducts import \
     load_known_adduct_conversions
 
@@ -18,16 +20,32 @@ def clean_adduct(spectrum_in):
     """
     if spectrum_in is None:
         return None
+    adduct = spectrum_in.get("adduct")
+    if adduct is None:
+        return spectrum_in
     spectrum = spectrum_in.clone()
-    adduct = spectrum.get("adduct")
-    cleaned_adduct = _clean_adduct(adduct)
+
+    cleaned_adduct = _clean_adduct(adduct, spectrum.get('charge'))
+
+    if spectrum.get("charge"):
+        if spectrum.get("charge") != get_charge_of_adduct(cleaned_adduct):
+            logger.warning("The charge in the adduct: %s and the given charge: %s do not match",
+                           adduct, spectrum.get("charge"))
+    else:
+        # set charge to adduct
+        charge_from_adduct = get_charge_of_adduct(cleaned_adduct)
+        if charge_from_adduct:
+            logger.info("Unknown charge was derived from adduct: %s, now charge is %s",
+                        cleaned_adduct, charge_from_adduct)
+            spectrum.set("charge", charge_from_adduct)
+
     if adduct != cleaned_adduct:
         spectrum.set("adduct", cleaned_adduct)
         logger.info("The adduct %d was set to %s", adduct, cleaned_adduct)
     return spectrum
 
 
-def _clean_adduct(adduct: str) -> str:
+def _clean_adduct(adduct: str, charge=None) -> str:
     """Clean adduct and make it consistent in style.
     Will transform adduct strings of type 'M+H+' to '[M+H]+'.
 
@@ -36,40 +54,74 @@ def _clean_adduct(adduct: str) -> str:
     adduct
         Input adduct string to be cleaned/edited.
     """
+    if not isinstance(adduct, str):
+        return adduct
+
+    adduct = adduct.strip().replace("\n", "").replace("*", "")
+    adduct = adduct.replace("++", "2+").replace("--", "2-")
+
+    if not adduct.startswith("["):
+        adduct = _add_missing_brackets_to_adduct(adduct)
+    if adduct.endswith("]"):
+        charge = _convert_int_charge_to_str(charge)
+        if charge:
+            adduct += charge
+    return _convert_known_adduct(adduct)
+
+
+def _add_missing_brackets_to_adduct(adduct):
+    """Adds missing brackets to an adduct and moves the charge outside. """
     def _get_adduct_charge(adduct):
+        # Remove parts that can confuse the charge extraction. Because they end with a number and the ] is missing.
+        for mol_part in ["CH2", "CH3", "NH3", "NH4", "O2"]:
+            if mol_part in adduct:
+                adduct = adduct.split(mol_part)[-1]
         regex_charges = r"[1-3]{0,1}[+-]{1,2}$"
         match = re.search(regex_charges, adduct)
         if match:
             return match.group(0)
         return match
 
-    def _adduct_conversion(adduct):
-        """Convert adduct if conversion rule is known"""
-        adduct_conversions = load_known_adduct_conversions()
-        if adduct in adduct_conversions:
-            return adduct_conversions[adduct]
+    if not adduct.startswith("["):
+        adduct = "[" + adduct
+    if adduct.endswith("]"):
         return adduct
 
-    if not isinstance(adduct, str):
-        return adduct
+    adduct_charge = _get_adduct_charge(adduct)
 
-    adduct = adduct.strip().replace("\n", "").replace("*", "")
-    adduct = adduct.replace("++", "2+").replace("--", "2-")
-    if adduct.startswith("["):
-        return _adduct_conversion(adduct)
+    if adduct_charge is None:
+        return adduct + "]"
+    return adduct[:-len(adduct_charge)] + "]" + adduct_charge
 
-    if adduct.endswith("]"):
-        return _adduct_conversion("[" + adduct)
 
-    adduct_core = "[" + adduct
-    # Remove parts that can confuse the charge extraction
-    for mol_part in ["CH2", "CH3", "NH3", "NH4", "O2"]:
-        if mol_part in adduct:
-            adduct = adduct.split(mol_part)[-1]
-    adduct_charge = _get_adduct_charge(adduct)
+def _convert_int_charge_to_str(charge):
+    """Converts integer to str format of charge
+
+    e.g.:
+    1 -> +
+    -1 -> -
+    2 -> 2+
+    -2 -> 2-
+    """
+    if charge is None:
+        return None
+    if not isinstance(charge, int):
+        logger.warning("Charge is not given as int. Apply 'make_charge_int' filter first.")
+        return None
+    if charge == 0:
+        return None
+    if charge < 0:
+        sign = "-"
+    else:
+        sign = "+"
+    if charge in (-1, 1):
+        return sign
+    return str(abs(charge)) + sign
 
-    if adduct_charge is None:
-        return _adduct_conversion(adduct_core + "]")
 
-    adduct_cleaned = adduct_core[:-len(adduct_charge)] + "]" + adduct_charge
-    return _adduct_conversion(adduct_cleaned)
+def _convert_known_adduct(adduct):
+    """Convert adduct if conversion rule is known"""
+    adduct_conversions = load_known_adduct_conversions()
+    if adduct in adduct_conversions:
+        return adduct_conversions[adduct]
+    return adduct
diff --git a/matchms/filtering/metadata_processing/derive_ionmode.py b/matchms/filtering/metadata_processing/derive_ionmode.py
index 11bb15a1..87f6c661 100644
--- a/matchms/filtering/metadata_processing/derive_ionmode.py
+++ b/matchms/filtering/metadata_processing/derive_ionmode.py
@@ -29,27 +29,23 @@ def derive_ionmode(spectrum_in: SpectrumType) -> SpectrumType:
 
     spectrum = spectrum_in.clone()
 
-    # Load lists of known adducts
-    known_adducts = load_known_adducts()
+    ionmode = spectrum.get("ionmode")
+    if ionmode in ["positive", "negative"]:
+        return spectrum
 
     adduct = spectrum.get("adduct", None)
     # Harmonize adduct string
     if adduct:
         adduct = _clean_adduct(adduct)
 
-    ionmode = spectrum.get("ionmode")
-    if ionmode:
-        assert ionmode == ionmode.lower(), ("Ionmode field not harmonized.",
-                                            "Apply 'make_ionmode_lowercase' filter first.")
-    if ionmode in ["positive", "negative"]:
-        return spectrum
+    # Load lists of known adducts
+    known_adducts = load_known_adducts()
     # Try completing missing or incorrect ionmodes
     if adduct in list(known_adducts["adduct"]):
         ionmode = known_adducts.loc[known_adducts["adduct"] == adduct, "ionmode"].values[0]
     else:
-        ionmode = "n/a"
+        ionmode = None
 
     spectrum.set("ionmode", ionmode)
     logger.info("Set ionmode to %s.", ionmode)
-
     return spectrum


Test patch:
diff --git a/tests/filtering/test_all_filter_order.py b/tests/filtering/test_all_filter_order.py
index f8d76ba9..d41df810 100644
--- a/tests/filtering/test_all_filter_order.py
+++ b/tests/filtering/test_all_filter_order.py
@@ -33,6 +33,8 @@ ANNOTATION_REPARATIONS = [msfilters.repair_inchi_inchikey_smiles, msfilters.deri
     # The adduct filter removes all occurances while derive formula from name only removes when it is at the end
     # of compound name. Removing adducts therefore has to happen first.
     [[msfilters.derive_adduct_from_name], [msfilters.derive_formula_from_name]],
+    [[msfilters.make_charge_int, msfilters.correct_charge, ], [msfilters.clean_adduct]],
+    [[msfilters.derive_adduct_from_name, ], [msfilters.clean_adduct]]
 ])
 def test_all_filter_order(early_filters: List[Callable], later_filters: List[Callable]):
     """Tests if early_filter is run before later_filter"""
diff --git a/tests/filtering/test_derive_adduct_from_name.py b/tests/filtering/test_derive_adduct_from_name.py
index 1ef49223..d6934682 100644
--- a/tests/filtering/test_derive_adduct_from_name.py
+++ b/tests/filtering/test_derive_adduct_from_name.py
@@ -17,7 +17,7 @@ from ..builder_Spectrum import SpectrumBuilder
     [{"compound_name": "peptideXYZ [M+H+K] C16H12"}, True, "[M+H+K]", "peptideXYZ C16H12", "[M+H+K]"],
     [{"name": ""}, True, None, "", None]
 ])
-def test_derive_adduct_from_name_parametrized(metadata, remove_adduct_from_name,
+def test_derive_adduct_from_name_parametrized(metadata: dict, remove_adduct_from_name: bool,
                                               expected_adduct, expected_name, removed_adduct):
     set_matchms_logger_level("INFO")
     spectrum_in = SpectrumBuilder().with_metadata(metadata).build()
@@ -49,7 +49,6 @@ def test_derive_adduct_from_name_parametrized(metadata, remove_adduct_from_name,
 ])
 def test_derive_adduct_from_name_multiple_adducts_in_name(metadata,
                                                           expected_adduct, expected_name):
-    set_matchms_logger_level("INFO")
     spectrum_in = SpectrumBuilder().with_metadata(metadata).build()
 
     spectrum = derive_adduct_from_name(spectrum_in, remove_adduct_from_name=True)
diff --git a/tests/filtering/test_repair_adduct/test_clean_adduct.py b/tests/filtering/test_repair_adduct/test_clean_adduct.py
index 672b789c..94861f4a 100644
--- a/tests/filtering/test_repair_adduct/test_clean_adduct.py
+++ b/tests/filtering/test_repair_adduct/test_clean_adduct.py
@@ -1,5 +1,6 @@
 import pytest
-from matchms.filtering.metadata_processing.clean_adduct import clean_adduct
+from matchms.filtering.metadata_processing.clean_adduct import (
+    _add_missing_brackets_to_adduct, _convert_int_charge_to_str, clean_adduct)
 from tests.builder_Spectrum import SpectrumBuilder
 
 
@@ -18,3 +19,45 @@ def test_clean_adduct(input_adduct, expected_adduct):
     spectrum_in = SpectrumBuilder().with_metadata({"adduct": input_adduct}).build()
     spectrum_out = clean_adduct(spectrum_in)
     assert spectrum_out.get("adduct") == expected_adduct
+
+
+@pytest.mark.parametrize("input_charge, expected_charge",
+                         [(1, "+"),
+                          (2, "2+"),
+                          (-1, "-"),
+                          (+2, "2+"),
+                          (-2, "2-"),
+                          (None, None),
+                          ("None", None),
+                          ])
+def test_convert_int_charge_to_str(input_charge, expected_charge):
+    assert _convert_int_charge_to_str(input_charge) == expected_charge
+
+
+@pytest.mark.parametrize("input_adduct, expected_adduct",
+                         [("M+", "[M]+"),
+                          ("M+CH3COO-", "[M+CH3COO]-"),
+                          ("M+CH3COO", "[M+CH3COO]"),
+                          ("M-CH3-", "[M-CH3]-"),
+                          ("[2M+Na]", "[2M+Na]"),
+                          ("2M+Na", "[2M+Na]"),
+                          ("M+NH3+", "[M+NH3]+"),
+                          ("M-H2O+2H2+", "[M-H2O+2H]2+")])
+def test_add_missing_brackets_to_adduct(input_adduct, expected_adduct):
+    assert _add_missing_brackets_to_adduct(input_adduct) == expected_adduct
+
+
+@pytest.mark.parametrize("input_adduct, charge, expected_adduct, expected_charge",
+                         [("M", 1, "[M]+", 1),
+                          ("M", -1, "[M]-", -1),
+                          ("[M]+", None, "[M]+", 1),
+                          ("M+", None, "[M]+", 1),
+                          ("M-H2O+2H2-", None, "[M-H2O+2H]2-", -2),
+                          ("M-H2O+2H2+", None, "[M-H2O+2H]2+", 2),
+                          (None, 1, None, 1)])
+def test_clean_adduct_with_charge(input_adduct, charge, expected_adduct, expected_charge):
+    spectrum_in = SpectrumBuilder().with_metadata({"adduct": input_adduct,
+                                                   "charge": charge}).build()
+    spectrum_out = clean_adduct(spectrum_in)
+    assert spectrum_out.get("adduct") == expected_adduct
+    assert spectrum_out.get("charge") == expected_charge
diff --git a/tests/filtering/test_spectrum_processor.py b/tests/filtering/test_spectrum_processor.py
index 354538a3..26250acb 100644
--- a/tests/filtering/test_spectrum_processor.py
+++ b/tests/filtering/test_spectrum_processor.py
@@ -8,15 +8,16 @@ from ..builder_Spectrum import SpectrumBuilder
 
 @pytest.fixture
 def spectrums():
-    s1 = SpectrumBuilder().\
-        with_metadata({"charge": "+1", "pepmass": 100}).\
-        with_mz([10, 20, 30]).with_intensities([0.1, 0.4, 10]).build()
-    s2 = SpectrumBuilder().with_metadata({"charge": "-1",
-                                          "pepmass": 102}).\
-        with_mz([10, 20, 30]).with_intensities([0.1, 0.2, 1]).build()
-    s3 = SpectrumBuilder().with_metadata({"charge": -1,
-                                          "pepmass": 104}).\
-        with_mz([10, ]).with_intensities([0.1, ]).build()
+    metadata1 = {"charge": "+1",
+                 "pepmass": 100}
+    metadata2 = {"charge": "-1",
+                 "pepmass": 102}
+    metadata3 = {"charge": -1,
+                 "pepmass": 104}
+
+    s1 = SpectrumBuilder().with_metadata(metadata1).build()
+    s2 = SpectrumBuilder().with_metadata(metadata2).build()
+    s3 = SpectrumBuilder().with_metadata(metadata3).build()
     return [s1, s2, s3]
 
 
@@ -137,20 +138,18 @@ def test_filter_spectrums(spectrums):
 
 def test_filter_spectrums_report(spectrums):
     processor = SpectrumProcessor("minimal")
-    processor.add_filter(filter_function=("require_minimum_number_of_peaks", {"n_required": 2}))
-    processor.add_filter(filter_function="add_losses")
     spectrums, report = processor.process_spectrums(spectrums, create_report=True)
-    assert len(spectrums) == 2
+    assert len(spectrums) == 3
     actual_masses = [s.get("precursor_mz") for s in spectrums]
-    expected_masses = [100, 102]
+    expected_masses = [100, 102, 104]
     assert actual_masses == expected_masses
     assert report.counter_number_processed == 3
-    assert report.counter_changed_metadata == {'make_charge_int': 2, 'interpret_pepmass': 3, 'derive_ionmode': 3}
+    assert report.counter_changed_spectrum == {'make_charge_int': 2, 'interpret_pepmass': 3, 'derive_ionmode': 3}
     report_df = report.to_dataframe()
-    assert np.all(report_df.loc[["require_minimum_number_of_peaks", "interpret_pepmass", "add_losses"]].values == np.array(
-        [[1, 0, 0],
-         [0, 3, 0],
-         [0, 0, 2]]))
+    assert np.all(report_df.loc[["make_charge_int", "interpret_pepmass", "derive_ionmode"]].values == np.array(
+        [[0, 2],
+         [0, 3],
+         [0, 3]]))
 
 
 def test_processing_report_class(spectrums):
@@ -161,7 +160,7 @@ def test_processing_report_class(spectrums):
         processing_report.add_to_report(s, spectrum_processed, "test_filter")
 
     assert not processing_report.counter_removed_spectrums
-    assert processing_report.counter_changed_metadata == {"test_filter": 3}
+    assert processing_report.counter_changed_spectrum == {"test_filter": 3}
 
 
 def test_adding_custom_filter(spectrums):
@@ -176,7 +175,7 @@ def test_adding_custom_filter(spectrums):
     assert filters[-1].__name__ == "nonsense_inchikey"
     spectrums, report = processor.process_spectrums(spectrums, create_report=True)
     assert report.counter_number_processed == 3
-    assert report.counter_changed_metadata == {'make_charge_int': 2, 'interpret_pepmass': 3,
+    assert report.counter_changed_spectrum == {'make_charge_int': 2, 'interpret_pepmass': 3,
                                                'derive_ionmode': 3, 'nonsense_inchikey': 3}
     assert spectrums[0].get("inchikey") == "NONSENSE", "Custom filter not executed properly"
 
@@ -193,7 +192,7 @@ def test_adding_custom_filter_with_parameters(spectrums):
     assert filters[-1].__name__ == "nonsense_inchikey_multiple"
     spectrums, report = processor.process_spectrums(spectrums, create_report=True)
     assert report.counter_number_processed == 3
-    assert report.counter_changed_metadata == {'make_charge_int': 2, 'interpret_pepmass': 3,
+    assert report.counter_changed_spectrum == {'make_charge_int': 2, 'interpret_pepmass': 3,
                                                'derive_ionmode': 3, 'nonsense_inchikey_multiple': 3}
     assert spectrums[0].get("inchikey") == "NONSENSENONSENSE", "Custom filter not executed properly"