Skip to content

Commit

Permalink
Merge pull request #71 from wilhelm-lab/release/0.4.2
Browse files Browse the repository at this point in the history
Release/0.4.2
  • Loading branch information
picciama authored Aug 3, 2023
2 parents 8a7a9ca + 62b78b2 commit 57135ee
Show file tree
Hide file tree
Showing 13 changed files with 496 additions and 446 deletions.
2 changes: 1 addition & 1 deletion .cookietemple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ full_name: Victor Giurcoiu
email: [email protected]
project_name: spectrum_fundamentals
project_short_description: Fundamentals public repo
version: 0.4.1
version: 0.4.2
license: MIT
4 changes: 2 additions & 2 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name-template: "0.4.1 🌈" # <<COOKIETEMPLE_FORCE_BUMP>>
tag-template: 0.4.1 # <<COOKIETEMPLE_FORCE_BUMP>>
name-template: "0.4.2 🌈" # <<COOKIETEMPLE_FORCE_BUMP>>
tag-template: 0.4.2 # <<COOKIETEMPLE_FORCE_BUMP>>
exclude-labels:
- "skip-changelog"

Expand Down
2 changes: 1 addition & 1 deletion cookietemple.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.4.1
current_version = 0.4.2

[bumpversion_files_whitelisted]
init_file = spectrum_fundamentals/__init__.py
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
# the built documents.
#
# The short X.Y version.
version = "0.4.1"
version = "0.4.2"
# The full version, including alpha/beta/rc tags.
release = "0.4.1"
release = "0.4.2"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
824 changes: 401 additions & 423 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "spectrum_fundamentals"
version = "0.4.1" # <<COOKIETEMPLE_FORCE_BUMP>>
version = "0.4.2" # <<COOKIETEMPLE_FORCE_BUMP>>
description = "Fundamentals public repo"
authors = ["WassimG <[email protected]>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion spectrum_fundamentals/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Initialize fundamentals."""
__author__ = "Victor Giurcoiu"
__email__ = "[email protected]"
__version__ = "0.4.1"
__version__ = "0.4.2"

import logging
import logging.handlers
Expand Down
2 changes: 1 addition & 1 deletion spectrum_fundamentals/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


@click.command()
@click.version_option(version="0.4.1", message=click.style("spectrum_fundamentals Version: 0.4.1"))
@click.version_option(version="0.4.2", message=click.style("spectrum_fundamentals Version: 0.4.2"))
def main() -> None:
"""spectrum_fundamentals."""

Expand Down
19 changes: 16 additions & 3 deletions spectrum_fundamentals/annotation/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ def handle_multiple_matches(
return matched_peaks_df, (original_length - length_after_matches)


def annotate_spectra(un_annot_spectra: pd.DataFrame) -> pd.DataFrame:
def annotate_spectra(
un_annot_spectra: pd.DataFrame, mass_tolerance: Optional[float] = None, unit_mass_tolerance: Optional[str] = None
) -> pd.DataFrame:
"""
Annotate a set of spectra.
Expand All @@ -134,12 +136,14 @@ def annotate_spectra(un_annot_spectra: pd.DataFrame) -> pd.DataFrame:
- removed_peaks: a NumPy array containing the indices of any peaks that were removed during the annotation process
:param un_annot_spectra: a Pandas DataFrame containing the raw peaks and metadata to be annotated
:param mass_tolerance: mass tolerance to calculate min and max mass
:param unit_mass_tolerance: unit for the mass tolerance (da or ppm)
:return: a Pandas DataFrame containing the annotated spectra with meta data
"""
raw_file_annotations = []
index_columns = {col: un_annot_spectra.columns.get_loc(col) for col in un_annot_spectra.columns}
for row in un_annot_spectra.values:
results = parallel_annotate(row, index_columns)
results = parallel_annotate(row, index_columns, mass_tolerance, unit_mass_tolerance)
if not results:
continue
raw_file_annotations.append(results)
Expand Down Expand Up @@ -205,7 +209,12 @@ def generate_annotation_matrix(
return intensity, mass


def parallel_annotate(spectrum: np.ndarray, index_columns: dict) -> Optional[Tuple[np.ndarray, np.ndarray, float, int]]:
def parallel_annotate(
spectrum: np.ndarray,
index_columns: dict,
mass_tolerance: Optional[float] = None,
unit_mass_tolerance: Optional[str] = None,
) -> Optional[Tuple[np.ndarray, np.ndarray, float, int]]:
"""
Perform parallel annotation of a spectrum.
Expand All @@ -217,6 +226,8 @@ def parallel_annotate(spectrum: np.ndarray, index_columns: dict) -> Optional[Tup
:param spectrum: a np.ndarray that contains the spectrum to be annotated
:param index_columns: a dictionary that contains the index columns of the spectrum
:param mass_tolerance: mass tolerance to calculate min and max mass
:param unit_mass_tolerance: unit for the mass tolerance (da or ppm)
:return: a tuple containing intensity values (np.ndarray), masses (np.ndarray), calculated mass (float),
and any removed peaks (List[str])
"""
Expand All @@ -228,6 +239,8 @@ def parallel_annotate(spectrum: np.ndarray, index_columns: dict) -> Optional[Tup
spectrum[index_columns[mod_seq_column]],
spectrum[index_columns["MASS_ANALYZER"]],
spectrum[index_columns["PRECURSOR_CHARGE"]],
mass_tolerance,
unit_mass_tolerance,
)
if not unmod_sequence:
return None
Expand Down
37 changes: 34 additions & 3 deletions spectrum_fundamentals/fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,21 @@ def compute_peptide_mass(sequence: str) -> float:
return forward_sum + ion_type_offsets[0] + ion_type_offsets[1]


def initialize_peaks(sequence: str, mass_analyzer: str, charge: int) -> Tuple[List[dict], int, str, float]:
def initialize_peaks(
sequence: str,
mass_analyzer: str,
charge: int,
mass_tolerance: Optional[float] = None,
unit_mass_tolerance: Optional[str] = None,
) -> Tuple[List[dict], int, str, float]:
"""
Generate theoretical peaks for a modified peptide sequence.
:param sequence: Modified peptide sequence
:param mass_analyzer: Type of mass analyzer used eg. FTMS, ITMS
:param charge: Precursor charge
:param mass_tolerance: mass tolerance to calculate min and max mass
:param unit_mass_tolerance: unit for the mass tolerance (da or ppm)
:raises AssertionError: if peptide sequence contained an unknown modification. TODO do this within the get_mod func.
:return: List of theoretical peaks, Flag to indicate if there is a tmt on n-terminus, Un modified peptide sequence
"""
Expand Down Expand Up @@ -190,16 +198,39 @@ def initialize_peaks(sequence: str, mass_analyzer: str, charge: int) -> Tuple[Li
return fragments_meta_data, tmt_n_term, peptide_sequence, (forward_sum + ion_type_offsets[0] + ion_type_offsets[1])


def get_min_max_mass(mass_analyzer: str, mass: float) -> Tuple[float, float]:
def get_min_max_mass(
mass_analyzer: str, mass: float, mass_tolerance: Optional[float] = None, unit_mass_tolerance: Optional[str] = None
) -> Tuple[float, float]:
"""Helper function to get min and max mass based on mass analyzer.
If both mass_tolerance and unit_mass_tolerance are provided, the function uses the provided tolerance
to calculate the min and max mass. If either `mass_tolerance` or `unit_mass_tolerance` is missing
(or both are None), the function falls back to the default tolerances based on the `mass_analyzer`.
Default mass tolerances for different mass analyzers:
- FTMS: +/- 20 ppm
- TOF: +/- 40 ppm
- ITMS: +/- 0.35 daltons
:param mass_tolerance: mass tolerance to calculate min and max mass
:param unit_mass_tolerance: unit for the mass tolerance (da or ppm)
:param mass_analyzer: the type of mass analyzer used to determine the tolerance.
:param mass: the theoretical fragment mass
:raises ValueError: if mass_analyzer is other than one of FTMS, TOF, ITMS
:raises ValueError: if unit_mass_tolerance is other than one of ppm, da
:return: a tuple (min, max) denoting the mass tolerance range.
"""
if mass_analyzer == "FTMS":
if mass_tolerance is not None and unit_mass_tolerance is not None:
if unit_mass_tolerance == "ppm":
min_mass = (mass * -mass_tolerance / 1000000) + mass
max_mass = (mass * mass_tolerance / 1000000) + mass
elif unit_mass_tolerance == "da":
min_mass = mass - mass_tolerance
max_mass = mass + mass_tolerance
else:
raise ValueError(f"Unsupported unit for the mass tolerance: {unit_mass_tolerance}")
elif mass_analyzer == "FTMS":
min_mass = (mass * -20 / 1000000) + mass
max_mass = (mass * 20 / 1000000) + mass
elif mass_analyzer == "TOF":
Expand Down
12 changes: 5 additions & 7 deletions spectrum_fundamentals/metrics/similarity.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ def correlation(
:param method: either pearson or spearman
:return: calculated correlations
"""
obs_int_arr = observed_intensities.toarray()
pred_int_arr = predicted_intensities.toarray()
observed_intensities_array = observed_intensities.toarray()
predicted_intensities_array = predicted_intensities.toarray()

if charge != 0:
if charge == 1:
Expand All @@ -232,13 +232,11 @@ def correlation(
boolean_array = constants.Y_ION_MASK

boolean_array = scipy.sparse.csr_matrix(boolean_array)
obs_int_arr = scipy.sparse.csr_matrix(obs_int_arr)
pred_int_arr = scipy.sparse.csr_matrix(pred_int_arr)
obs_int_arr = obs_int_arr.multiply(boolean_array).toarray()
pred_int_arr = pred_int_arr.multiply(boolean_array).toarray()
observed_intensities_array = observed_intensities.multiply(boolean_array).toarray()
predicted_intensities_array = predicted_intensities.multiply(boolean_array).toarray()

pear_corr = []
for obs, pred in zip(obs_int_arr, pred_int_arr):
for obs, pred in zip(observed_intensities_array, predicted_intensities_array):
valid_ion_mask = pred > constants.EPSILON
obs = obs[valid_ion_mask]
pred = pred[valid_ion_mask]
Expand Down
4 changes: 3 additions & 1 deletion tests/unit_tests/test_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ def test_indices_to_one_hot_with_list_and_class(self):
expected_output = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])
np.testing.assert_equal(charge.indices_to_one_hot(labels, classes), expected_output)

"""
def test_indices_to_one_hot_with_wrong_input_type(self):
"""Test indices_to_one_hot correctly raises TypeError on wrong input type."""
# Test indices_to_one_hot correctly raises TypeError on wrong input type.
try:
labels = None
self.assertRaises(TypeError, charge.indices_to_one_hot, labels)
except Exception as e: # explicitly silence typeguard.TypeCheckError here without importing TypeGuard
if e.__class__.__name__ != "TypeCheckError":
raise
"""

def test_indices_to_one_hot_with_incompatible_classes(self):
"""Test indices_to_one_hot correctly raises TypeError on wrong input type."""
Expand Down
28 changes: 28 additions & 0 deletions tests/unit_tests/test_fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,31 @@ def test_compute_peptide_masses_with_invalid_mod(self):
"""Negative testing of computation of peptide mass with unknown modification in mod string."""
seq = "SEQUENC[UNIMOD:0]E"
self.assertRaises(AssertionError, fragments.compute_peptide_mass, seq)


class TestMassTolerances(unittest.TestCase):
"""Testing the mass tolerance calculations in various scenarios."""

def test_mass_tol_with_ppm(self):
"""Test get_min_max_mass with a user defined ppm measure."""
window = fragments.get_min_max_mass(
mass_analyzer="FTMS", mass=10.0, mass_tolerance=15, unit_mass_tolerance="ppm"
)
self.assertEqual(window, (9.99985, 10.00015))

def test_mass_tol_with_da(self):
"""Test get_min_max_mass with a user defined da measure."""
window = fragments.get_min_max_mass(
mass_analyzer="FTMS", mass=10.0, mass_tolerance=0.3, unit_mass_tolerance="da"
)
self.assertEqual(window, (9.7, 10.3))

def test_mass_tol_with_defaults(self):
"""Test get_min_max_mass with mass analyzer defaults."""
window_ftms = fragments.get_min_max_mass(mass_analyzer="FTMS", mass=10.0)
window_itms = fragments.get_min_max_mass(mass_analyzer="ITMS", mass=10.0)
window_tof = fragments.get_min_max_mass(mass_analyzer="TOF", mass=10.0)

self.assertEqual(window_ftms, (9.9998, 10.0002))
self.assertEqual(window_tof, (9.9996, 10.0004))
self.assertEqual(window_itms, (9.65, 10.35))

0 comments on commit 57135ee

Please sign in to comment.