Repository: matchms/matchms
Pull request #142 (2020-09-01 10:02:46)

Add scores_by_reference and scores_by_query to Scores
implemented: 
- ``Scores.scores_by_reference()``
- ``Scores.scores_by_query()``

florian-huber: The weighing is now added in PR #165 .
I haven't decided on the normalization of weights yet. For the Spec2Vec similarity score it will not make a difference since that is a cosine score (and hence only cares about the angle between spectrum vectors, not their length).
jspaaks: But the angle changes with length of the elements of the vector, right? For a 2 dimensional vector ``[1,10]``the angle will be different than for ``[1,100]``?

florian-huber: In the spectrum vector the words do not represent separate dimensions as in your example. It is rather that every word is placed somewhere in the _size_-dimensional space according to word2vec. The spectrum is then the sum of all word vectors. 
To account for the fact that some words are more important than others, we weigh them based on their peak intensity, a bit like this: ``spectrum_vector = sum([weights[i]* word_vectors[i] for i in range(number_words)])``.

If I change the weights to a sum of 1, I would only rescale all word vectors by ``/np.sum(weights)``, so it is more like going from ``[1,10]`` to ``[2,20]``.

By the way: In the original code we took the **sum** of all weighted word vectors. In the current implementation it is the **mean**. 

florian-huber: The weighing of words by peak intensity was added as part of #183 .

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d675c629..aab104c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Four new peak filtering functions [#119](https://github.com/matchms/matchms/pull/119)
+- score_by_reference and score_by_query methods to Scores [#142](https://github.com/matchms/matchms/pull/142)
 
 ## [0.5.2] - 2020-08-26
 
diff --git a/matchms/Scores.py b/matchms/Scores.py
index fdc38825..6b7dcec7 100644
--- a/matchms/Scores.py
+++ b/matchms/Scores.py
@@ -121,6 +121,78 @@ class Scores:
         self._scores = self.similarity_function(self.references[:, 0], self.queries[0, :])
         return self
 
+    def scores_by_reference(self, reference: ReferencesType) -> numpy.ndarray:
+        """Return all scores (not sorted) for the given reference spectrum.
+
+        Parameters
+        ----------
+        reference
+            Single reference Spectrum.
+        """
+        assert reference in self.references, "Given input not found in references."
+        selected_idx = int(numpy.where(self.references[:, 0] == reference)[0])
+        selected_scores = []
+        for i, score in enumerate(self._scores[selected_idx, :].copy()):
+            if isinstance(score, tuple):
+                selected_scores.append((self.queries[0, i], *score))
+            else:
+                selected_scores.append((self.queries[0, i], score))
+        return selected_scores
+
+    def scores_by_query(self, query: QueriesType) -> numpy.ndarray:
+        """Return all scores (not sorted) for the given query spectrum.
+
+        Parameters
+        ----------
+        query
+            Single query Spectrum.
+
+
+        For example
+
+        .. testcode::
+
+            import numpy as np
+            from matchms import Scores, Spectrum
+            from matchms.similarity import CosineGreedy
+
+            spectrum_1 = Spectrum(mz=np.array([100, 150, 200.]),
+                                  intensities=np.array([0.7, 0.2, 0.1]),
+                                  metadata={'id': 'spectrum1'})
+            spectrum_2 = Spectrum(mz=np.array([100, 140, 190.]),
+                                  intensities=np.array([0.4, 0.2, 0.1]),
+                                  metadata={'id': 'spectrum2'})
+            spectrum_3 = Spectrum(mz=np.array([110, 140, 195.]),
+                                  intensities=np.array([0.6, 0.2, 0.1]),
+                                  metadata={'id': 'spectrum3'})
+            spectrum_4 = Spectrum(mz=np.array([100, 150, 200.]),
+                                  intensities=np.array([0.6, 0.1, 0.6]),
+                                  metadata={'id': 'spectrum4'})
+            references = [spectrum_1, spectrum_2, spectrum_3]
+            queries = [spectrum_2, spectrum_3, spectrum_4]
+
+            scores = Scores(references, queries, CosineGreedy()).calculate()
+            selected_scores = scores.scores_by_query(spectrum_4)
+            selected_scores.sort(key=lambda s: s[1], reverse=True)
+            print([x[1].round(3) for x in selected_scores])
+
+        Should output
+
+        .. testoutput::
+
+            [0.796, 0.613, 0.0]
+
+        """
+        assert query in self.queries, "Given input not found in queries."
+        selected_idx = int(numpy.where(self.queries[0, :] == query)[0])
+        selected_scores = []
+        for i, score in enumerate(self._scores[:, selected_idx].copy()):
+            if isinstance(score, tuple):
+                selected_scores.append((self.references[i, 0], *score))
+            else:
+                selected_scores.append((self.references[i, 0], score))
+        return selected_scores
+
     @property
     def scores(self) -> numpy.ndarray:
         """Scores as numpy array


Test patch:
diff --git a/tests/test_scores.py b/tests/test_scores.py
index 67195bb3..aff6748b 100644
--- a/tests/test_scores.py
+++ b/tests/test_scores.py
@@ -1,6 +1,9 @@
 import numpy
 import pytest
 from matchms import Scores
+from matchms import Spectrum
+from matchms.similarity import CosineGreedy
+from matchms.similarity import IntersectMz
 
 
 class DummySimilarityFunction:
@@ -130,3 +133,99 @@ def test_scores_next():
         ("rrr", "qq", "rrrqq", 5)
     ]
     assert actual == expected
+
+
+def test_scores_by_referencey():
+    "Test scores_by_reference method."
+    spectrum_1 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.7, 0.2, 0.1]),
+                          metadata={'id': 'spectrum1'})
+    spectrum_2 = Spectrum(mz=numpy.array([100, 140, 190.]),
+                          intensities=numpy.array([0.4, 0.2, 0.1]),
+                          metadata={'id': 'spectrum2'})
+    spectrum_3 = Spectrum(mz=numpy.array([110, 140, 195.]),
+                          intensities=numpy.array([0.6, 0.2, 0.1]),
+                          metadata={'id': 'spectrum3'})
+    spectrum_4 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.6, 0.1, 0.6]),
+                          metadata={'id': 'spectrum4'})
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_3, spectrum_4]
+
+    scores = Scores(references, queries, CosineGreedy()).calculate()
+    selected_scores = scores.scores_by_reference(spectrum_2)
+
+    expected_result = [(scores.queries[:, i][0], *scores.scores[1, i]) for i in range(2)]
+    assert selected_scores == expected_result, "Expected different scores."
+
+
+def test_scores_by_referencey_non_tuple_score():
+    "Test scores_by_reference method."
+    spectrum_1 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.7, 0.2, 0.1]),
+                          metadata={'id': 'spectrum1'})
+    spectrum_2 = Spectrum(mz=numpy.array([100, 140, 190.]),
+                          intensities=numpy.array([0.4, 0.2, 0.1]),
+                          metadata={'id': 'spectrum2'})
+    spectrum_3 = Spectrum(mz=numpy.array([110, 140, 195.]),
+                          intensities=numpy.array([0.6, 0.2, 0.1]),
+                          metadata={'id': 'spectrum3'})
+    spectrum_4 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.6, 0.1, 0.6]),
+                          metadata={'id': 'spectrum4'})
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_3, spectrum_4]
+
+    scores = Scores(references, queries, IntersectMz()).calculate()
+    selected_scores = scores.scores_by_reference(spectrum_2)
+
+    expected_result = [(scores.queries[:, i][0], scores.scores[1, i]) for i in range(2)]
+    assert selected_scores == expected_result, "Expected different scores."
+
+
+def test_scores_by_query():
+    "Test scores_by_query method."
+    spectrum_1 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.7, 0.2, 0.1]),
+                          metadata={'id': 'spectrum1'})
+    spectrum_2 = Spectrum(mz=numpy.array([100, 140, 190.]),
+                          intensities=numpy.array([0.4, 0.2, 0.1]),
+                          metadata={'id': 'spectrum2'})
+    spectrum_3 = Spectrum(mz=numpy.array([110, 140, 195.]),
+                          intensities=numpy.array([0.6, 0.2, 0.1]),
+                          metadata={'id': 'spectrum3'})
+    spectrum_4 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.6, 0.1, 0.6]),
+                          metadata={'id': 'spectrum4'})
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_2, spectrum_3, spectrum_4]
+
+    scores = Scores(references, queries, CosineGreedy()).calculate()
+    selected_scores = scores.scores_by_query(spectrum_4)
+
+    expected_result = [(scores.references[i][0], *scores.scores[i, 2]) for i in range(3)]
+    assert selected_scores == expected_result, "Expected different scores."
+
+
+def test_scores_by_query_non_tuple_score():
+    "Test scores_by_query method."
+    spectrum_1 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.7, 0.2, 0.1]),
+                          metadata={'id': 'spectrum1'})
+    spectrum_2 = Spectrum(mz=numpy.array([100, 140, 190.]),
+                          intensities=numpy.array([0.4, 0.2, 0.1]),
+                          metadata={'id': 'spectrum2'})
+    spectrum_3 = Spectrum(mz=numpy.array([110, 140, 195.]),
+                          intensities=numpy.array([0.6, 0.2, 0.1]),
+                          metadata={'id': 'spectrum3'})
+    spectrum_4 = Spectrum(mz=numpy.array([100, 150, 200.]),
+                          intensities=numpy.array([0.6, 0.1, 0.6]),
+                          metadata={'id': 'spectrum4'})
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_2, spectrum_3, spectrum_4]
+
+    scores = Scores(references, queries, IntersectMz()).calculate()
+    selected_scores = scores.scores_by_query(spectrum_4)
+
+    expected_result = [(scores.references[i][0], scores.scores[i, 2]) for i in range(3)]
+    assert selected_scores == expected_result, "Expected different scores."