From 62d40e2ab2298e33f000b9f89ab93835d146b651 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 6 Mar 2023 15:58:27 -0800 Subject: [PATCH 01/41] (wip) changed ims collapsing --- diadem/cli.py | 10 +- diadem/config.py | 148 +++-- diadem/data_io/__init__.py | 22 + diadem/{ => data_io}/mzml.py | 139 +--- diadem/data_io/timstof.py | 881 ++++++++++++++++++++++++++ diadem/data_io/utils.py | 122 ++++ diadem/deisotoping.py | 30 +- diadem/index/fragment_buckets.py | 4 +- diadem/index/indexed_db.py | 70 +- diadem/search/diadem.py | 112 +++- profiling/get_data.bash | 3 + profiling/run_profile.py | 2 +- profiling/run_profile_multithread.py | 4 +- profiling/run_profile_multithread.zsh | 11 + profiling/run_profile_tims.py | 10 + pyproject.toml | 21 +- tests/conftest.py | 3 + tests/test_database_build.py | 5 +- tests/test_database_parquet_cache.py | 4 + tests/test_database_scoring.py | 1 + tests/test_dia_search.py | 8 +- 21 files changed, 1358 insertions(+), 252 deletions(-) create mode 100644 diadem/data_io/__init__.py rename diadem/{ => data_io}/mzml.py (83%) create mode 100644 diadem/data_io/timstof.py create mode 100644 diadem/data_io/utils.py create mode 100644 profiling/run_profile_tims.py diff --git a/diadem/cli.py b/diadem/cli.py index 0c05bcb..d2ce5a9 100644 --- a/diadem/cli.py +++ b/diadem/cli.py @@ -38,8 +38,8 @@ def main_cli() -> None: @main_cli.command(help="Runs the search module of DIAdem") @click.option( - "--mzml_file", - help="mzML file to use as an input for the search", + "--data_path", + help="mzML or .d file to use as an input for the search", ) @click.option("--fasta", help="fasta file to use as an input") @click.option("--out_prefix", help="Prefix to add to all output files") @@ -47,7 +47,7 @@ def main_cli() -> None: "--mode", type=click.Choice(["dda", "dia"], case_sensitive=False), default="dia" ) @click.option("--config", help="Path to the config toml configuration file to use.") -def search(mzml_file, fasta, out_prefix, mode, config) -> None: +def search(data_path, fasta, out_prefix, mode, config) -> None: setup_logger() if config: config = DiademConfig.from_toml(config) @@ -56,11 +56,11 @@ def search(mzml_file, fasta, out_prefix, mode, config) -> None: config = DiademConfig() if mode == "dia": diadem_main( - fasta_path=fasta, mzml_path=mzml_file, config=config, out_prefix=out_prefix + fasta_path=fasta, data_path=data_path, config=config, out_prefix=out_prefix ) elif mode == "dda": dda_main( - mzml_path=mzml_file, fasta_path=fasta, config=config, out_prefix=out_prefix + mzml_path=data_path, fasta_path=fasta, config=config, out_prefix=out_prefix ) else: raise NotImplementedError diff --git a/diadem/config.py b/diadem/config.py index c146c5c..14d0c25 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -3,7 +3,7 @@ import hashlib import sys from argparse import Namespace -from dataclasses import asdict, dataclass, field +from dataclasses import asdict, dataclass, field, fields from typing import Literal import tomli_w @@ -24,9 +24,13 @@ @dataclass(frozen=True, eq=True) -class DiademConfig: # noqa - g_tolerances: tuple[float, ...] = field(default=(20, 10)) - g_tolerance_units: tuple[MassError, ...] = field(default=("ppm", "ppm")) +class DiademIndexConfig: + """Configuration to generate an index. + + Base class for the diadem index, this is mean to contain the configuration + options relevant to generate the index, and should not have parameters that + are used at runtime (index gen vs index use). + """ peptide_length_range: tuple[int, int] = field( default=(7, 25), @@ -43,7 +47,7 @@ class DiademConfig: # noqa default="by", ) ion_charges: tuple[int, ...] = field( - default=(1,), + default=(1, 2), ) ion_mz_range: tuple[float, float] = field( default=(250, 2000.0), @@ -56,50 +60,6 @@ class DiademConfig: # noqa # Variable mods # Static mods - # Main score - # Currently unused ... - scoring_score_function: ScoringFunctions = "Hyperscore" - - run_max_peaks: int = 1e6 - - # the 5k number comes from the neviskii lab paper on deisotoping - run_max_peaks_per_spec: int = 5_000 - - # Prallelism 1 means no parallelism, -1 means all cores, any other positive - # integer means use that many cores. - run_parallelism: int = -2 - run_deconvolute_spectra: bool = True - run_min_peak_intensity: float = 100 - run_debug_log_frequency: int = 20 - run_allowed_fails: int = 500 - run_window_size: int = 21 - run_max_peaks_per_window: int = 150 - - # Min intensity to consider for matching and extracting - run_min_intensity_ratio: float = 0.011 - run_min_correlation_score: float = 0.25 - - run_scaling_ratio = 0.01 - run_scalin_limits: tuple[float, float] = (0.01, 0.999) - - @property - def ms2ml_config(self) -> Config: - """Returns the ms2ml config. - - It exports all the parameters that are used inside of - ms2ml to its own configuration object. - """ - conf = Config( - g_tolerances=self.g_tolerances, - g_tolerance_units=self.g_tolerance_units, - peptide_length_range=self.peptide_length_range, - precursor_charges=self.precursor_charges, - ion_series=self.ion_series, - ion_charges=self.ion_charges, - peptide_mz_range=self.peptide_mz_range, - ) - return conf - def log(self, logger: Logger, level: str = "INFO") -> None: """Logs all the configurations using the passed logger.""" logger.log(level, "Diadem Configuration:") @@ -158,11 +118,93 @@ def hash(self) -> str: Example ------- - >>> DiademConfig().hash() - '19922fcb81d81062169e5a677517e00b' - >>> DiademConfig(ion_series = "y").hash() - 'a365384390de3ba5f448096a73155005' + >>> DiademIndexConfig().hash() + '1a23e68d04576bb73dbd5e0173679e64' + >>> DiademIndexConfig(ion_series = "y").hash() + '846dbaf6adb3e2ddc5779fc5169ec675' """ h = hashlib.md5() h.update(tomli_w.dumps(self.toml_dict()).encode()) return h.hexdigest() + + @property + def ms2ml_config(self) -> Config: + """Returns the ms2ml config. + + It exports all the parameters that are used inside of + ms2ml to its own configuration object. + """ + conf = Config( + g_tolerances=[], + g_tolerance_units=[], + peptide_length_range=self.peptide_length_range, + precursor_charges=self.precursor_charges, + ion_series=self.ion_series, + ion_charges=self.ion_charges, + peptide_mz_range=self.peptide_mz_range, + ) + return conf + + +@dataclass(frozen=True, eq=True) +class DiademConfig(DiademIndexConfig): # noqa + g_tolerances: tuple[float, ...] = field(default=(20, 50)) + g_tolerance_units: tuple[MassError, ...] = field(default=("ppm", "ppm")) + + g_ims_tolerance: float = 0.05 + g_ims_tolerance_unit: Literal["abs"] = "abs" + # Main score + # Currently unused ... + scoring_score_function: ScoringFunctions = "Hyperscore" + + run_max_peaks: int = 1e6 + + # the 5k number comes from the neviskii lab paper on deisotoping + run_max_peaks_per_spec: int = 5_000 + + # Prallelism 1 means no parallelism, -1 means all cores, any other positive + # integer means use that many cores. + run_parallelism: int = -4 + run_deconvolute_spectra: bool = True + run_min_peak_intensity: float = 100 + run_debug_log_frequency: int = 50 + run_allowed_fails: int = 700 + run_window_size: int = 21 + run_max_peaks_per_window: int = 150 + + # Min intensity to consider for matching and extracting + run_min_intensity_ratio: float = 0.01 + run_min_correlation_score: float = 0.5 + + run_scaling_ratio = 0.001 + run_scalin_limits: tuple[float, float] = (0.001, 0.999) + + @property + def ms2ml_config(self) -> Config: + """Returns the ms2ml config. + + It exports all the parameters that are used inside of + ms2ml to its own configuration object. + """ + conf = Config( + g_tolerances=self.g_tolerances, + g_tolerance_units=self.g_tolerance_units, + peptide_length_range=self.peptide_length_range, + precursor_charges=self.precursor_charges, + ion_series=self.ion_series, + ion_charges=self.ion_charges, + peptide_mz_range=self.peptide_mz_range, + ) + return conf + + @property + def index_config(self) -> DiademIndexConfig: + """Generates an index config. + + The index config is a subset of the DiademConfig. + Therefore generating this subset allow us to hash + it in a way that would identify the index generation. + """ + self_dict = asdict(self) + kwargs = {x.name: self_dict[x.name] for x in fields(DiademIndexConfig)} + return DiademIndexConfig(**kwargs) diff --git a/diadem/data_io/__init__.py b/diadem/data_io/__init__.py new file mode 100644 index 0000000..61b6a43 --- /dev/null +++ b/diadem/data_io/__init__.py @@ -0,0 +1,22 @@ +from os import PathLike + +from diadem.config import DiademConfig +from diadem.data_io.mzml import SpectrumStacker +from diadem.data_io.timstof import TimsSpectrumStacker + + +def read_raw_data( + filepath: PathLike, config: DiademConfig +) -> TimsSpectrumStacker | SpectrumStacker: + """Generic function to read data for DIA. + + It uses the file extension to know whether to dispatch the data to an + mzML or timsTOF reader. + """ + if str(filepath).endswith(".d"): + rf = TimsSpectrumStacker(filepath=filepath, config=config) + elif str(filepath).lower().endswith(".mzml"): + rf = SpectrumStacker(filepath, config=config) + else: + raise NotImplementedError + return rf diff --git a/diadem/mzml.py b/diadem/data_io/mzml.py similarity index 83% rename from diadem/mzml.py rename to diadem/data_io/mzml.py index 5d94dab..b97b347 100644 --- a/diadem/mzml.py +++ b/diadem/data_io/mzml.py @@ -3,7 +3,7 @@ import os from dataclasses import dataclass from pathlib import Path -from typing import Iterable, Iterator +from typing import Iterator import numpy as np from joblib import Parallel, delayed @@ -16,27 +16,11 @@ from tqdm.auto import tqdm from diadem.config import DiademConfig, MassError +from diadem.data_io.utils import slice_from_center, strictzip, xic from diadem.deisotoping import deisotope from diadem.search.metrics import get_ref_trace_corrs from diadem.utils import check_sorted -try: - zip([], [], strict=True) - - def strictzip(*args: Iterable) -> Iterable: - """Like zip but checks that the length of all elements is the same.""" - return zip(*args, strict=True) - -except TypeError: - - def strictzip(*args: Iterable) -> Iterable: - """Like zip but checks that the length of all elements is the same.""" - args = [list(arg) for arg in args] - lengs = {len(x) for x in args} - if len(lengs) > 1: - raise ValueError("All arguments need to have the same legnths") - return zip(*args) - @dataclass class ScanGroup: @@ -77,8 +61,8 @@ def get_highest_window( self, window: int, min_intensity_ratio: float, - tolerance: float, - tolerance_unit: str, + mz_tolerance: float, + mz_tolerance_unit: str, min_correlation: float, max_peaks: int, ) -> StackedChromatograms: @@ -98,8 +82,8 @@ def get_highest_window( window=window, min_intensity_ratio=min_intensity_ratio, min_correlation=min_correlation, - tolerance=tolerance, - tolerance_unit=tolerance_unit, + mz_tolerance=mz_tolerance, + mz_tolerance_unit=mz_tolerance_unit, max_peaks=max_peaks, ) @@ -163,102 +147,6 @@ def __len__(self) -> int: return len(self.intensities) -# @profile -def xic( - query_mz: NDArray[np.float32], - query_int: NDArray[np.float32], - mzs: NDArray[np.float32], - tolerance_unit: MassError = "da", - tolerance: float = 0.02, -) -> tuple[NDArray[np.float32], list[list[int]]]: - """Gets the extracted ion chromatogram form arrays. - - Gets the extracted ion chromatogram from the passed mzs and intensities - The output should be the same length as the passed mzs. - - Returns - ------- - NDArray[np.float32] - An array of length `len(mzs)` that integrates such masses in - the query_int (matching with the query_mz array ...) - - list[list[int]] - A nested list of length `len(mzs)` where each sub-list contains - the indices of the `query_int` array that were integrated. - - """ - theo_mz_indices, obs_mz_indices = annotate_peaks( - theo_mz=mzs, - mz=query_mz, - tolerance=tolerance, - unit=tolerance_unit, - ) - - outs = np.zeros_like(mzs, dtype="float") - inds = [] - for i in range(len(outs)): - query_indices = obs_mz_indices[theo_mz_indices == i] - ints_subset = query_int[query_indices] - if len(ints_subset) == 0: - inds.append([]) - else: - outs[i] = np.sum(ints_subset) - inds.append(query_indices) - - return outs, inds - - -def slice_from_center(center: int, window: int, length: int) -> tuple[slice, int]: - """Generates a slice provided a center and window size. - - Creates a slice that accounts for the endings of an iterable - in such way that the window size is maintained. - - Examples - -------- - >>> my_list = [0,1,2,3,4,5,6] - >>> slc, center_index = slice_from_center( - ... center=4, window=3, length=len(my_list)) - >>> slc - slice(3, 6, None) - >>> my_list[slc] - [3, 4, 5] - >>> my_list[slc][center_index] == my_list[4] - True - - >>> slc = slice_from_center(1, 3, len(my_list)) - >>> slc - (slice(0, 3, None), 1) - >>> my_list[slc[0]] - [0, 1, 2] - - >>> slc = slice_from_center(6, 3, len(my_list)) - >>> slc - (slice(4, 7, None), 2) - >>> my_list[slc[0]] - [4, 5, 6] - >>> my_list[slc[0]][slc[1]] == my_list[6] - True - - """ - start = center - (window // 2) - end = center + (window // 2) + 1 - center_index = window // 2 - - if start < 0: - start = 0 - end = window - center_index = center - - if end >= length: - end = length - start = end - window - center_index = window - (length - center) - - slice_q = slice(start, end) - return slice_q, center_index - - @dataclass class StackedChromatograms: """A class containing the elements of a stacked chromatogram. @@ -279,7 +167,8 @@ class StackedChromatograms: base_peak_intensity : Intensity of the base peak in the reference spectrum stack_peak_indices : - List of indices used to stack the array, it is a list of dimensions [w] + List of indices used to stack the array, it is a list of dimensions [w], + where each element can be either a list of indices or an empty list. Details ------- @@ -373,8 +262,8 @@ def from_group( group: ScanGroup, index: int, window: int = 21, - tolerance: float = 0.02, - tolerance_unit: MassError = "da", + mz_tolerance: float = 0.02, + mz_tolerance_unit: MassError = "da", min_intensity_ratio: float = 0.01, min_correlation: float = 0.5, max_peaks: int = 150, @@ -389,9 +278,9 @@ def from_group( The index of the spectrum to use as the reference window : int, optional The number of spectra to stack, by default 21 - tolerance : float, optional + mz_tolerance : float, optional The tolerance to use when matching m/z values, by default 0.02 - tolerance_unit : MassError, optional + mz_tolerance_unit : MassError, optional The unit of the tolerance, by default "da" min_intensity_ratio : float, optional The minimum intensity ratio to use when stacking, by default 0.01 @@ -434,8 +323,8 @@ def from_group( query_mz=m, query_int=inten, mzs=center_mzs, - tolerance=tolerance, - tolerance_unit=tolerance_unit, + tolerance=mz_tolerance, + tolerance_unit=mz_tolerance_unit, ) ) if i == center_index: diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py new file mode 100644 index 0000000..c0c53ed --- /dev/null +++ b/diadem/data_io/timstof.py @@ -0,0 +1,881 @@ +from __future__ import annotations + +import os +from dataclasses import dataclass +from itertools import chain +from typing import Iterator, Literal + +import numpy as np +from alphatims.bruker import TimsTOF +from joblib import Parallel, delayed +from loguru import logger +from ms2ml.utils.mz_utils import get_tolerance +from numpy.typing import NDArray +from tqdm.auto import tqdm + +from diadem.config import DiademConfig +from diadem.data_io.mzml import ( + MassError, + ScanGroup, + SpectrumStacker, + StackedChromatograms, +) +from diadem.data_io.utils import slice_from_center, xic +from diadem.deisotoping import deisotope +from diadem.search.metrics import get_ref_trace_corrs + +IMSError = Literal["abs", "pct"] + + +@dataclass +class TimsStackedChromatograms(StackedChromatograms): + """A class containing the elements of a stacked chromatogram. + + The stacked chromatogram is the extracted ion chromatogram + across a window of spectra. + + Parameters + ---------- + array : + An array of shape [i, w] + mzs : + An array of shape [i] + ref_index : + An integer in the range [0, i] + ref_ims : + IMS value for the reference peak + parent_index : + Identifier of the range where the window was extracted + base_peak_intensity : + Intensity of the base peak in the reference spectrum + stack_peak_indices : + List of indices used to stack the array, it is a list of dimensions [w], + where each element can be either a list of indices or an empty list. + + Details + ------- + The dimensions of the arrays are `w` the window + size of the extracted ion chromatogram. `i` the number + of m/z peaks that were extracted. + + """ + + ref_ims: float + + # @profile + @staticmethod + def from_group( + group: TimsScanGroup, + index: int, + window: int = 21, + mz_tolerance: float = 0.02, + mz_tolerance_unit: MassError = "da", + ims_tolerance: float = 0.1, + ims_tolerance_unit: IMSError = "abs", + # TODO implement abs and pct ims error ... + # maybe just use the mz ppm tolerance an multiply by 10000 ... + min_intensity_ratio: float = 0.01, + min_correlation: float = 0.5, + max_peaks: int = 150, + ) -> TimsStackedChromatograms: + """Generates a stacked chromatogram from a TimsScanGroup. + + Parameters + ---------- + group : ScanGroup + A scan group containing the spectra to stack + index : int + The index of the spectrum to use as the reference + window : int, optional + The number of spectra to stack, by default 21 + mz_tolerance : float, optional + The tolerance to use when matching m/z values, by default 0.02 + mz_tolerance_unit : MassError, optional + The unit of the tolerance, by default "da" + ims_tolerance : float, optional + The tolerance to use for the ion mobility dimension. + ims_tolerance_unit : IMSError + The unit of the IMS tolerance to use, 'pct' (percent) and + 'abs' (absolute) are acceptable values. + min_intensity_ratio : float, optional + The minimum intensity ratio to use when stacking, by default 0.01 + min_correlation : float, optional + The minimum correlation to use when stacking, by default 0.5 + max_peaks : int, optional + The maximum number of peaks to return in a group, by default is 150. + If the candidates is more than this number, it will the best co-eluting + peaks. + + """ + # TODO consider if deisotoping should happen at this stage, + # not at the pre-processing stage. + # The issue with that was that tracking indices is harder. + # TODO abstract this so it is not as redundant with super().from_group() + + # The center index is the same as the provided index + # Except in cases where the edge of the group is reached, where + # the center index is adjusted to the edge of the group + slice_q, center_index = slice_from_center( + center=index, window=window, length=len(group.mzs) + ) + mzs = group.mzs[slice_q] + intensities = group.intensities[slice_q] + imss = group.imss[slice_q] + + center_mzs = mzs[center_index] + center_intensities = intensities[center_index] + center_ims = imss[center_index] + + bp_intensity_index = np.argmax(center_intensities) + bp_ims = center_ims[bp_intensity_index] + + int_keep = center_intensities >= ( + center_intensities.max() * min_intensity_ratio + ) + if ims_tolerance_unit != "abs": + raise NotImplementedError() + ims_int_keep = np.abs(center_ims - bp_ims) <= ims_tolerance + int_keep = int_keep & ims_int_keep + + center_mzs = center_mzs[int_keep] + center_intensities = center_intensities[int_keep] + + # TODO move this to its own helper function (collapse_unique ??) + # ... Maybe even "proprocess_ims_spec(mzs, imss, ints, ref_ims, ...)" + # We collapse all unique mzs, after filtering for IMS tolerance + # Note: Getting what indices were used to generate u_mzs[0] + # would be np.where(inv == 0) + u_center_mzs, inv = np.unique(center_mzs, return_inverse=True) + u_center_intensities = np.zeros( + len(u_center_mzs), dtype=center_intensities.dtype + ) + np.add.at(u_center_intensities, inv, center_intensities) + + # After makig it unique, we deisotope the spectrum + # after this, getting the indices that generated du_center_mzs[0] + # would be np.where(np.isin(inv, du_center_indices[0])) + du_center_mzs, du_center_intensities, du_center_indices = deisotope( + u_center_mzs, + u_center_intensities, + max_charge=3, + diff=mz_tolerance, + unit=mz_tolerance_unit, + track_indices=True, + ) + + xic_outs = [] + + for i, (m, inten, ims) in enumerate(zip(mzs, intensities, imss)): + # We first filter the peaks that are inside our IMS tolerance + # By getting their indices. + t_int_keep = np.abs(ims - bp_ims) <= ims_tolerance + t_int_keep = np.where(t_int_keep)[0] + + m = m[t_int_keep] + inten = inten[t_int_keep] + + u_mzs, inv = np.unique(m, return_inverse=True) + u_intensities = np.zeros(len(u_mzs), dtype=inten.dtype) + np.add.at(u_intensities, inv, inten) + + du_mzs, du_intensities, du_indices = deisotope( + u_mzs, + u_intensities, + max_charge=3, + diff=mz_tolerance, + unit=mz_tolerance_unit, + track_indices=True, + ) + + outs, inds = xic( + query_mz=du_mzs, + query_int=du_intensities, + mzs=du_center_mzs, + tolerance=mz_tolerance, + tolerance_unit=mz_tolerance_unit, + ) + + # Since inds are the indices used from that suvset array; + # We find what indices in the original array were used for + # each value. + out_inds = [] + for y in inds: + if len(y) > 0: + collapsed_indices = np.concatenate( + [np.where(np.isin(inv, du_indices[w]))[0] for w in y] + ) + out_inds.append(np.unique(t_int_keep[collapsed_indices])) + else: + out_inds.append([]) + + xic_outs.append((outs, out_inds)) + if i == center_index: + assert xic_outs[-1][0].sum() >= du_center_intensities.max() + + stacked_arr = np.stack([x[0] for x in xic_outs], axis=-1) + + indices = [x[1] for x in xic_outs] + + if stacked_arr.shape[-2] > 1: + ref_id = np.argmax(stacked_arr[..., center_index]) + corrs = get_ref_trace_corrs(arr=stacked_arr, ref_idx=ref_id) + + # I think adding the 1e-5 is needed here due to numric instability + # in the flaoting point operation + assert np.max(corrs) <= ( + corrs[ref_id] + 1e-5 + ), "Reference does not have max corrr" + + max_peak_corr = np.sort(corrs)[-max_peaks] if len(corrs) > max_peaks else -1 + keep = corrs >= max(min_correlation, max_peak_corr) + + stacked_arr = stacked_arr[..., keep, ::1] + du_center_mzs = du_center_mzs[keep] + du_center_intensities = du_center_intensities[keep] + indices = [[y for y, k in zip(x, keep) if k] for x in indices] + + ref_id = np.argmax(stacked_arr[..., center_index]) + bp_int = stacked_arr[ref_id, center_index] + + out = TimsStackedChromatograms( + array=stacked_arr, + mzs=du_center_mzs, + ref_index=ref_id, + parent_index=index, + base_peak_intensity=bp_int, + stack_peak_indices=indices, + center_intensities=du_center_intensities, + ref_ims=bp_ims, + ) + return out + + +@dataclass +class TimsScanGroup(ScanGroup): + imss: list[NDArray] + + def __post_init__(self) -> None: + """Validates that the values in the instance are consistent. + + Automatically runs when a new instance is created. + """ + super().__post_init__() + if len(self.imss) != len(self.mzs): + raise ValueError("IMS values do not have the same lenth as the MZ values") + + def get_highest_window( + self, + window: int, + min_intensity_ratio: float, + mz_tolerance: float, + mz_tolerance_unit: MassError, + ims_tolerance: float, + ims_tolerance_unit: IMSError, + min_correlation: float, + max_peaks: int, + ) -> TimsStackedChromatograms: + top_index = np.argmax(self.base_peak_int) + window = TimsStackedChromatograms.from_group( + self, + window=window, + index=top_index, + min_intensity_ratio=min_intensity_ratio, + min_correlation=min_correlation, + mz_tolerance=mz_tolerance, + mz_tolerance_unit=mz_tolerance_unit, + ims_tolerance=ims_tolerance, + ims_tolerance_unit=ims_tolerance_unit, + max_peaks=max_peaks, + ) + + return window + + def scale_window_intensities( + self, + index: int, + scaling: NDArray, + mzs: NDArray, + window_indices: list[list[int]], + window_mzs: NDArray, + ) -> None: + super().scale_window_intensities( + index=index, + scaling=scaling, + mzs=mzs, + window_indices=window_indices, + window_mzs=window_mzs, + ) + + def __len__(self) -> int: + return len(self.imss) + + +class TimsSpectrumStacker(SpectrumStacker): + def __init__(self, filepath: TimsTOF, config: DiademConfig) -> None: + self.datafile = TimsTOF(filepath) + self.config = config + unique_precursor_indices = np.unique(self.datafile.precursor_indices) + unique_precursor_indices = [x for x in unique_precursor_indices if x != 0] + + if "DEBUG_DIADEM" in os.environ: + logger.error( + "RUNNING DIADEM IN DEBUG MODE (only the second precursor index)" + ) + self.unique_precursor_indices = unique_precursor_indices[2:3] + else: + self.unique_precursor_indices = unique_precursor_indices + + def _precursor_iso_window_groups( + self, precursor_index: int, progress: bool = True + ) -> dict[str:TimsScanGroup]: + index_win_dicts = _get_precursor_index_windows( + self.datafile, precursor_index=precursor_index, progress=progress + ) + out = {} + + for k, v in index_win_dicts.items(): + mzs = [] + ints = [] + imss = [] + + for vi in v: + new_order = np.argsort(vi["mz"]) + mzs.append(vi["mz"][new_order]) + ints.append(vi["intensity"][new_order]) + imss.append(vi["ims"][new_order]) + + # TODO change this to a data structure that stores + # this only once. + quad_low = list({x["quad_low_mz_values"] for x in v}) + quad_high = list({x["quad_high_mz_values"] for x in v}) + assert len(quad_high) == 1 + assert len(quad_low) == 1 + + bp_indices = np.array([np.argmax(x) for x in ints]) + bp_ints = np.array([x1[x2] for x1, x2 in zip(ints, bp_indices)]) + bp_mz = np.array([x1[x2] for x1, x2 in zip(mzs, bp_indices)]) + # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) + rts = np.array([x["rt_values_min"] for x in v]) + scan_indices = [str(x["scan_indices"]) for x in v] + + x = TimsScanGroup( + precursor_range=(quad_low[0], quad_high[0]), + mzs=mzs, + intensities=ints, + imss=imss, + base_peak_int=bp_ints, + base_peak_mz=bp_mz, + # base_peak_ims=bp_ims, + iso_window_name=k, + retention_times=rts, + scan_ids=scan_indices, + ) + out[k] = x + + return out + + def get_iso_window_groups(self, workerpool: None | Parallel) -> list[TimsScanGroup]: + results = [] + + if workerpool is None: + nested_results = [ + self._precursor_iso_window_groups(i) + for i in self.unique_precursor_indices + ] + else: + nested_results = workerpool( + delayed(self._precursor_iso_window_groups)(i) + for i in self.unique_precursor_indices + ) + + for r in nested_results: + for rr in r.values(): + results.append(rr) + + return results + + def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGroup]: + for i in self.unique_precursor_indices: + nested_results = self._precursor_iso_window_groups(i, progress=progress) + for r in nested_results.values(): + yield r + + +def find_neighbors_mzsort( + ims_vals: NDArray[np.float64], + sorted_mz_values: NDArray[np.float64], + intensities: NDArray[np.float64], + top_n: int = 500, + top_n_pct: float = 0.1, + ims_tol: float = 0.02, + mz_tol: float = 0.02, + mz_tol_unit: MassError = "Da", +) -> dict[int : list[int]]: + """Finds the neighbors of the most intense peaks. + + Arguments: + --------- + ims_vals: NDArray[np.float64] + Array containing the ion mobility values of the precursor. + sorted_mz_values: NDArray[np.float64] + Sorted array contianing the mz values + intensities: NDArray[np.float64] + Array containing the intensities of the peaks + top_n : int + Number of peaks to use as seeds for neighborhood finding, + defautls to 500. + It will internally use the largest of either this number + or `len(intensities)*top_n_pct` + top_n_pct : float + Minimum percentage of the intensities to use as seeds for the + neighbors. defaults to 0.1 + ims_tol : float + Maximum distance to consider as a neighbor along the IMS dimension. + defaults to 0.02 + mz_tol : float + Maximum distance to consider as a neighbor along the MZ dimension. + defaults to 0.02 + mz_tol_unit : Literal['ppm', 'Da'] + Unit that describes the mz tolerance. + defaults to 'Da' + + """ + if mz_tol_unit.lower() == "da": + pass + elif mz_tol_unit.lower() == "ppm": + mz_tol: NDArray[np.float64] = get_tolerance( + mz_tol, theoretical=sorted_mz_values, unit="ppm" + ) + else: + raise ValueError("Only 'Da' and 'ppm' values are supported as mass errors") + + if intensities is not None: + top_n = int(max(len(intensities) * top_n_pct, top_n)) + if len(intensities) > top_n: + top_indices = np.argpartition(intensities, -top_n)[-top_n:] + else: + intensities = None + + opts = {} + for i1, (ims1, mz1) in enumerate(zip(ims_vals, sorted_mz_values)): + if intensities is not None: + if i1 not in top_indices: + opts.setdefault(i1, []).append(i1) + continue + + candidates = np.where(np.abs(sorted_mz_values - mz1) <= mz_tol)[0] + tmp_ims = ims_vals[candidates] + + match_indices = np.where(np.abs(tmp_ims - ims1) <= ims_tol)[0] + for i2 in candidates[match_indices]: + opts.setdefault(i1, [i1]).append(i2) + opts.setdefault(i2, [i2]).append(i1) + + opts = {k: list(set(v)) for k, v in opts.items()} + return opts + + +def propose_boxes( + intensities: NDArray[np.float32], + ims_values: NDArray[np.float32], + mz_values: NDArray[np.float64], + ref_ims: float, + ref_mz: float, + mz_sizes=(0.02,), + ims_sizes=(0.05, 0.1), +): + """Proposes and scores bounding boxes around a peak. + + Details + ------- + Score definition: + The score is more of a loss than a score (since lower is better) + The score is defined as + [ + (max_mz_error + # max_ims_error) + + (weighted_mz_standard_deviation) + + # (weighted_ims_standard_deviation) + ] / total_intensity + + Therefore, boxes with very low variance in their mzs or IMSs will have low score. + And boxes with high intensity will also have lower intensity. + + Returns + ------- + boxes + Each box has 5 number, [min_mz, max_mz, min_ims, max_ims, score] + box_intensities: list[float] + The summed intensity in each of the boxes + box_centroids: list[tuple[float, float]] + Each centroid is a tuple of `mz_centroid, ims_centroid` where the + centroid is a weighted average of that dimension, using the intensities + as weights. + + """ + score_mutiplier = max(mz_sizes) # + max(ims_sizes) + delta_ims = np.abs(ims_values - ref_ims) + delta_mzs = np.abs(mz_values - ref_mz) + + # each box has 5 number, [min_mz, max_mz, min_ims, max_ims, score] + boxes = [] + box_centroids = [] + box_intensities = [] + + for mz_tol in mz_sizes: + for ims_tol in ims_sizes: + box_index = (delta_ims <= ims_tol) & (delta_mzs <= mz_tol) + tmp_mzs = mz_values[box_index] + tmp_ims = ims_values[box_index] + tmp_intensity = intensities[box_index] + + box_intensity = tmp_intensity.sum() + if box_intensity == 0: + raise RuntimeError(f"Box has 0 intensity {box_index}") + mz_centroid = (tmp_mzs * tmp_intensity).sum() / box_intensity + ims_centroid = (tmp_ims * tmp_intensity).sum() / box_intensity + + ims_std = ( + (delta_ims[box_index] ** 2) * tmp_intensity + ).sum() / box_intensity + ims_std = np.sqrt(ims_std) + mz_std = ( + 0 # ((delta_mzs[box_index] ** 2)*tmp_intensity).sum() / box_intensity + ) + score = (score_mutiplier + (mz_std + ims_std)) / box_intensity + boxes.append( + [ + ref_mz - mz_tol, + ref_mz + mz_tol, + ref_ims - ims_tol, + ref_ims + ims_tol, + score, + ] + ) + box_intensities.append(box_intensity) + box_centroids.append((mz_centroid, ims_centroid)) + + return boxes, box_intensities, box_centroids + + +# Code modified from https://pyimagesearch.com/2015/02/16/faster-non-maximum-suppression-python/ +# Malisiewicz et al. +def non_max_suppression_fast(boxes, overlapThresh, eps=1e-5, invert_scores=False): + # Invert_scores=True keeps lowest values for the scores (like a loss) + + # Eps adds a little edge to each side of the boxes + # (making boxes of width or height==0 manageable) + + # if there are no boxes, return an empty list + if len(boxes) == 0: + return [] + # if the bounding boxes integers, convert them to floats -- + # this is important since we'll be doing a bunch of divisions + if boxes.dtype.kind == "i": + boxes = boxes.astype("float") + # initialize the list of picked indexes + pick = [] + # grab the coordinates of the bounding boxes + # Note our boxes are [min(x),max(x),min(y),max(y)], instead of [x,y,x,y] in the orifinal implementation + x1 = boxes[:, 0] - eps + x2 = boxes[:, 1] + eps + y1 = boxes[:, 2] - eps + y2 = boxes[:, 3] + eps + scores = boxes[:, 4] + # compute the area of the bounding boxes and sort the bounding + # boxes by the bottom-right y-coordinate of the bounding box + area = (x2 - x1) * (y2 - y1) + idxs = np.argsort(scores) + if invert_scores: + idxs = idxs[::-1] + + # keep looping while some indexes still remain in the indexes + # list + while len(idxs) > 0: + # grab the last index in the indexes list and add the + # index value to the list of picked indexes + last = len(idxs) - 1 + i = idxs[last] + pick.append(i) + # find the largest (x, y) coordinates for the start of + # the bounding box and the smallest (x, y) coordinates + # for the end of the bounding box + xx1 = np.maximum(x1[i], x1[idxs[:last]]) + yy1 = np.maximum(y1[i], y1[idxs[:last]]) + xx2 = np.minimum(x2[i], x2[idxs[:last]]) + yy2 = np.minimum(y2[i], y2[idxs[:last]]) + # compute the width and height of the bounding box + w = np.maximum(0, xx2 - xx1 + eps) + h = np.maximum(0, yy2 - yy1 + eps) + # compute the ratio of overlap + overlap = (w * h) / area[idxs[:last]] + # delete all indexes from the index list that have + box_inds_delete = np.where(overlap > overlapThresh)[0] + idxs = np.delete(idxs, np.concatenate(([last], box_inds_delete))) + + # return the indices of the best bounding boxes + return pick + + +def bundle_neighbors_mzsorted(ims_values, sorted_mz_values, intensities, top_n=500): + BOX_EPS = 1e-7 + + opts = find_neighbors_mzsort( + ims_values, + sorted_mz_values=sorted_mz_values, + intensities=intensities, + top_n=top_n, + ims_tol=0.1, + mz_tol=50, + mz_tol_unit="ppm", + ) + + unambiguous = {k for k, v in opts.items() if len(v) == 1} + ambiguous = {k: v for k, v in opts.items() if len(v) > 1} + + unambiguous = { + "ims": ims_values[list(unambiguous)], + "mz": sorted_mz_values[list(unambiguous)], + "intensity": intensities[list(unambiguous)], + } + + if len(ambiguous) > 0: + all_boxes = [] + all_intensities = [] + all_centroids = [] + for k, v in ambiguous.items(): + boxes, box_intensities, box_centroids = propose_boxes( + intensities[v], + ims_values[v], + mz_values=sorted_mz_values[v], + ref_ims=ims_values[k], + ref_mz=sorted_mz_values[k], + mz_sizes=( + 0.01, + 0.05, + ), + ims_sizes=(0.01, 0.05, 0.1), + # ims_sizes = (0.005, 0.01, 0.02), + ) + all_boxes.extend(boxes) + all_intensities.extend(box_intensities) + all_centroids.extend(box_centroids) + + best_boxes_indices = non_max_suppression_fast( + np.stack(all_boxes), 0, eps=BOX_EPS, invert_scores=True + ) + best_centroids = [all_centroids[x] for x in best_boxes_indices] + best_intensities = [all_intensities[x] for x in best_boxes_indices] + mzs, imss = zip(*best_centroids) + boxed = { + "ims": imss, + "mz": mzs, + "intensity": best_intensities, + } + + final = {k: np.concatenate([unambiguous[k], boxed[k]]) for k in unambiguous} + else: + final = unambiguous + return final + + +def get_break_indices( + inds: NDArray[np.int64], +) -> tuple[NDArray[np.int64], NDArray[np.int64]]: + """Gets the incides and break values for an increasing array. + + Example: + ------- + >>> tmp = np.array([1,2,3,4,7,8,9,11,12,13]) + >>> get_break_indices(tmp) + (array([0, 4, 7, 9]), array([ 1, 7, 11, 13])) + """ + if "DEBUG_DIADEM" in os.environ: + logger.error("DEBUG MODE IN DIADEM, using only 1/4 of the spectra.") + inds = inds[: int(len(inds) / 4)] + + breaks = 1 + np.where(np.diff(inds) > 1)[0] + breaks = np.concatenate([np.array([0]), breaks, np.array([inds.size - 1])]) + + break_indices = inds[breaks] + return breaks, break_indices + + +def _get_precursor_index_windows( + dia_data: TimsTOF, precursor_index: int, progress: bool = True +) -> dict[dict[list]]: + PRELIM_N_PEAK_FILTER = 5000 # noqa + MIN_INTENSITY_KEEP = 100 # noqa + MIN_NUM_SEED_BOXES = 500 # noqa + + inds = dia_data[{"precursor_indices": precursor_index}, "raw"] + breaks, break_inds = get_break_indices(inds=inds) + + push_data = dia_data.convert_from_indices( + break_inds[:-1], + raw_indices_sorted=True, + return_rt_values_min=True, + # I think this is a better value than the actual RT + # since accounts for accumulation time. + return_quad_mz_values=True, + return_scan_indices=True, + ) + + pbar = tqdm( + enumerate(zip(breaks[:-1], breaks[1:])), + total=len(breaks), + disable=not progress, + desc=f"Collecting spectra for precursor={precursor_index}", + ) + quad_splits = {} + for bi, (startind, endind) in pbar: + curr_push_data = {k: v[bi] for k, v in push_data.items()} + peak_data = dia_data.convert_from_indices( + inds[startind:endind], + raw_indices_sorted=True, + return_mz_values=True, + return_corrected_intensity_values=True, + return_mobility_values=True, + ) + + start_len = len(peak_data["mz_values"]) + keep_intens = peak_data["corrected_intensity_values"] > MIN_INTENSITY_KEEP + peak_data = {k: v[keep_intens] for k, v in peak_data.items()} + + if len(peak_data["corrected_intensity_values"]) > PRELIM_N_PEAK_FILTER: + partition_indices = np.argpartition( + peak_data["corrected_intensity_values"], -PRELIM_N_PEAK_FILTER + )[-PRELIM_N_PEAK_FILTER:] + peak_data = {k: v[partition_indices] for k, v in peak_data.items()} + + sort_idx = np.argsort(peak_data["mz_values"]) + peak_data = {k: v[sort_idx] for k, v in peak_data.items()} + if len(peak_data["corrected_intensity_values"]) == 0: + continue + + f = collapse_ims( + ims_values=peak_data["mobility_values"], + mz_values=peak_data["mz_values"], + intensity_values=peak_data["corrected_intensity_values"], + top_n=MIN_NUM_SEED_BOXES, + ) + pct_compression = round(len(f["mz"]) / start_len, ndigits=2) + pbar.set_postfix( + { + "peaks": start_len, + "final_peaks": len(f["mz"]), + "pct": pct_compression, + "min": np.min(peak_data["corrected_intensity_values"]), + "max": np.max(peak_data["corrected_intensity_values"]), + }, + refresh=False, + ) + + curr_push_data.update(f) + quad_name = ( + f"{curr_push_data['quad_low_mz_values']:.6f}," + f" {curr_push_data['quad_high_mz_values']:.6f}" + ) + quad_splits.setdefault(quad_name, []).append(curr_push_data) + return quad_splits + + +def collapse_seeds( + seeds: dict[int, list[int]], intensity_values: NDArray[np.float64] +) -> tuple[dict[int, int], set[int]]: + seeds = seeds.copy() + seed_keys = list(seeds.keys()) + seed_order = np.argsort(-intensity_values[np.array(seed_keys)]) + taken = set() + out_seeds = {} + max_iter = 3 + + for s in seed_order: + sk = seed_keys[s] + if sk in taken: + continue + + out_seeds[sk] = set(seeds.pop(sk, {sk})) + curr_len = 1 + + for _ in range(max_iter): + neigh_set = set(chain(*[seeds.pop(x, set()) for x in out_seeds[sk]])).union( + out_seeds[sk] + ) + untaken = neigh_set.difference(taken) + taken.update(untaken) + out_seeds[sk].update(untaken) + t_curr_len = len(out_seeds[sk]) + if curr_len == t_curr_len: + break + curr_len = t_curr_len + + out_seeds = {k: v for k, v in out_seeds.items() if len(v) > 0} + return out_seeds, taken + + +def collapse_ims( + ims_values, + mz_values, + intensity_values, + top_n=500, + top_n_pct=0.2, + ims_tol=0.01, + mz_tol=0.01, +) -> dict[str, NDArray[np.float64]]: + neighborhoods = find_neighbors_mzsort( + ims_vals=ims_values, + sorted_mz_values=mz_values, + intensities=intensity_values, + top_n=top_n, + top_n_pct=top_n_pct, + ims_tol=ims_tol, + mz_tol=mz_tol, + ) + + unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} + ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} + seeds = {k: v for k, v in ambiguous.items() if len(v) > 5} + + out_seeds, taken = collapse_seeds(seeds=seeds, intensity_values=intensity_values) + + ambiguous_untaken = list(set(ambiguous).difference(taken)) + unambiguous_out = { + "ims": ims_values[list(chain(unambiguous, ambiguous_untaken))], + "mz": mz_values[list(chain(unambiguous, ambiguous_untaken))], + "intensity": intensity_values[list(chain(unambiguous, ambiguous_untaken))], + } + + bundled = { + "ims": np.zeros(len(out_seeds), dtype=np.float64), + "mz": np.zeros(len(out_seeds), dtype=np.float64), + "intensity": np.zeros(len(out_seeds), dtype=np.float32), + } + for i, v in enumerate(out_seeds.values()): + v = list(v) + v_intensities = intensity_values[v] + bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) + bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity + bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity + + final = { + k: np.concatenate([unambiguous_out[k], bundled[k]]) for k in unambiguous_out + } + return final + + +if __name__ == "__main__": + file = "/Users/sebastianpaez/git/diadem/profiling/profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d" + config = DiademConfig() + + data = TimsSpectrumStacker(file, config) + group = next(data.yield_iso_window_groups()) + group.get_highest_window( + 21, + 0.01, + mz_tolerance=0.02, + mz_tolerance_unit="da", + ims_tolerance=0.03, + ims_tolerance_unit="abs", + min_correlation=0.5, + max_peaks=150, + ) diff --git a/diadem/data_io/utils.py b/diadem/data_io/utils.py new file mode 100644 index 0000000..7eb3dca --- /dev/null +++ b/diadem/data_io/utils.py @@ -0,0 +1,122 @@ +from typing import Iterable + +import numpy as np +from ms2ml.utils.mz_utils import annotate_peaks +from numpy.typing import NDArray + +from diadem.config import MassError + + +def slice_from_center(center: int, window: int, length: int) -> tuple[slice, int]: + """Generates a slice provided a center and window size. + + Creates a slice that accounts for the endings of an iterable + in such way that the window size is maintained. + + Examples + -------- + >>> my_list = [0,1,2,3,4,5,6] + >>> slc, center_index = slice_from_center( + ... center=4, window=3, length=len(my_list)) + >>> slc + slice(3, 6, None) + >>> my_list[slc] + [3, 4, 5] + >>> my_list[slc][center_index] == my_list[4] + True + + >>> slc = slice_from_center(1, 3, len(my_list)) + >>> slc + (slice(0, 3, None), 1) + >>> my_list[slc[0]] + [0, 1, 2] + + >>> slc = slice_from_center(6, 3, len(my_list)) + >>> slc + (slice(4, 7, None), 2) + >>> my_list[slc[0]] + [4, 5, 6] + >>> my_list[slc[0]][slc[1]] == my_list[6] + True + + """ + start = center - (window // 2) + end = center + (window // 2) + 1 + center_index = window // 2 + + if start < 0: + start = 0 + end = window + center_index = center + + if end >= length: + end = length + start = end - window + center_index = window - (length - center) + + slice_q = slice(start, end) + return slice_q, center_index + + +try: + zip([], [], strict=True) + + def strictzip(*args: Iterable) -> Iterable: + """Like zip but checks that the length of all elements is the same.""" + return zip(*args, strict=True) + +except TypeError: + + def strictzip(*args: Iterable) -> Iterable: + """Like zip but checks that the length of all elements is the same.""" + # TODO optimize this, try to get the length and fallback to making it a list + args = [list(arg) for arg in args] + lengs = {len(x) for x in args} + if len(lengs) > 1: + raise ValueError("All arguments need to have the same legnths") + return zip(*args) + + +# @profile +def xic( + query_mz: NDArray[np.float32], + query_int: NDArray[np.float32], + mzs: NDArray[np.float32], + tolerance_unit: MassError = "da", + tolerance: float = 0.02, +) -> tuple[NDArray[np.float32], list[list[int]]]: + """Gets the extracted ion chromatogram form arrays. + + Gets the extracted ion chromatogram from the passed mzs and intensities + The output should be the same length as the passed mzs. + + Returns + ------- + NDArray[np.float32] + An array of length `len(mzs)` that integrates such masses in + the query_int (matching with the query_mz array ...) + + list[list[int]] + A nested list of length `len(mzs)` where each sub-list contains + the indices of the `query_int` array that were integrated. + + """ + theo_mz_indices, obs_mz_indices = annotate_peaks( + theo_mz=mzs, + mz=query_mz, + tolerance=tolerance, + unit=tolerance_unit, + ) + + outs = np.zeros_like(mzs, dtype="float") + inds = [] + for i in range(len(outs)): + query_indices = obs_mz_indices[theo_mz_indices == i] + ints_subset = query_int[query_indices] + if len(ints_subset) == 0: + inds.append([]) + else: + outs[i] = np.sum(ints_subset) + inds.append(query_indices) + + return outs, inds diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 3cd4759..1ebb321 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -28,6 +28,7 @@ def deisotope( max_charge: int, diff: float, unit: str, + track_indices: bool = False, ) -> tuple[NDArray[np.float32], NDArray[np.float32]]: """Deisotopes the passed spectra. @@ -45,6 +46,17 @@ def deisotope( Tolerance to use when searching (typically 20 for ppm or 0.02 for da) unit, str Unit for the diff. ppm or da + track_indices, bool + Whether to return the indices of the combined indices as well. + + Examples + -------- + >>> my_mzs = [800.9, 803.408, 804.4108, 805.4106] + >>> my_intens = [1-(0.1*i) for i,_ in enumerate(my_mzs)] + >>> deisotope(my_mzs, my_intens, max_charge=2, diff=5.0, unit="ppm") + (array([800.9 , 803.408]), array([1. , 2.4])) + >>> deisotope(my_mzs, my_intens, max_charge=2, diff=5.0, unit="ppm", track_indices=True) + (array([800.9 , 803.408]), array([1. , 2.4]), ([0], [1, 2, 3])) """ if unit.lower() == "da": @@ -61,6 +73,8 @@ def mass_unit_fun(x: float, y: float) -> float: {"mz": mz, "intensity": intensity, "envelope": None, "charge": None} for mz, intensity in peaks ] + for i in range(len(peaks)): + peaks[i]["indices"] = [i] for i in range(len(mz) - 1, -1, -1): j = i - 1 @@ -69,8 +83,12 @@ def mass_unit_fun(x: float, y: float) -> float: tol = mass_unit_fun(mz[i], diff) for charge in range(1, max_charge + 1): iso = NEUTRON / charge + # Note, this assumes that the isotope envelope always decreases in + # intensity, which is not accurate for high mol weight fragments. if abs(delta - iso) <= tol and inten[i] < inten[j]: peaks[j]["intensity"] += peaks[i]["intensity"] + if track_indices: + peaks[j]["indices"].extend(peaks[i]["indices"]) if peaks[i]["charge"] and peaks[i]["charge"] != charge: continue peaks[j]["charge"] = charge @@ -79,6 +97,8 @@ def mass_unit_fun(x: float, y: float) -> float: j -= 1 peaks = _filter_peaks(peaks) + if not track_indices: + peaks = peaks[0:2] return peaks @@ -90,9 +110,11 @@ def _filter_peaks(peaks: dict) -> tuple[NDArray[np.float32], NDArray[np.float32] It filters the ones that are not assigned to be in an envelope, thus keeping only monoisotopic peaks. """ - peaktuples = [(x["mz"], x["intensity"]) for x in peaks if x["envelope"] is None] + peaktuples = [ + (x["mz"], x["intensity"], x["indices"]) for x in peaks if x["envelope"] is None + ] if len(peaktuples) == 0: - mzs, ints = [], [] + mzs, ints, inds = [], [], [] else: - mzs, ints = zip(*peaktuples) - return np.array(mzs), np.array(ints) + mzs, ints, inds = zip(*peaktuples) + return np.array(mzs), np.array(ints), inds diff --git a/diadem/index/fragment_buckets.py b/diadem/index/fragment_buckets.py index 259f190..42bd56b 100644 --- a/diadem/index/fragment_buckets.py +++ b/diadem/index/fragment_buckets.py @@ -420,7 +420,9 @@ def yield_buckets_matching_ms2( # @profile def yield_candidates( - self, ms2_range: tuple[float, float], ms1_range: tuple[float, float] + self, + ms2_range: tuple[float, float], + ms1_range: tuple[float, float], ) -> Iterator[tuple[int, float, str]]: """Yields fragments that match the passed masses. diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index fd05903..6eeb7e2 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -215,11 +215,21 @@ def targets(self, value: list[Peptide]) -> None: value : list[Peptide] A list of peptide objects to set as the targets for the database. """ + seen = set() + use = [] + # Since right now my peptide implementation is not hashable, + # this needs to be done to make sure all targets are unique. for x in value: x.config = self.ms2ml_config - self._targets = value + if (proforma := x.to_proforma()) in seen: + continue + else: + use.append(x) + seen.add(proforma) + + self._targets = use self._decoys = None - self.target_proforma = {x.to_proforma() for x in value} + self.target_proforma = {x.to_proforma() for x in use} @property def decoys(self) -> list[Peptide]: @@ -269,12 +279,12 @@ def ms1_filter(pep: Peptide) -> Peptide | None: config=self.config.ms2ml_config, only_unique=True, enzyme=self.config.db_enzyme, - missed_cleavages=1, + missed_cleavages=self.config.db_max_missed_cleavages, allow_modifications=False, out_hook=ms1_filter, ) - sequences = list(adapter.parse()) - self.targets = sequences + sequences = adapter.parse() + self.targets = list(sequences) def prefilter_ms1( self, ms1_range: tuple[float, float], num_decimals: int = 3 @@ -457,11 +467,13 @@ def index_from_sequences(self) -> None: Usage ----- - > db = IndexedDb(...) - > db.targets = [Peptide(...), Peptide(...)] + ``` + db = IndexedDb(...) + db.targets = [Peptide(...), Peptide(...)] or - > db.targets_from_fasta(...) - > db.index_from_sequences() + db.targets_from_fasta(...) + db.index_from_sequences() + ``` Note: ---- @@ -545,8 +557,18 @@ def index_from_arrays( also have to be the same. """ - assert len(prec_mzs) == len(prec_seqs) - assert all(len(frag_mzs) == len(x) for x in [frag_series, frag_to_prec_ids]) + if not len(prec_mzs) == len(prec_seqs): + raise ValueError( + "The length of the precursor mzs and the precursor sequences needs to" + " be the same." + ) + if not all(len(frag_mzs) == len(x) for x in [frag_series, frag_to_prec_ids]): + raise ValueError( + "The length of the frag_mz, frag_series and frag_to_prec_ids need to be" + " the same" + ) + if not len(prec_seqs) == len(np.unique(prec_seqs)): + raise ValueError("All precursor sequences need to be unique!") # Sorted externally by ms2 mz with disabled_gc(): @@ -614,6 +636,8 @@ def yield_candidates( ) -> Iterator[tuple[int, float, str]]: """Yields candidate fragments that match both an ms1 and an ms2 range. + Nore: MS1 range is ignored when the database has been pre-filtered. + Parameters ---------- ms2_range : tuple[float, float] @@ -776,6 +800,14 @@ def hyperscore( scores["Peptide"] = self.seqs[indices_seqs_local] scores["decoy"] = [s not in self.target_proforma for s in scores["Peptide"]] + try: + assert len(np.unique(scores["Peptide"])) == len(scores), np.unique( + scores["Peptide"], return_counts=True + ) + except AssertionError: + # There is a bug that gets detected here where a single peptide gets + # scored multiple times ... Usually with different IDs + breakpoint() return scores @@ -805,6 +837,7 @@ def index_prefiltered_from_parquet( chunk_seq_df.select(["seq_id", "seq_mz"]), on="seq_id", how="inner" ) .sort(pl.col("mz")) + .unique() .collect() ) @@ -827,16 +860,20 @@ def index_prefiltered_from_parquet( out.prefiltered_ms1 = True # TODO change it so the only required section # is the proforma seqs that are in the mz range - chunk_seq_df_coll = chunk_seq_df.sort(pl.col("seq_id")).collect() + chunk_seq_df_coll = chunk_seq_df.sort(pl.col("seq_id")).unique().collect() out.seq_prec_mzs = chunk_seq_df_coll["seq_mz"].to_numpy() out.seqs = chunk_seq_df_coll["seq_proforma"].to_numpy() out.seq_ids = chunk_seq_df_coll["seq_id"].to_numpy() + target_set = chunk_seq_df.select(["seq_proforma", "decoy"]).collect() target_set = set( - chunk_seq_df.filter(pl.col("decoy") is False) - .select(["seq_proforma"]) - .collect()["seq_proforma"] + target_set["seq_proforma"].to_numpy()[ + np.invert(target_set["decoy"].to_numpy()) + ] ) + + if len(target_set) == 0: + logger.warning(f"No targets were found in range {min_mz}-{max_mz}") out.target_proforma = target_set return out @@ -849,7 +886,8 @@ def db_from_fasta( It internally checks the existance of a cache in the form of an sqlite file. Future implementations will allow cahching in the form of parquet. """ - config_hash = config.hash() + index_config = config.index_config + config_hash = index_config.hash() file_cache = file_cache_dir(file=fasta) curr_cache = file_cache / config_hash diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 9cdfc95..c00effe 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -2,6 +2,7 @@ import itertools import time +from os import PathLike from pathlib import Path import numpy as np @@ -13,8 +14,10 @@ from tqdm.auto import tqdm from diadem.config import DiademConfig +from diadem.data_io import read_raw_data +from diadem.data_io.mzml import ScanGroup, StackedChromatograms +from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta -from diadem.mzml import ScanGroup, SpectrumStacker, StackedChromatograms from diadem.search.search_utils import make_pin @@ -30,7 +33,10 @@ def plot_to_log(*args, **kwargs) -> None: # noqa # @profile def search_group( - group: ScanGroup, db: IndexedDb, config: DiademConfig, progress: bool = True + group: ScanGroup | TimsScanGroup, + db: IndexedDb, + config: DiademConfig, + progress: bool = True, ) -> DataFrame: """Search a group of scans. @@ -59,9 +65,36 @@ def search_group( MS2_TOLERANCE = config.g_tolerances[1] # noqa MS2_TOLERANCE_UNIT = config.g_tolerance_units[1] # noqa + IMS_TOLERANCE = config.g_ims_tolerance # noqa + IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa + + new_window_kwargs = { + "window": WINDOWSIZE, + "min_intensity_ratio": MIN_INTENSITY_RATIO, + "min_correlation": MIN_CORR_SCORE, + "mz_tolerance": MS2_TOLERANCE, + "mz_tolerance_unit": MS2_TOLERANCE_UNIT, + "max_peaks": WINDOW_MAX_PEAKS, + } + + if hasattr(group, "imss"): + logger.info("Detected a diaPASEF dataset") + new_window_kwargs.update( + { + "ims_tolerance": IMS_TOLERANCE, + "ims_tolerance_unit": IMS_TOLERANCE_UNIT, + } + ) + stack_getter = TimsStackedChromatograms.from_group + + else: + logger.info("No IMS detected") + stack_getter = StackedChromatograms.from_group + # Results and stats related variables group_results = [] intensity_log = [] + score_log = [] index_log = [] fwhm_log = [] num_peaks = 0 @@ -84,14 +117,8 @@ def search_group( ) break - new_stack: StackedChromatograms = group.get_highest_window( - window=WINDOWSIZE, - min_intensity_ratio=MIN_INTENSITY_RATIO, - min_correlation=MIN_CORR_SCORE, - tolerance=MS2_TOLERANCE, - tolerance_unit=MS2_TOLERANCE_UNIT, - max_peaks=WINDOW_MAX_PEAKS, - ) + new_stack: StackedChromatograms | TimsStackedChromatograms + new_stack = group.get_highest_window(**new_window_kwargs) if new_stack.base_peak_intensity < MIN_PEAK_INTENSITY: break @@ -132,6 +159,7 @@ def search_group( scores = None if scores is not None: + score_log.append(scores["Score"].max()) scores["id"] = match_id ref_peak_mz = new_stack.mzs[new_stack.ref_index] @@ -144,7 +172,6 @@ def search_group( # Scale based on the inverse of the reference chromatogram normalized_trace = new_stack.ref_trace / new_stack.ref_trace.max() - # scaling = SCALING_RATIO * (1-normalized_trace) scaling = 1 - normalized_trace # Since depending on the sampling rate the peak might be very assymetrical, # for now I am not mirroring the scaling @@ -162,15 +189,10 @@ def search_group( if (num_peaks % DEBUG_FREQUENCY) == 0: before = new_stack.ref_trace.copy() - s = StackedChromatograms.from_group( + s = stack_getter( group=group, index=new_stack.parent_index, - window=WINDOWSIZE, - tolerance=db.config.g_tolerances[1], - tolerance_unit=db.config.g_tolerance_units[1], - min_intensity_ratio=MIN_INTENSITY_RATIO, - min_correlation=MIN_CORR_SCORE, - max_peaks=MAX_PEAKS, + **new_window_kwargs, ) plot_to_log( [before, s.ref_trace], @@ -184,7 +206,7 @@ def search_group( group_results.append(scores.copy()) else: - logger.info(f"{match_id} did not match any peptides, scaling and skipping") + logger.debug(f"{match_id} did not match any peptides, scaling and skipping") scaling = SCALING_RATIO * np.ones_like(new_stack.ref_trace) group.scale_window_intensities( index=new_stack.parent_index, @@ -196,6 +218,14 @@ def search_group( num_fails += 1 num_peaks += 1 + pbar.set_postfix( + { + "last_id": last_id, + "max_intensity": curr_highest_peak_int, + "num_fails": num_fails, + "num_scores": len(group_results), + } + ) pbar.update(1) if (num_peaks % DEBUG_FREQUENCY) == 0: logger.debug( @@ -206,6 +236,7 @@ def search_group( plot_to_log( np.log1p(np.array(intensity_log)), title="Max (log) intensity over time" ) + plot_to_log(np.array(score_log), title="Score over time") plot_to_log(np.array(index_log), title="Requested index over time") plot_to_log(np.array(fwhm_log), title="FWHM across time") logger.info( @@ -221,7 +252,7 @@ def search_group( # @profile def diadem_main( fasta_path: Path | str, - mzml_path: Path | str, + data_path: Path | str, config: DiademConfig, out_prefix: str = "", ) -> None: @@ -231,8 +262,8 @@ def diadem_main( ---------- fasta_path : Path | str Path to the fasta file - mzml_path : Path | str - Path to the mzml file + data_path : Path | str + Path to the mzml file or .d directory. config : DiademConfig Configuration object to use for the run. out_prefix : str, optional @@ -248,8 +279,8 @@ def diadem_main( ) # set up mzml file - ss = SpectrumStacker( - mzml_file=mzml_path, + ss = read_raw_data( + filepath=data_path, config=config, ) @@ -261,24 +292,37 @@ def diadem_main( group_results = search_group(group=group, db=group_db, config=config) results.append(group_results) else: + + @delayed + def setup_db_and_search( + precursor_range: tuple[float, float], + db: IndexedDb, + cache_location: PathLike, + config: DiademConfig, + group: ScanGroup, + ) -> DataFrame: + pfdb = db.index_prefiltered_from_parquet(cache_location, *precursor_range) + results = search_group(group=group, db=pfdb, config=config) + return results + with Parallel(n_jobs=config.run_parallelism) as workerpool: groups = ss.get_iso_window_groups(workerpool=workerpool) precursor_ranges = [group.precursor_range for group in groups] - dbs = workerpool( - delayed(db.index_prefiltered_from_parquet)(cache, *prange) - for prange in precursor_ranges - ) results = workerpool( - delayed(search_group)(group=group, db=pfdb, config=config) - for group, pfdb in zip(groups, dbs) + setup_db_and_search( + precursor_range=prange, + db=db, + cache_location=cache, + config=config, + group=group, + ) + for group, prange in zip(groups, precursor_ranges) ) results: pd.DataFrame = pd.concat(results, ignore_index=True) prefix = out_prefix + ".diadem" if out_prefix else "diadem" - prefix_dir = Path(prefix).absolute() - - prefix_dir.parent.mkdir(exist_ok=True) + Path(prefix).absolute().parent.mkdir(exist_ok=True) logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) @@ -286,7 +330,7 @@ def diadem_main( make_pin( results, fasta_path=fasta_path, - mzml_path=mzml_path, + mzml_path=data_path, pin_path=prefix + ".tsv.pin", ) diff --git a/profiling/get_data.bash b/profiling/get_data.bash index c3372c5..0551934 100644 --- a/profiling/get_data.bash +++ b/profiling/get_data.bash @@ -8,3 +8,6 @@ curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_dia gunzip ./profiling_data/*.gz for i in ./profiling_data/*.zip ; do unzip $i ; done mv LFQ_timsTOFPro_* ./profiling_data/. + +curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Bacteria/UP000000625/UP000000625_83333.fasta.gz --output ./profiling_data/UP000000625_83333.fasta.gz +gunzip ./profiling_data/UP000000625_83333.fasta.gz diff --git a/profiling/run_profile.py b/profiling/run_profile.py index 3897ea7..37cf2ab 100644 --- a/profiling/run_profile.py +++ b/profiling/run_profile.py @@ -4,7 +4,7 @@ cli.setup_logger() cli.diadem_main( fasta_path="./profiling_data/uniprot_human_sp_canonical_2021-11-19_crap.fasta", - mzml_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", + data_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", config=DiademConfig(run_parallelism=1), out_prefix="lineprofile_results/results", ) diff --git a/profiling/run_profile_multithread.py b/profiling/run_profile_multithread.py index 9f95ab1..f2591e4 100644 --- a/profiling/run_profile_multithread.py +++ b/profiling/run_profile_multithread.py @@ -4,7 +4,7 @@ cli.setup_logger(level="INFO") cli.diadem_main( fasta_path="./profiling_data/uniprot_human_sp_canonical_2021-11-19_crap.fasta", - mzml_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", - config=DiademConfig(run_parallelism=-2), + data_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", + config=DiademConfig(run_parallelism=-4), out_prefix="lineprofile_results_multithread/results", ) diff --git a/profiling/run_profile_multithread.zsh b/profiling/run_profile_multithread.zsh index 0fe745d..a939e69 100644 --- a/profiling/run_profile_multithread.zsh +++ b/profiling/run_profile_multithread.zsh @@ -1,9 +1,20 @@ +python -m pip install ../. PYTHONOPTIMIZE=1 python run_profile_multithread.py +mokapot lineprofile_results_multithread/results.diadem.tsv.pin --test_fdr 0.01 --keep_decoys R -e 'library(tidyverse) ; foo = readr::read_tsv("mokapot.peptides.txt") ; foo2 = readr::read_tsv("mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_multithread/td_plot.png", plot = g)' R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_multithread/raw_td_plot.png", plot = g)' R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_multithread/iter_score_plot.png", plot = g)' # [INFO] - Found 26288 peptides with q<=0.01 # 2023-02-13 12:43:55.191 | INFO | diadem.search.diadem:diadem_main:295 - Elapsed time: 3487.5772829055786 + +## Experimental branch feature/fix_reference_peak +# Changing scoring to require the reference peak to be included +# [INFO] - Found 6759 peptides with q<=0.01 +# 2023-02-13 18:40:01.719 | INFO | diadem.search.diadem:diadem_main:305 - Elapsed time: 1881.4424259662628 + +# Tuning some parameters, changing threads to 4 +# [INFO] - Found 16790 peptides with q<=0.01 +# 2023-02-21 17:46:55.441 | INFO | diadem.search.diadem:diadem_main:312 - Elapsed time: 6328.449725866318 diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py new file mode 100644 index 0000000..1787560 --- /dev/null +++ b/profiling/run_profile_tims.py @@ -0,0 +1,10 @@ +from diadem import cli +from diadem.config import DiademConfig + +cli.setup_logger() +cli.diadem_main( + fasta_path="./profiling_data/UP000000625_83333.fasta", + data_path="./profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d", + config=DiademConfig(run_parallelism=1), + out_prefix="lineprofile_results_tims/results", +) diff --git a/pyproject.toml b/pyproject.toml index b077b1e..7bc9fe4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ {name = "Carollyn Allen", email = "callen@talus.bio"}, ] description = "A modular, feature-centric toolkit for DIA proteomics" -requires-python = ">=3.9,<=3.12" +requires-python = ">=3.9,<3.11" keywords = ["proteomics", "dia", "mass spec"] license = {text = "Apache 2.0"} classifiers = [ @@ -22,8 +22,7 @@ classifiers = [ dependencies = [ "pandas >= 1.5.2", "numpy >= 1.23.5", - # "ms2ml >= 0.0.33", - "ms2ml >= 0.0.31", + "ms2ml >= 0.0.33", "tqdm >= 4.64.1", "loguru >= 0.6.0", "rich-click >= 1.6.0", @@ -31,13 +30,15 @@ dependencies = [ "pyarrow >= 10.0.1", "platformdirs >= 2.6.0", "joblib >= 1.2.0", - # Deisotoping - "brain-isotopic-distribution >= 1.5.11", - "ms-peak-picker >= 0.1.40", - "ms-deisotope >= 0.0.46", + "alphatims >= 1.0.6", + "hdf5plugin", + "polars >= 0.16.9", ] dynamic = ["version"] +[project.scripts] +diadem = "diadem.cli:main_cli" + [project.readme] file = "README.md" content-type = "text/markdown" @@ -55,6 +56,12 @@ test = [ profiling = [ "line_profiler", ] +dev = [ + "ruff", + "black", + "isort", + "pylance", +] [tool.setuptools.packages.find] diff --git a/tests/conftest.py b/tests/conftest.py index cd2bcec..66aab9b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -166,5 +166,8 @@ def albumin_peptides(): "LVAASQAALGLLVAASQAALGL", ] + # ALBUMIN_PEPTIDE_SEQS = set(ALBUMIN_PEPTIDE_SEQS) + # I am not making this a set on purposen to make sere the runtime handles it + # correctly out = [Peptide.from_proforma_seq(f"{x}/2") for x in ALBUMIN_PEPTIDE_SEQS] return out diff --git a/tests/test_database_build.py b/tests/test_database_build.py index e955a73..22fed62 100644 --- a/tests/test_database_build.py +++ b/tests/test_database_build.py @@ -1,3 +1,5 @@ +import numpy as np + from diadem.config import DiademConfig from diadem.index.indexed_db import IndexedDb @@ -12,6 +14,7 @@ def test_peptide_scoring(sample_peaks, albumin_peptides): db = IndexedDb(config=diadem_config, chunksize=64) db.targets = albumin_peptides db.index_from_sequences() + # breakpoint() scores = db.hyperscore(z2_mass, mzs, ints) assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"]) return db @@ -26,5 +29,5 @@ def test_database_from_fasta(shared_datadir, sample_peaks): mzs, ints, z2_mass = sample_peaks scores = db.hyperscore(z2_mass, mzs, ints) - assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"]) + assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"][np.invert(scores["decoy"])]) return db diff --git a/tests/test_database_parquet_cache.py b/tests/test_database_parquet_cache.py index 5e37e01..bd21f70 100644 --- a/tests/test_database_parquet_cache.py +++ b/tests/test_database_parquet_cache.py @@ -1,3 +1,4 @@ +import numpy as np import pandas as pd from diadem.config import DiademConfig @@ -27,3 +28,6 @@ def test_parquet_generation(shared_datadir, tmpdir, sample_peaks): mzs, ints, z2_mass = sample_peaks scores = db.hyperscore(z2_mass, mzs, ints) assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"]) + + pep_scores = scores[scores["Peptide"] == "VPQVSTPTLVEVSR/2"] + assert all(np.invert(pep_scores["decoy"])) diff --git a/tests/test_database_scoring.py b/tests/test_database_scoring.py index c9b12ea..b8a13cd 100644 --- a/tests/test_database_scoring.py +++ b/tests/test_database_scoring.py @@ -29,6 +29,7 @@ def test_fasta_shows_in_db(shared_datadir): ms1_range = (s.mz - 10, s.mz + 10) score_df = db.hyperscore(ms1_range, spec_mz=mzs, spec_int=intens, top_n=2) + score_df = score_df[np.invert(score_df["decoy"])] assert s.to_proforma() in list( score_df["Peptide"] ), f"Peptide i={i} {s.to_proforma()} not in db" diff --git a/tests/test_dia_search.py b/tests/test_dia_search.py index 989f391..489f0e1 100644 --- a/tests/test_dia_search.py +++ b/tests/test_dia_search.py @@ -1,3 +1,4 @@ +import numpy as np import pandas as pd import pytest from ms2ml import Peptide @@ -6,8 +7,8 @@ from diadem.search.diadem import diadem_main -@pytest.mark.parametrize("parallel", [True, False], ids=["Parallel", "NoParallel"]) -def test_dia_search_works(tmpdir, shared_datadir, parallel): +@pytest.mark.parametrize("parallel", [False, True], ids=["NoParallel", "Parallel"]) +def test_dia_search_works_mzml(tmpdir, shared_datadir, parallel): """Uses simulated data to test diadem. Uses data simulated using Synthedia to check if the full search @@ -17,10 +18,11 @@ def test_dia_search_works(tmpdir, shared_datadir, parallel): mzml = shared_datadir / "mzml/FGFR1_600_800_5min_group_0_sample_0.mzML" config = DiademConfig(run_parallelism=2 if parallel else 1) out = str(tmpdir / "out") - diadem_main(config=config, mzml_path=mzml, fasta_path=fasta, out_prefix=out) + diadem_main(config=config, data_path=mzml, fasta_path=fasta, out_prefix=out) expected_csv = out + ".diadem.csv" df = pd.read_csv(expected_csv) + df = df[np.invert(df["decoy"])] peptides = {Peptide.from_sequence(x).stripped_sequence for x in df.Peptide.unique()} theo_table = pd.read_csv( From d6946f770b909eb028fb6db401e916a101c132f9 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 8 Mar 2023 22:15:01 -0800 Subject: [PATCH 02/41] (wip) changed parameters on IMS collapsing and deisotoping --- diadem/config.py | 4 +- diadem/data_io/__init__.py | 2 +- diadem/data_io/mzml.py | 13 ++- diadem/data_io/timstof.py | 162 +++++++++++++++++++----------- diadem/deisotoping.py | 1 + diadem/index/indexed_db.py | 15 ++- diadem/search/diadem.py | 65 ++++++++---- diadem/utils.py | 11 ++ profiling/.gitignore | 5 + profiling/lineprofile_dia.zsh | 2 +- profiling/lineprofile_timstof.zsh | 26 +++++ profiling/run_profile_tims.py | 10 +- 12 files changed, 229 insertions(+), 87 deletions(-) create mode 100644 profiling/lineprofile_timstof.zsh diff --git a/diadem/config.py b/diadem/config.py index 14d0c25..9c76fa0 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -148,10 +148,10 @@ def ms2ml_config(self) -> Config: @dataclass(frozen=True, eq=True) class DiademConfig(DiademIndexConfig): # noqa - g_tolerances: tuple[float, ...] = field(default=(20, 50)) + g_tolerances: tuple[float, ...] = field(default=(20, 20)) g_tolerance_units: tuple[MassError, ...] = field(default=("ppm", "ppm")) - g_ims_tolerance: float = 0.05 + g_ims_tolerance: float = 0.03 g_ims_tolerance_unit: Literal["abs"] = "abs" # Main score # Currently unused ... diff --git a/diadem/data_io/__init__.py b/diadem/data_io/__init__.py index 61b6a43..0d4b1b2 100644 --- a/diadem/data_io/__init__.py +++ b/diadem/data_io/__init__.py @@ -13,7 +13,7 @@ def read_raw_data( It uses the file extension to know whether to dispatch the data to an mzML or timsTOF reader. """ - if str(filepath).endswith(".d"): + if str(filepath).endswith(".d") or str(filepath).endswith("hdf"): rf = TimsSpectrumStacker(filepath=filepath, config=config) elif str(filepath).lower().endswith(".mzml"): rf = SpectrumStacker(filepath, config=config) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index b97b347..c62e5d8 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -19,7 +19,7 @@ from diadem.data_io.utils import slice_from_center, strictzip, xic from diadem.deisotoping import deisotope from diadem.search.metrics import get_ref_trace_corrs -from diadem.utils import check_sorted +from diadem.utils import check_sorted, plot_to_log @dataclass @@ -56,6 +56,10 @@ def __post_init__(self) -> None: "Precursor mass range should have 2 elements," f" has {len(self.precursor_range)}" ) + plot_to_log( + self.base_peak_int, + title=f"Base peak chromatogram for the Group in {self.iso_window_name}", + ) def get_highest_window( self, @@ -139,6 +143,13 @@ def scale_window_intensities( self.intensities[i][sim] = self.intensities[i][sim] * s else: continue + + int_remove = self.intensities[i] < 10 + if np.any(int_remove): + self.intensities[i] = self.intensities[i][np.invert(int_remove)] + self.mzs[i] = self.mzs[i][np.invert(int_remove)] + if hasattr(self, "imss"): + self.imss[i] = self.imss[i][np.invert(int_remove)] self.base_peak_int[i] = np.max(self.intensities[i]) self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index c0c53ed..d138f30 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -1,8 +1,10 @@ from __future__ import annotations import os +from contextlib import contextmanager from dataclasses import dataclass from itertools import chain +from os import PathLike from typing import Iterator, Literal import numpy as np @@ -62,8 +64,8 @@ class TimsStackedChromatograms(StackedChromatograms): ref_ims: float - # @profile @staticmethod + # @profile def from_group( group: TimsScanGroup, index: int, @@ -129,16 +131,12 @@ def from_group( bp_intensity_index = np.argmax(center_intensities) bp_ims = center_ims[bp_intensity_index] - int_keep = center_intensities >= ( - center_intensities.max() * min_intensity_ratio - ) if ims_tolerance_unit != "abs": raise NotImplementedError() - ims_int_keep = np.abs(center_ims - bp_ims) <= ims_tolerance - int_keep = int_keep & ims_int_keep + ims_keep = np.abs(center_ims - bp_ims) <= ims_tolerance - center_mzs = center_mzs[int_keep] - center_intensities = center_intensities[int_keep] + center_mzs = center_mzs[ims_keep] + center_intensities = center_intensities[ims_keep] # TODO move this to its own helper function (collapse_unique ??) # ... Maybe even "proprocess_ims_spec(mzs, imss, ints, ref_ims, ...)" @@ -162,6 +160,13 @@ def from_group( unit=mz_tolerance_unit, track_indices=True, ) + int_keep = du_center_intensities >= ( + du_center_intensities.max() * min_intensity_ratio + ) + du_center_mzs, du_center_intensities = ( + du_center_mzs[int_keep], + du_center_intensities[int_keep], + ) xic_outs = [] @@ -311,10 +316,11 @@ def __len__(self) -> int: class TimsSpectrumStacker(SpectrumStacker): - def __init__(self, filepath: TimsTOF, config: DiademConfig) -> None: - self.datafile = TimsTOF(filepath) + def __init__(self, filepath: PathLike, config: DiademConfig) -> None: + self.filepath = filepath self.config = config - unique_precursor_indices = np.unique(self.datafile.precursor_indices) + with self.lazy_datafile() as datafile: + unique_precursor_indices = np.unique(datafile.precursor_indices) unique_precursor_indices = [x for x in unique_precursor_indices if x != 0] if "DEBUG_DIADEM" in os.environ: @@ -325,52 +331,75 @@ def __init__(self, filepath: TimsTOF, config: DiademConfig) -> None: else: self.unique_precursor_indices = unique_precursor_indices + @contextmanager + def lazy_datafile(self) -> TimsTOF: + """Reads the timstof data and yields it as context manager. + + This is right now a bandaid to prevent the full TimsTOF object + to be stored in memmory for the whole lifetime of this class. + """ + _datafile = TimsTOF(self.filepath) + yield _datafile + del _datafile + def _precursor_iso_window_groups( self, precursor_index: int, progress: bool = True ) -> dict[str:TimsScanGroup]: - index_win_dicts = _get_precursor_index_windows( - self.datafile, precursor_index=precursor_index, progress=progress - ) - out = {} - - for k, v in index_win_dicts.items(): - mzs = [] - ints = [] - imss = [] - - for vi in v: - new_order = np.argsort(vi["mz"]) - mzs.append(vi["mz"][new_order]) - ints.append(vi["intensity"][new_order]) - imss.append(vi["ims"][new_order]) - - # TODO change this to a data structure that stores - # this only once. - quad_low = list({x["quad_low_mz_values"] for x in v}) - quad_high = list({x["quad_high_mz_values"] for x in v}) - assert len(quad_high) == 1 - assert len(quad_low) == 1 - - bp_indices = np.array([np.argmax(x) for x in ints]) - bp_ints = np.array([x1[x2] for x1, x2 in zip(ints, bp_indices)]) - bp_mz = np.array([x1[x2] for x1, x2 in zip(mzs, bp_indices)]) - # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) - rts = np.array([x["rt_values_min"] for x in v]) - scan_indices = [str(x["scan_indices"]) for x in v] - - x = TimsScanGroup( - precursor_range=(quad_low[0], quad_high[0]), - mzs=mzs, - intensities=ints, - imss=imss, - base_peak_int=bp_ints, - base_peak_mz=bp_mz, - # base_peak_ims=bp_ims, - iso_window_name=k, - retention_times=rts, - scan_ids=scan_indices, + with self.lazy_datafile() as datafile: + index_win_dicts = _get_precursor_index_windows( + datafile, precursor_index=precursor_index, progress=progress ) - out[k] = x + out = {} + + for k, v in index_win_dicts.items(): + mzs = [] + ints = [] + imss = [] + + for vi in v: + new_order = np.argsort(vi["mz"]) + mzs.append(vi["mz"][new_order]) + ints.append(vi["intensity"][new_order]) + imss.append(vi["ims"][new_order]) + + # TODO change this to a data structure that stores + # this only once. + quad_low = list({x["quad_low_mz_values"] for x in v}) + quad_high = list({x["quad_high_mz_values"] for x in v}) + assert len(quad_high) == 1 + assert len(quad_low) == 1 + + bp_indices = [np.argmax(x) if len(x) else None for x in ints] + bp_ints = np.array( + [ + x1[x2] if x2 is not None else 0 + for x1, x2 in zip(ints, bp_indices) + ] + ) + bp_mz = np.array( + [ + x1[x2] if x2 is not None else -1 + for x1, x2 in zip(mzs, bp_indices) + ] + ) + bp_indices = np.array(bp_indices) + # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) + rts = np.array([x["rt_values_min"] for x in v]) + scan_indices = [str(x["scan_indices"]) for x in v] + + x = TimsScanGroup( + precursor_range=(quad_low[0], quad_high[0]), + mzs=mzs, + intensities=ints, + imss=imss, + base_peak_int=bp_ints, + base_peak_mz=bp_mz, + # base_peak_ims=bp_ims, + iso_window_name=k, + retention_times=rts, + scan_ids=scan_indices, + ) + out[k] = x return out @@ -413,6 +442,9 @@ def find_neighbors_mzsort( ) -> dict[int : list[int]]: """Finds the neighbors of the most intense peaks. + It finds the neighboring peaks for the `top_n` most intense peaks + or the (TOTAL_PEAKS) * `top_n_pct` peaks, whichever is largest. + Arguments: --------- ims_vals: NDArray[np.float64] @@ -703,9 +735,9 @@ def get_break_indices( def _get_precursor_index_windows( dia_data: TimsTOF, precursor_index: int, progress: bool = True ) -> dict[dict[list]]: - PRELIM_N_PEAK_FILTER = 5000 # noqa + PRELIM_N_PEAK_FILTER = 10_000 # noqa MIN_INTENSITY_KEEP = 100 # noqa - MIN_NUM_SEED_BOXES = 500 # noqa + MIN_NUM_SEED_BOXES = 1000 # noqa inds = dia_data[{"precursor_indices": precursor_index}, "raw"] breaks, break_inds = get_break_indices(inds=inds) @@ -738,8 +770,8 @@ def _get_precursor_index_windows( ) start_len = len(peak_data["mz_values"]) - keep_intens = peak_data["corrected_intensity_values"] > MIN_INTENSITY_KEEP - peak_data = {k: v[keep_intens] for k, v in peak_data.items()} + # keep_intens = peak_data["corrected_intensity_values"] > MIN_INTENSITY_KEEP + # peak_data = {k: v[keep_intens] for k, v in peak_data.items()} if len(peak_data["corrected_intensity_values"]) > PRELIM_N_PEAK_FILTER: partition_indices = np.argpartition( @@ -758,14 +790,19 @@ def _get_precursor_index_windows( intensity_values=peak_data["corrected_intensity_values"], top_n=MIN_NUM_SEED_BOXES, ) + filter = f["intensity"] > MIN_INTENSITY_KEEP + f = {k: v[filter] for k, v in f.items()} pct_compression = round(len(f["mz"]) / start_len, ndigits=2) + final_len = len(f["intensity"]) pbar.set_postfix( { "peaks": start_len, - "final_peaks": len(f["mz"]), + "f_peaks": final_len, "pct": pct_compression, "min": np.min(peak_data["corrected_intensity_values"]), "max": np.max(peak_data["corrected_intensity_values"]), + "f_min": int(np.min(f["intensity"])) if final_len else 0, + "f_max": int(np.max(f["intensity"])) if final_len else 0, }, refresh=False, ) @@ -779,6 +816,7 @@ def _get_precursor_index_windows( return quad_splits +# @profile def collapse_seeds( seeds: dict[int, list[int]], intensity_values: NDArray[np.float64] ) -> tuple[dict[int, int], set[int]]: @@ -813,6 +851,7 @@ def collapse_seeds( return out_seeds, taken +# @profile def collapse_ims( ims_values, mz_values, @@ -836,7 +875,12 @@ def collapse_ims( ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} seeds = {k: v for k, v in ambiguous.items() if len(v) > 5} - out_seeds, taken = collapse_seeds(seeds=seeds, intensity_values=intensity_values) + if seeds: + out_seeds, taken = collapse_seeds( + seeds=seeds, intensity_values=intensity_values + ) + else: + out_seeds, taken = {}, set() ambiguous_untaken = list(set(ambiguous).difference(taken)) unambiguous_out = { diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 1ebb321..3277f8a 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -22,6 +22,7 @@ def ppm_to_delta_mass(obs: float, ppm: float) -> float: # Ported implementation from sage +# @profile def deisotope( mz: NDArray[np.float32] | list[float], inten: NDArray[np.float32] | list[float], diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 6eeb7e2..3a23394 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -283,8 +283,9 @@ def ms1_filter(pep: Peptide) -> Peptide | None: allow_modifications=False, out_hook=ms1_filter, ) - sequences = adapter.parse() - self.targets = list(sequences) + sequences = list(adapter.parse()) + assert len(sequences) == len({x.to_proforma() for x in sequences}) + self.targets = sequences def prefilter_ms1( self, ms1_range: tuple[float, float], num_decimals: int = 3 @@ -655,7 +656,7 @@ def score_arrays( precursor_mz: float | tuple[float, float], spec_mz: Iterable[float], spec_int: Iterable[float], - ) -> DataFrame: + ) -> DataFrame | None: """Scores a spectrum against the index. The result is a data frame containing all generic data required to @@ -807,7 +808,13 @@ def hyperscore( except AssertionError: # There is a bug that gets detected here where a single peptide gets # scored multiple times ... Usually with different IDs - breakpoint() + + # This issue happens when a sequence is also in the decoys + logger.error( + f"{scores} has multiple peptides with the " + "same id (ocasionally happens when it is both a " + "target and a decoy)" + ) return scores diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index c00effe..c85a5c0 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -7,7 +7,6 @@ import numpy as np import pandas as pd -import uniplot from joblib import Parallel, delayed from loguru import logger from pandas import DataFrame @@ -19,16 +18,7 @@ from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta from diadem.search.search_utils import make_pin - - -def plot_to_log(*args, **kwargs) -> None: # noqa - """Plot to log. - - Generates a plot of the passed data to the function. - All arguments are passed internally to uniplot.plot_to_string. - """ - for line in uniplot.plot_to_string(*args, **kwargs): - logger.debug(line) +from diadem.utils import plot_to_log # @profile @@ -67,6 +57,7 @@ def search_group( IMS_TOLERANCE = config.g_ims_tolerance # noqa IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa + MAX_NUM_CONSECUTIVE_FAILS = 50 new_window_kwargs = { "window": WINDOWSIZE, @@ -101,10 +92,12 @@ def search_group( # Fail related variables num_fails = 0 + num_consecutive_fails = 0 curr_highest_peak_int = 2**30 last_id = None pbar = tqdm(desc=f"Slice: {group.iso_window_name}", disable=not progress) + st = time.time() while True: if not (curr_highest_peak_int >= MIN_PEAK_INTENSITY and num_peaks <= MAX_PEAKS): @@ -117,8 +110,17 @@ def search_group( ) break + if num_consecutive_fails > MAX_NUM_CONSECUTIVE_FAILS: + logger.warning( + "Exiting with early termination due " + f"to consecurtive fails {num_consecutive_fails}" + ) + group_results = group_results[:-num_consecutive_fails] + break + new_stack: StackedChromatograms | TimsStackedChromatograms new_stack = group.get_highest_window(**new_window_kwargs) + if new_stack.base_peak_intensity < MIN_PEAK_INTENSITY: break @@ -159,7 +161,6 @@ def search_group( scores = None if scores is not None: - score_log.append(scores["Score"].max()) scores["id"] = match_id ref_peak_mz = new_stack.mzs[new_stack.ref_index] @@ -205,6 +206,8 @@ def search_group( plot_to_log([scaling], title="Scaling") group_results.append(scores.copy()) + score_log.append(scores["Score"].max()) + num_consecutive_fails = 0 else: logger.debug(f"{match_id} did not match any peptides, scaling and skipping") scaling = SCALING_RATIO * np.ones_like(new_stack.ref_trace) @@ -216,6 +219,7 @@ def search_group( window_mzs=new_stack.mzs, ) num_fails += 1 + num_consecutive_fails += 1 num_peaks += 1 pbar.set_postfix( @@ -227,10 +231,32 @@ def search_group( } ) pbar.update(1) - if (num_peaks % DEBUG_FREQUENCY) == 0: - logger.debug( - f"peak {num_peaks}/{MAX_PEAKS} max ; Intensity {curr_highest_peak_int}" + if ((et := time.time()) - st) >= 2: + from ms2ml.utils.mz_utils import get_tolerance + + tot_candidates = 0 + for mz in new_stack.mzs: + ms2_tol = get_tolerance( + MS2_TOLERANCE, + theoretical=mz, + unit=MS2_TOLERANCE_UNIT, + ) + + candidates = db.bucketlist.yield_candidates( + ms1_range=group.precursor_range, + ms2_range=(mz - ms2_tol, mz + ms2_tol), + ) + for _ in candidates: + tot_candidates += 1 + + logger.error( + f"Iteration took waaaay too long scores={scores} ;" + f" {tot_candidates} total candidates" ) + logger.error(f"{new_stack.mzs.copy()}; len({len(new_stack.mzs)})") + st = et + else: + st = et pbar.close() plot_to_log( @@ -245,7 +271,10 @@ def search_group( f"Intensity of the last scored peak {curr_highest_peak_int} " f"on index {last_id}" ) - group_results = pd.concat(group_results) + + if len(group_results) == 0: + logger.error("No results were accumulated in this group!") + group_results = pd.concat(group_results) if len(group_results) else None return group_results @@ -319,7 +348,9 @@ def setup_db_and_search( for group, prange in zip(groups, precursor_ranges) ) - results: pd.DataFrame = pd.concat(results, ignore_index=True) + results: pd.DataFrame = pd.concat( + [x for x in results if x is not None], ignore_index=True + ) prefix = out_prefix + ".diadem" if out_prefix else "diadem" Path(prefix).absolute().parent.mkdir(exist_ok=True) diff --git a/diadem/utils.py b/diadem/utils.py index d3c0cca..782b2fa 100644 --- a/diadem/utils.py +++ b/diadem/utils.py @@ -2,11 +2,22 @@ from contextlib import contextmanager import numpy as np +import uniplot from loguru import logger from ms2ml import Peptide from numpy.typing import NDArray +def plot_to_log(*args, **kwargs) -> None: # noqa + """Plot to log. + + Generates a plot of the passed data to the function. + All arguments are passed internally to uniplot.plot_to_string. + """ + for line in uniplot.plot_to_string(*args, **kwargs): + logger.debug(line) + + @contextmanager # @profile def disabled_gc() -> None: diff --git a/profiling/.gitignore b/profiling/.gitignore index 055cf29..397a8b5 100644 --- a/profiling/.gitignore +++ b/profiling/.gitignore @@ -1,6 +1,11 @@ diadem.csv diadem.parquet +**diadem.csv +**diadem.tsv.pin +**diadem.parquet profiling_data/* line_profile*.txt profile*.txt + +**mokapot*.txt diff --git a/profiling/lineprofile_dia.zsh b/profiling/lineprofile_dia.zsh index 1f6aab8..af43837 100644 --- a/profiling/lineprofile_dia.zsh +++ b/profiling/lineprofile_dia.zsh @@ -3,7 +3,7 @@ # what you want profiled mkdir -p lineprofile_results sed -ie "s/# @profile/@profile/g" ../diadem/**/*.py -python -m pip install ../. +python -m pip install -e ../. sed -ie "s/@profile/# @profile/g" ../diadem/**/*.py set -x diff --git a/profiling/lineprofile_timstof.zsh b/profiling/lineprofile_timstof.zsh new file mode 100644 index 0000000..463df20 --- /dev/null +++ b/profiling/lineprofile_timstof.zsh @@ -0,0 +1,26 @@ + + + +# This one does need you to go into the code and decorate with @profile +# what you want profiled +mkdir -p lineprofile_results +sed -ie "s/# @profile/@profile/g" ../diadem/**/*.py +python -m pip install "../.[test,profiling,dev]" +sed -ie "s/@profile/# @profile/g" ../diadem/**/*.py + +set -x +set -e + +# PYTHONOPTIMIZE=1 +mkdir -p lineprofile_results_tims +DEBUG_DIADEM=1 python -m kernprof -l run_profile_tims.py +python -m line_profiler run_profile_tims.py.lprof > "line_profile_tims_$(date '+%Y%m%d_%H%M').txt" +python -m line_profiler run_profile_tims.py.lprof > "line_profile_tims_latest.txt" +python -m pip install -e "../.[test,profiling,dev]" + + +R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/raw_td_plot.png", plot = g)' +R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_tims/iter_score_plot.png", plot = g)' + +mokapot lineprofile_results_tims/results.diadem.tsv.pin --test_fdr 0.05 --keep_decoys -d lineprofile_results_tims +R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/mokapot.peptides.txt") ; foo2 = readr::read_tsv("mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/td_plot.png", plot = g)' diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 1787560..935d00b 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -4,7 +4,13 @@ cli.setup_logger() cli.diadem_main( fasta_path="./profiling_data/UP000000625_83333.fasta", - data_path="./profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d", - config=DiademConfig(run_parallelism=1), + data_path="./profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf", + config=DiademConfig( + run_parallelism=3, + run_max_peaks=20000, + run_allowed_fails=500, + g_tolerances=(25, 25), + g_tolerance_units=("ppm", "ppm"), + ), out_prefix="lineprofile_results_tims/results", ) From 7581b30447b38e20bea784cb2df909e0c66cf7df Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 9 Mar 2023 11:09:31 -0800 Subject: [PATCH 03/41] (wip) fixed bug where empty spectra would crash the program --- diadem/data_io/mzml.py | 9 +++++++-- diadem/data_io/timstof.py | 11 +++-------- profiling/run_profile_tims.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index c62e5d8..2e79847 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -150,8 +150,13 @@ def scale_window_intensities( self.mzs[i] = self.mzs[i][np.invert(int_remove)] if hasattr(self, "imss"): self.imss[i] = self.imss[i][np.invert(int_remove)] - self.base_peak_int[i] = np.max(self.intensities[i]) - self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] + + if len(self.intensities[i]): + self.base_peak_int[i] = np.max(self.intensities[i]) + self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] + else: + self.base_peak_int[i] = -1 + self.base_peak_mz[i] = -1 def __len__(self) -> int: """Returns the number of spectra in the group.""" diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index d138f30..ab3000c 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -324,10 +324,8 @@ def __init__(self, filepath: PathLike, config: DiademConfig) -> None: unique_precursor_indices = [x for x in unique_precursor_indices if x != 0] if "DEBUG_DIADEM" in os.environ: - logger.error( - "RUNNING DIADEM IN DEBUG MODE (only the second precursor index)" - ) - self.unique_precursor_indices = unique_precursor_indices[2:3] + logger.error("RUNNING DIADEM IN DEBUG MODE (only the 4th precursor index)") + self.unique_precursor_indices = unique_precursor_indices[3:4] else: self.unique_precursor_indices = unique_precursor_indices @@ -430,6 +428,7 @@ def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGro yield r +# @profile def find_neighbors_mzsort( ims_vals: NDArray[np.float64], sorted_mz_values: NDArray[np.float64], @@ -721,10 +720,6 @@ def get_break_indices( >>> get_break_indices(tmp) (array([0, 4, 7, 9]), array([ 1, 7, 11, 13])) """ - if "DEBUG_DIADEM" in os.environ: - logger.error("DEBUG MODE IN DIADEM, using only 1/4 of the spectra.") - inds = inds[: int(len(inds) / 4)] - breaks = 1 + np.where(np.diff(inds) > 1)[0] breaks = np.concatenate([np.array([0]), breaks, np.array([inds.size - 1])]) diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 935d00b..b125b0e 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -6,7 +6,7 @@ fasta_path="./profiling_data/UP000000625_83333.fasta", data_path="./profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf", config=DiademConfig( - run_parallelism=3, + run_parallelism=1, # Needs to use 1 core for profiling run_max_peaks=20000, run_allowed_fails=500, g_tolerances=(25, 25), From fd8d2e1740d737f31add00f7abdf9a708079e940 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 13 Mar 2023 07:08:28 -0700 Subject: [PATCH 04/41] (wip) further improvememet on timstof data --- diadem/data_io/timstof.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index ab3000c..10638f4 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -25,6 +25,7 @@ from diadem.data_io.utils import slice_from_center, xic from diadem.deisotoping import deisotope from diadem.search.metrics import get_ref_trace_corrs +from diadem.utils import is_sorted IMSError = Literal["abs", "pct"] @@ -137,6 +138,7 @@ def from_group( center_mzs = center_mzs[ims_keep] center_intensities = center_intensities[ims_keep] + assert is_sorted(center_mzs) # TODO move this to its own helper function (collapse_unique ??) # ... Maybe even "proprocess_ims_spec(mzs, imss, ints, ref_ims, ...)" @@ -148,10 +150,12 @@ def from_group( len(u_center_mzs), dtype=center_intensities.dtype ) np.add.at(u_center_intensities, inv, center_intensities) + assert is_sorted(u_center_mzs) # After makig it unique, we deisotope the spectrum # after this, getting the indices that generated du_center_mzs[0] # would be np.where(np.isin(inv, du_center_indices[0])) + du_center_mzs, du_center_intensities, du_center_indices = deisotope( u_center_mzs, u_center_intensities, @@ -167,6 +171,7 @@ def from_group( du_center_mzs[int_keep], du_center_intensities[int_keep], ) + assert is_sorted(du_center_mzs) xic_outs = [] @@ -192,6 +197,7 @@ def from_group( track_indices=True, ) + assert is_sorted(du_mzs) outs, inds = xic( query_mz=du_mzs, query_int=du_intensities, @@ -480,19 +486,17 @@ def find_neighbors_mzsort( else: raise ValueError("Only 'Da' and 'ppm' values are supported as mass errors") - if intensities is not None: - top_n = int(max(len(intensities) * top_n_pct, top_n)) - if len(intensities) > top_n: - top_indices = np.argpartition(intensities, -top_n)[-top_n:] - else: - intensities = None + top_n = int(max(len(intensities) * top_n_pct, top_n)) + if len(intensities) > top_n: + top_indices = np.argpartition(intensities, -top_n)[-top_n:] + else: + intensities = None opts = {} for i1, (ims1, mz1) in enumerate(zip(ims_vals, sorted_mz_values)): - if intensities is not None: - if i1 not in top_indices: - opts.setdefault(i1, []).append(i1) - continue + if i1 not in top_indices: + opts.setdefault(i1, []).append(i1) + continue candidates = np.where(np.abs(sorted_mz_values - mz1) <= mz_tol)[0] tmp_ims = ims_vals[candidates] @@ -647,7 +651,17 @@ def non_max_suppression_fast(boxes, overlapThresh, eps=1e-5, invert_scores=False return pick -def bundle_neighbors_mzsorted(ims_values, sorted_mz_values, intensities, top_n=500): +def bundle_neighbors_mzsorted( + ims_values: NDArray[np.float32], + sorted_mz_values: NDArray[np.float32], + intensities: NDArray[np.float32], + top_n: int = 500, +): + """ + + WARNING: + Output is not necessarily sorted!!! + """ BOX_EPS = 1e-7 opts = find_neighbors_mzsort( From e6518da1f42486f8da57b12dfb208ea84521a302 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 16 Mar 2023 15:31:31 -0500 Subject: [PATCH 05/41] (wip) changed location for deisotoping --- .pre-commit-config.yaml | 6 +- README.md | 26 +++++- diadem/config.py | 4 + diadem/data_io/mzml.py | 3 +- diadem/data_io/timstof.py | 142 ++++++++++++++++--------------- diadem/data_io/utils.py | 2 +- diadem/deisotoping.py | 125 ++++++++++++++++++++++++--- diadem/index/fragment_buckets.py | 21 ++++- diadem/index/indexed_db.py | 46 ++++++---- diadem/search/diadem.py | 28 ++++-- profiling/get_data.bash | 8 +- 11 files changed, 296 insertions(+), 115 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2507587..9fd1550 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,17 +8,17 @@ repos: - id: trailing-whitespace - id: detect-private-key - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black language_version: python3.9 - repo: https://github.com/pycqa/isort - rev: 5.11.4 + rev: 5.12.0 hooks: - id: isort name: isort (python) - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.228 + rev: v0.0.256 hooks: - id: ruff args: ['--fix'] diff --git a/README.md b/README.md index 6ece584..9dd9711 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,35 @@ + +![GitHub](https://img.shields.io/github/license/TalusBio/diadem?style=flat-square) +![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/talusbio/diadem?style=flat-square) + + # diadem A feature-centric DIA search engine +## Current stage + +Under development. + +## Installation + +```shell +git clone git@github.com:TalusBio/diadem.git +cd diadem +pip install . +# pip install -e ".[dev,profiling,test]" # for development install +``` + +## Usage + +```shell +``` + + ## Release milestones - [x] Search prototype -- [ ] Stable search engine (more as in -) +- [-] Stable search engine (more as in -) - [ ] Quantification module - [ ] Stable quant module - [ ] RT alignment module diff --git a/diadem/config.py b/diadem/config.py index 9c76fa0..38fcf8f 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -148,6 +148,10 @@ def ms2ml_config(self) -> Config: @dataclass(frozen=True, eq=True) class DiademConfig(DiademIndexConfig): # noqa + # TODO split tolerances in 'within spectrum' and 'between spectra' + # since tolerances for deisotoping should be a lot lower than they should be + # for database matching ... 5ppm for a database match is ok, 1 ppm for + # an isotope envelope is barely acceptable. g_tolerances: tuple[float, ...] = field(default=(20, 20)) g_tolerance_units: tuple[MassError, ...] = field(default=("ppm", "ppm")) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 2e79847..68f1fa7 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -1,9 +1,9 @@ from __future__ import annotations import os +from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path -from typing import Iterator import numpy as np from joblib import Parallel, delayed @@ -144,6 +144,7 @@ def scale_window_intensities( else: continue + # TODO this is hard-coded right now, change as a param int_remove = self.intensities[i] < 10 if np.any(int_remove): self.intensities[i] = self.intensities[i][np.invert(int_remove)] diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 10638f4..f82c467 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -1,11 +1,12 @@ from __future__ import annotations import os +from collections.abc import Iterator from contextlib import contextmanager from dataclasses import dataclass from itertools import chain from os import PathLike -from typing import Iterator, Literal +from typing import Literal import numpy as np from alphatims.bruker import TimsTOF @@ -23,7 +24,7 @@ StackedChromatograms, ) from diadem.data_io.utils import slice_from_center, xic -from diadem.deisotoping import deisotope +from diadem.deisotoping import deisotope_with_ims from diadem.search.metrics import get_ref_trace_corrs from diadem.utils import is_sorted @@ -152,27 +153,6 @@ def from_group( np.add.at(u_center_intensities, inv, center_intensities) assert is_sorted(u_center_mzs) - # After makig it unique, we deisotope the spectrum - # after this, getting the indices that generated du_center_mzs[0] - # would be np.where(np.isin(inv, du_center_indices[0])) - - du_center_mzs, du_center_intensities, du_center_indices = deisotope( - u_center_mzs, - u_center_intensities, - max_charge=3, - diff=mz_tolerance, - unit=mz_tolerance_unit, - track_indices=True, - ) - int_keep = du_center_intensities >= ( - du_center_intensities.max() * min_intensity_ratio - ) - du_center_mzs, du_center_intensities = ( - du_center_mzs[int_keep], - du_center_intensities[int_keep], - ) - assert is_sorted(du_center_mzs) - xic_outs = [] for i, (m, inten, ims) in enumerate(zip(mzs, intensities, imss)): @@ -188,20 +168,10 @@ def from_group( u_intensities = np.zeros(len(u_mzs), dtype=inten.dtype) np.add.at(u_intensities, inv, inten) - du_mzs, du_intensities, du_indices = deisotope( - u_mzs, - u_intensities, - max_charge=3, - diff=mz_tolerance, - unit=mz_tolerance_unit, - track_indices=True, - ) - - assert is_sorted(du_mzs) outs, inds = xic( - query_mz=du_mzs, - query_int=du_intensities, - mzs=du_center_mzs, + query_mz=u_mzs, + query_int=u_intensities, + mzs=u_center_mzs, tolerance=mz_tolerance, tolerance_unit=mz_tolerance_unit, ) @@ -213,7 +183,7 @@ def from_group( for y in inds: if len(y) > 0: collapsed_indices = np.concatenate( - [np.where(np.isin(inv, du_indices[w]))[0] for w in y] + [np.where(inv == w)[0] for w in y] ) out_inds.append(np.unique(t_int_keep[collapsed_indices])) else: @@ -221,7 +191,7 @@ def from_group( xic_outs.append((outs, out_inds)) if i == center_index: - assert xic_outs[-1][0].sum() >= du_center_intensities.max() + assert xic_outs[-1][0].sum() >= u_center_intensities.max() stacked_arr = np.stack([x[0] for x in xic_outs], axis=-1) @@ -241,8 +211,8 @@ def from_group( keep = corrs >= max(min_correlation, max_peak_corr) stacked_arr = stacked_arr[..., keep, ::1] - du_center_mzs = du_center_mzs[keep] - du_center_intensities = du_center_intensities[keep] + u_center_mzs = u_center_mzs[keep] + u_center_intensities = u_center_intensities[keep] indices = [[y for y, k in zip(x, keep) if k] for x in indices] ref_id = np.argmax(stacked_arr[..., center_index]) @@ -250,12 +220,12 @@ def from_group( out = TimsStackedChromatograms( array=stacked_arr, - mzs=du_center_mzs, + mzs=u_center_mzs, ref_index=ref_id, parent_index=index, base_peak_intensity=bp_int, stack_peak_indices=indices, - center_intensities=du_center_intensities, + center_intensities=u_center_intensities, ref_ims=bp_ims, ) return out @@ -361,10 +331,15 @@ def _precursor_iso_window_groups( imss = [] for vi in v: - new_order = np.argsort(vi["mz"]) - mzs.append(vi["mz"][new_order]) - ints.append(vi["intensity"][new_order]) - imss.append(vi["ims"][new_order]) + if len(vi["mz"]) > 0: + new_order = np.argsort(vi["mz"]) + mzs.append(vi["mz"][new_order]) + ints.append(vi["intensity"][new_order]) + imss.append(vi["ims"][new_order]) + else: + mzs.append(vi["mz"]) + ints.append(vi["intensity"]) + imss.append(vi["ims"]) # TODO change this to a data structure that stores # this only once. @@ -436,9 +411,9 @@ def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGro # @profile def find_neighbors_mzsort( - ims_vals: NDArray[np.float64], - sorted_mz_values: NDArray[np.float64], - intensities: NDArray[np.float64], + ims_vals: NDArray[np.float32], + sorted_mz_values: NDArray[np.float32], + intensities: NDArray[np.float32], top_n: int = 500, top_n_pct: float = 0.1, ims_tol: float = 0.02, @@ -452,11 +427,11 @@ def find_neighbors_mzsort( Arguments: --------- - ims_vals: NDArray[np.float64] + ims_vals: NDArray[np.float32] Array containing the ion mobility values of the precursor. - sorted_mz_values: NDArray[np.float64] + sorted_mz_values: NDArray[np.float32] Sorted array contianing the mz values - intensities: NDArray[np.float64] + intensities: NDArray[np.float32] Array containing the intensities of the peaks top_n : int Number of peaks to use as seeds for neighborhood finding, @@ -480,7 +455,7 @@ def find_neighbors_mzsort( if mz_tol_unit.lower() == "da": pass elif mz_tol_unit.lower() == "ppm": - mz_tol: NDArray[np.float64] = get_tolerance( + mz_tol: NDArray[np.float32] = get_tolerance( mz_tol, theoretical=sorted_mz_values, unit="ppm" ) else: @@ -490,11 +465,11 @@ def find_neighbors_mzsort( if len(intensities) > top_n: top_indices = np.argpartition(intensities, -top_n)[-top_n:] else: - intensities = None + top_indices = None opts = {} for i1, (ims1, mz1) in enumerate(zip(ims_vals, sorted_mz_values)): - if i1 not in top_indices: + if top_indices is None or i1 not in top_indices: opts.setdefault(i1, []).append(i1) continue @@ -513,7 +488,7 @@ def find_neighbors_mzsort( def propose_boxes( intensities: NDArray[np.float32], ims_values: NDArray[np.float32], - mz_values: NDArray[np.float64], + mz_values: NDArray[np.float32], ref_ims: float, ref_mz: float, mz_sizes=(0.02,), @@ -657,10 +632,9 @@ def bundle_neighbors_mzsorted( intensities: NDArray[np.float32], top_n: int = 500, ): - """ - - WARNING: - Output is not necessarily sorted!!! + """Warning: + ------- + Output is not necessarily sorted!!!. """ BOX_EPS = 1e-7 @@ -747,6 +721,9 @@ def _get_precursor_index_windows( PRELIM_N_PEAK_FILTER = 10_000 # noqa MIN_INTENSITY_KEEP = 100 # noqa MIN_NUM_SEED_BOXES = 1000 # noqa + IMS_TOL = 0.01 + MZ_TOL = 0.01 + MAX_ISOTOPE_CHARGE = 2 inds = dia_data[{"precursor_indices": precursor_index}, "raw"] breaks, break_inds = get_break_indices(inds=inds) @@ -798,10 +775,29 @@ def _get_precursor_index_windows( mz_values=peak_data["mz_values"], intensity_values=peak_data["corrected_intensity_values"], top_n=MIN_NUM_SEED_BOXES, + ims_tol=IMS_TOL, + mz_tol=MZ_TOL, ) filter = f["intensity"] > MIN_INTENSITY_KEEP f = {k: v[filter] for k, v in f.items()} - pct_compression = round(len(f["mz"]) / start_len, ndigits=2) + + d_out = {"mz": None, "intensity": None, "ims": None} + if len(f["mz"]) < 0: + new_order = np.argsort(f["mz"]) + f = {k: v[new_order] for k, v in f.items()} + + d_out["mz"], d_out["intensity"], d_out["ims"] = deisotope_with_ims( + ims_diff=IMS_TOL, + ims_unit="abs", + imss=f["ims"], + inten=f["intensity"], + mz=f["mz"], + mz_unit="da", + track_indices=False, + mz_diff=MZ_TOL, + max_charge=MAX_ISOTOPE_CHARGE, + ) + pct_compression = round(len(d_out["mz"]) / start_len, ndigits=2) final_len = len(f["intensity"]) pbar.set_postfix( { @@ -810,13 +806,13 @@ def _get_precursor_index_windows( "pct": pct_compression, "min": np.min(peak_data["corrected_intensity_values"]), "max": np.max(peak_data["corrected_intensity_values"]), - "f_min": int(np.min(f["intensity"])) if final_len else 0, - "f_max": int(np.max(f["intensity"])) if final_len else 0, + "f_min": int(np.min(d_out["intensity"])) if final_len else 0, + "f_max": int(np.max(d_out["intensity"])) if final_len else 0, }, refresh=False, ) - curr_push_data.update(f) + curr_push_data.update(d_out) quad_name = ( f"{curr_push_data['quad_low_mz_values']:.6f}," f" {curr_push_data['quad_high_mz_values']:.6f}" @@ -827,7 +823,7 @@ def _get_precursor_index_windows( # @profile def collapse_seeds( - seeds: dict[int, list[int]], intensity_values: NDArray[np.float64] + seeds: dict[int, list[int]], intensity_values: NDArray[np.float32] ) -> tuple[dict[int, int], set[int]]: seeds = seeds.copy() seed_keys = list(seeds.keys()) @@ -869,7 +865,17 @@ def collapse_ims( top_n_pct=0.2, ims_tol=0.01, mz_tol=0.01, -) -> dict[str, NDArray[np.float64]]: +) -> dict[str, NDArray[np.float32]]: + """Sample output + ------------- + ``` + bundled = { + "ims": np.zeros(..., dtype=np.float32), + "mz": np.zeros(..., dtype=np.float32), + "intensity": np.zeros(..., dtype=np.float32), + } + ```. + """ neighborhoods = find_neighbors_mzsort( ims_vals=ims_values, sorted_mz_values=mz_values, @@ -899,8 +905,8 @@ def collapse_ims( } bundled = { - "ims": np.zeros(len(out_seeds), dtype=np.float64), - "mz": np.zeros(len(out_seeds), dtype=np.float64), + "ims": np.zeros(len(out_seeds), dtype=np.float32), + "mz": np.zeros(len(out_seeds), dtype=np.float32), "intensity": np.zeros(len(out_seeds), dtype=np.float32), } for i, v in enumerate(out_seeds.values()): diff --git a/diadem/data_io/utils.py b/diadem/data_io/utils.py index 7eb3dca..4de178a 100644 --- a/diadem/data_io/utils.py +++ b/diadem/data_io/utils.py @@ -1,4 +1,4 @@ -from typing import Iterable +from collections.abc import Iterable import numpy as np from ms2ml.utils.mz_utils import annotate_peaks diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 3277f8a..c55da1a 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -33,7 +33,7 @@ def deisotope( ) -> tuple[NDArray[np.float32], NDArray[np.float32]]: """Deisotopes the passed spectra. - TE MZS NEED TO BE SORTED!! + THE MZS NEED TO BE SORTED!! Parameters ---------- @@ -97,13 +97,116 @@ def mass_unit_fun(x: float, y: float) -> float: peaks[i]["envelope"] = j j -= 1 - peaks = _filter_peaks(peaks) if not track_indices: - peaks = peaks[0:2] + peaks = _filter_peaks(peaks, extract=("mz", "intensity")) + else: + peaks = _filter_peaks(peaks, extract=("mz", "intensity", "indices")) + return peaks + + +# TODO this is a function prototype, if it works abstract it with the prototype +# @profile +def deisotope_with_ims( + mz: NDArray[np.float32] | list[float], + inten: NDArray[np.float32] | list[float], + imss: NDArray[np.float32] | list[float], + max_charge: int, + mz_diff: float, + mz_unit: str, + ims_diff: float, + ims_unit: float, + track_indices: bool = False, +) -> tuple[NDArray[np.float32], NDArray[np.float32]]: + """Deisotopes the passed spectra. + + THE MZS NEED TO BE SORTED!! + + Parameters + ---------- + mz, list[float] + A list of the mz values to be deisotoped. + inten, list[float] + A list of the intensities that correspond in order to the elements in mz. + max_charge, int + Maximum charge to look for in the isotopes. + diff, float + Tolerance to use when searching (typically 20 for ppm or 0.02 for da) + unit, str + Unit for the diff. ppm or da + track_indices, bool + Whether to return the indices of the combined indices as well. + + Examples + -------- + >>> my_mzs = [800.9, 803.408, 803.409, 804.4108, 804.4109, 805.4106] + >>> my_imss = [0.7, 0.7, 0.8, 0.7, 0.8, 0.7] + >>> my_intens = [1-(0.1*i) for i,_ in enumerate(my_mzs)] + >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs") + (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), (0.7, 0.7, 0.8)) + >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs", track_indices=True) + (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), (0.7, 0.7, 0.8), ([0], [1, 3, 5], [2, 4])) + """ + if mz_unit.lower() == "da": + + def mass_unit_fun(x: float, y: float) -> float: + return y + + elif mz_unit.lower() == "ppm": + mass_unit_fun = ppm_to_delta_mass + else: + raise NotImplementedError("Masses need to be either 'da' or 'ppm'") + + if ims_unit.lower() == "abs": + + def ims_unit_fun(x: float, y: float) -> float: + return y + + else: + raise NotImplementedError("only abs is supported as an IMS difference") + + peaks = [(mz, intensity, ims) for mz, intensity, ims in zip(mz, inten, imss)] + peaks = [ + {"mz": mz, "ims": ims, "intensity": intensity, "envelope": None, "charge": None} + for mz, intensity, ims in peaks + ] + for i in range(len(peaks)): + peaks[i]["indices"] = [i] + + for i in range(len(mz) - 1, -1, -1): + j = i - 1 + while j >= 0 and mz[i] - mz[j] <= NEUTRON + mass_unit_fun(mz[i], mz_diff): + delta = mz[i] - mz[j] + ims_delta = imss[i] - imss[j] + mz_tol = mass_unit_fun(mz[i], mz_diff) + ims_tol = ims_unit_fun(imss[i], ims_diff) + matches_ims = abs(ims_delta) <= ims_tol + if matches_ims: + for charge in range(1, max_charge + 1): + iso = NEUTRON / charge + # Note, this assumes that the isotope envelope always decreases in + # intensity, which is not accurate for high mol weight fragments. + matches_mz = abs(delta - iso) <= mz_tol + if matches_mz and inten[i] < inten[j]: + peaks[j]["intensity"] += peaks[i]["intensity"] + if track_indices: + peaks[j]["indices"].extend(peaks[i]["indices"]) + if peaks[i]["charge"] and peaks[i]["charge"] != charge: + continue + peaks[j]["charge"] = charge + peaks[i]["charge"] = charge + peaks[i]["envelope"] = j + j -= 1 + + if not track_indices: + peaks = _filter_peaks(peaks, extract=("mz", "intensity", "ims")) + else: + peaks = _filter_peaks(peaks, extract=("mz", "intensity", "ims", "indices")) return peaks -def _filter_peaks(peaks: dict) -> tuple[NDArray[np.float32], NDArray[np.float32]]: +def _filter_peaks( + peaks: dict, extract: tuple[str] +) -> tuple[NDArray[np.float32] | list, ...]: """Filters peaks to remove isotope envelopes. When passed a list of dictionaries that look like this: @@ -111,11 +214,13 @@ def _filter_peaks(peaks: dict) -> tuple[NDArray[np.float32], NDArray[np.float32] It filters the ones that are not assigned to be in an envelope, thus keeping only monoisotopic peaks. """ - peaktuples = [ - (x["mz"], x["intensity"], x["indices"]) for x in peaks if x["envelope"] is None - ] + peaktuples = [[x[y] for y in extract] for x in peaks if x["envelope"] is None] if len(peaktuples) == 0: - mzs, ints, inds = [], [], [] + out_tuple = tuple([] for _ in extract) else: - mzs, ints, inds = zip(*peaktuples) - return np.array(mzs), np.array(ints), inds + out_tuple = zip(*peaktuples) + out_tuple = tuple( + np.array(x) if y in ["mz", "intensity", "ims"] else x + for x, y in zip(out_tuple, extract) + ) + return out_tuple diff --git a/diadem/index/fragment_buckets.py b/diadem/index/fragment_buckets.py index 42bd56b..b75b932 100644 --- a/diadem/index/fragment_buckets.py +++ b/diadem/index/fragment_buckets.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from dataclasses import dataclass -from typing import Iterator, Literal +from typing import Literal import numpy as np from numpy.typing import NDArray @@ -152,12 +153,14 @@ def concatenate(cls, *args: FragmentBucket) -> FragmentBucket: assert ( len(sorting_level) == 1 ), "Cannot concatenate buckets with different sorting levels" + if len(args) == 1: + return args[0] return cls( fragment_mzs=np.concatenate([x.fragment_mzs for x in args]), fragment_series=np.concatenate([x.fragment_series for x in args]), precursor_ids=np.concatenate([x.precursor_ids for x in args]), precursor_mzs=np.concatenate([x.precursor_mzs for x in args]), - is_sorted=True, + is_sorted=False, sorting_level=sorting_level.pop(), ) @@ -365,7 +368,9 @@ def __init__( for k, v in unpacked.items(): self.buckets[k].append(v) - iterator = enumerate(tqdm(self.buckets, disable=not progress)) + iterator = enumerate( + tqdm(self.buckets, disable=not progress, desc="Concatenating buckets") + ) # This gets progressively updated with the minimum bucket size. min_ms2_mz = 2**15 @@ -383,6 +388,7 @@ def __init__( self.min_ms2_mz = min_ms2_mz + # @profile def unpack_bucket(self, bucket: FragmentBucket) -> dict[int, FragmentBucket]: """Unpacks a bucket into a dictionary of buckets. @@ -392,11 +398,18 @@ def unpack_bucket(self, bucket: FragmentBucket) -> dict[int, FragmentBucket]: """ # TODO decide if this should be a fragment bucket method... integerized = (bucket.fragment_mzs * self.prod_num).astype(int) + sorted_lookup = len(integerized > 1000) and is_sorted(integerized) uniqs = np.unique(integerized) out = {} for u in uniqs: - idxs = integerized == u + if sorted_lookup: + idxs = slice( + np.searchsorted(integerized, u, "left"), + np.searchsorted(integerized, u, "right"), + ) + else: + idxs = integerized == u # TODO consider here not traking precursor mzs anymore out[u] = FragmentBucket( diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 3a23394..a7ea53c 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -2,10 +2,10 @@ import copy from collections import namedtuple +from collections.abc import Iterable, Iterator from functools import lru_cache from os import PathLike from pathlib import Path -from typing import Iterable, Iterator import numpy as np import pandas as pd @@ -426,6 +426,7 @@ def _dump_peptides_parquet( "mz": [], "ion_series": [], "seq_id": [], + "precursor_mz": [], } append = False @@ -439,7 +440,10 @@ def _dump_peptides_parquet( seq_chunk["seq_proforma"].append(prec_seqs) seq_chunk["decoy"].append(decoy) - for x, y, z in zip(frag_mzs, ion_series, [seq_id] * num_frags): + for w, x, y, z in zip( + [prec_mzs] * num_frags, frag_mzs, ion_series, [seq_id] * num_frags + ): + frag_chunk["precursor_mz"].append(float(w)) frag_chunk["mz"].append(float(x)) frag_chunk["ion_series"].append(y) frag_chunk["seq_id"].append(z) @@ -451,6 +455,9 @@ def _dump_peptides_parquet( write_parquet( fragment_file_path, pd.DataFrame(frag_chunk), append=append ) + + # This just flushes the chunk so next iteration starts + # clean. for x in seq_chunk: seq_chunk[x] = [] for x in frag_chunk: @@ -836,15 +843,12 @@ def index_prefiltered_from_parquet( logger.info( f"Filtering ms1 ranges {min_mz} to {max_mz} in database {self.name}" ) - chunk_seq_df = seqs_df.filter(pl.col("seq_mz") >= min_mz).filter( - pl.col("seq_mz") < max_mz - ) + joint_frags = ( - frags_df.join( - chunk_seq_df.select(["seq_id", "seq_mz"]), on="seq_id", how="inner" - ) + frags_df.filter(pl.col("precursor_mz") >= min_mz) + .filter(pl.col("precursor_mz") < max_mz) + # .unique(maintain_order=False) .sort(pl.col("mz")) - .unique() .collect() ) @@ -852,27 +856,35 @@ def index_prefiltered_from_parquet( out.bucketlist = PrefilteredMS1BucketList( [ FragmentBucket( - fragment_mzs=joint_frags["mz"].to_numpy(), + fragment_mzs=joint_frags["mz"].to_numpy().astype(np.float32), fragment_series=joint_frags["ion_series"].to_numpy(), precursor_ids=joint_frags["seq_id"].to_numpy(), - precursor_mzs=joint_frags["seq_mz"].to_numpy(), + precursor_mzs=joint_frags["precursor_mz"] + .to_numpy() + .astype(np.float32), sorting_level="ms2", is_sorted=True, ) ], num_decimal=2, max_frag_mz=2000, + progress=True, ) out.prefiltered_ms1 = True # TODO change it so the only required section # is the proforma seqs that are in the mz range - chunk_seq_df_coll = chunk_seq_df.sort(pl.col("seq_id")).unique().collect() - out.seq_prec_mzs = chunk_seq_df_coll["seq_mz"].to_numpy() - out.seqs = chunk_seq_df_coll["seq_proforma"].to_numpy() - out.seq_ids = chunk_seq_df_coll["seq_id"].to_numpy() - - target_set = chunk_seq_df.select(["seq_proforma", "decoy"]).collect() + chunk_seq_df = ( + seqs_df.filter(pl.col("seq_mz") >= min_mz) + .filter(pl.col("seq_mz") < max_mz) + .unique(maintain_order=False) + .sort(pl.col("seq_id")) + ).collect() + out.seq_prec_mzs = chunk_seq_df["seq_mz"].to_numpy().astype(np.float32) + out.seqs = chunk_seq_df["seq_proforma"].to_numpy() + out.seq_ids = chunk_seq_df["seq_id"].to_numpy() + + target_set = chunk_seq_df.select(["seq_proforma", "decoy"]) target_set = set( target_set["seq_proforma"].to_numpy()[ np.invert(target_set["decoy"].to_numpy()) diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index c85a5c0..cae8b5f 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -190,17 +190,27 @@ def search_group( if (num_peaks % DEBUG_FREQUENCY) == 0: before = new_stack.ref_trace.copy() - s = stack_getter( - group=group, - index=new_stack.parent_index, - **new_window_kwargs, - ) - plot_to_log( - [before, s.ref_trace], - title=( + try: + s = stack_getter( + group=group, + index=new_stack.parent_index, + **new_window_kwargs, + ) + plot_vals = [before, s.ref_trace] + title = ( f"Window before ({new_stack.ref_mz}) " f"m/z and after ({s.ref_mz}) m/z" - ), + ) + except ValueError: + # This happens when all peaks are removed + # ie: all in the spectrum matched a peptide + # and were removed + plot_vals = [before] + title = f"Window before ({new_stack.ref_mz}) m/z and after (None)" + + plot_to_log( + plot_vals, + title=title, lines=True, ) plot_to_log([scaling], title="Scaling") diff --git a/profiling/get_data.bash b/profiling/get_data.bash index 0551934..cdcbf57 100644 --- a/profiling/get_data.bash +++ b/profiling/get_data.bash @@ -5,9 +5,15 @@ aws s3 cp --profile mfa s3://data-pipeline-mzml-bucket/221229_ChessFest_Plate3/C aws s3 cp --profile mfa s3://data-pipeline-metadata-bucket/uniprot_human_sp_canonical_2021-11-19_crap.fasta ./profiling_data/. curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_PASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_PASEF.d.zip curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_diaPASEF.d.zip -gunzip ./profiling_data/*.gz +curl https://ftp.pride.ebi.ac.uk/pride/data/archive/2021/07/PXD027359/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.d.zip --output ./profiling_data/hela_5min_diaPASEF.d.zip +# gunzip ./profiling_data/*.gz for i in ./profiling_data/*.zip ; do unzip $i ; done mv LFQ_timsTOFPro_* ./profiling_data/. +mv 20210510_TIMS03_EVO03* ./profiling_data/. + +alphatims export hdf profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.d curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Bacteria/UP000000625/UP000000625_83333.fasta.gz --output ./profiling_data/UP000000625_83333.fasta.gz gunzip ./profiling_data/UP000000625_83333.fasta.gz +curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Eukaryota/UP000005640/UP000005640_9606.fasta.gz --output ./profiling_data/UP000005640_9606.fasta.gz +gunzip ./profiling_data/UP000005640_9606.fasta.gz From ec45f9819e4847f54bbc4f4d68e6d1b22ba351cc Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 22 Mar 2023 12:12:06 -0700 Subject: [PATCH 06/41] (wip) adding precursor information to the searches --- .dvc/.gitignore | 3 + .dvc/config | 0 .dvcignore | 3 + diadem/data_io/mzml.py | 217 ++++++++++---- diadem/data_io/timstof.py | 470 +++++++++++-------------------- diadem/deisotoping.py | 50 +++- diadem/index/fragment_buckets.py | 30 +- diadem/index/indexed_db.py | 133 ++++++--- diadem/search/diadem.py | 80 +++--- profiling/run_profile_tims.py | 6 +- pyproject.toml | 2 +- 11 files changed, 539 insertions(+), 455 deletions(-) create mode 100644 .dvc/.gitignore create mode 100644 .dvc/config create mode 100644 .dvcignore diff --git a/.dvc/.gitignore b/.dvc/.gitignore new file mode 100644 index 0000000..528f30c --- /dev/null +++ b/.dvc/.gitignore @@ -0,0 +1,3 @@ +/config.local +/tmp +/cache diff --git a/.dvc/config b/.dvc/config new file mode 100644 index 0000000..e69de29 diff --git a/.dvcignore b/.dvcignore new file mode 100644 index 0000000..5197305 --- /dev/null +++ b/.dvcignore @@ -0,0 +1,3 @@ +# Add patterns of files dvc should ignore, which could improve +# the performance. Learn more at +# https://dvc.org/doc/user-guide/dvcignore diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 68f1fa7..4fe5972 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -10,7 +10,6 @@ from loguru import logger from ms2ml import Spectrum from ms2ml.data.adapters import MZMLAdapter -from ms2ml.utils.mz_utils import annotate_peaks from numpy.typing import NDArray from pandas import DataFrame from tqdm.auto import tqdm @@ -21,6 +20,10 @@ from diadem.search.metrics import get_ref_trace_corrs from diadem.utils import check_sorted, plot_to_log +# TODO re-make some of these classes as ABCs +# It would make intent explicit, since they are sub-classed +# for timstof data equivalents. + @dataclass class ScanGroup: @@ -35,6 +38,10 @@ class ScanGroup: scan_ids: list[str] iso_window_name: str + precursor_mzs: list[NDArray] + precursor_intensities: list[NDArray] + precursor_rts: NDArray + def __post_init__(self) -> None: """Check that all the arrays have the same length.""" elems = [ @@ -53,8 +60,10 @@ def __post_init__(self) -> None: raise ValueError("Not all lengths are the same") if len(self.precursor_range) != 2: raise ValueError( - "Precursor mass range should have 2 elements," - f" has {len(self.precursor_range)}" + ( + "Precursor mass range should have 2 elements," + f" has {len(self.precursor_range)}" + ), ) plot_to_log( self.base_peak_int, @@ -98,9 +107,7 @@ def scale_window_intensities( self, index: int, scaling: NDArray, - mzs: NDArray, window_indices: list[list[int]], - window_mzs: NDArray, ) -> None: """Scales the intensities of specific mzs in a window of the chromatogram. @@ -122,42 +129,50 @@ def scale_window_intensities( """ window = len(scaling) slc, center_index = slice_from_center( - center=index, window=window, length=len(self) + center=index, + window=window, + length=len(self), ) + slc = range(*slc.indices(len(self))) - # TODO this can be tracked internally ... - match_obs_mz_indices, match_win_mz_indices = annotate_peaks( - theo_mz=mzs, - mz=window_mzs, - tolerance=0.02, - unit="da", - ) + zipped = strictzip(slc, scaling, window_indices) + for i, s, si in zipped: + inds = [np.array(x) for x in si if len(x)] + if inds: + inds = np.unique(np.concatenate(inds)) + self._scale_spectrum_at( + spectrum_index=i, + value_indices=inds, + scaling_factor=s, + ) - match_win_mz_indices = np.unique(match_win_mz_indices) + def _scale_spectrum_at( + self, + spectrum_index: int, + value_indices: NDArray[np.int64], + scaling_factor: float, + ) -> None: + i = spectrum_index # Alias for brevity in within this function - zipped = strictzip(range(*slc.indices(len(self))), scaling, window_indices) - for i, s, si in zipped: - for mz_i in match_win_mz_indices: - sim = si[mz_i] - if len(sim) > 0: - self.intensities[i][sim] = self.intensities[i][sim] * s - else: - continue - - # TODO this is hard-coded right now, change as a param - int_remove = self.intensities[i] < 10 - if np.any(int_remove): - self.intensities[i] = self.intensities[i][np.invert(int_remove)] - self.mzs[i] = self.mzs[i][np.invert(int_remove)] - if hasattr(self, "imss"): - self.imss[i] = self.imss[i][np.invert(int_remove)] - - if len(self.intensities[i]): - self.base_peak_int[i] = np.max(self.intensities[i]) - self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] - else: - self.base_peak_int[i] = -1 - self.base_peak_mz[i] = -1 + if len(value_indices) > 0: + self.intensities[i][value_indices] = ( + self.intensities[i][value_indices] * scaling_factor + ) + else: + return None + + # TODO this is hard-coded right now, change as a param + int_remove = self.intensities[i] < 10 + if np.any(int_remove): + self.intensities[i] = self.intensities[i][np.invert(int_remove)] + self.mzs[i] = self.mzs[i][np.invert(int_remove)] + + if len(self.intensities[i]): + self.base_peak_int[i] = np.max(self.intensities[i]) + self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] + else: + self.base_peak_int[i] = -1 + self.base_peak_mz[i] = -1 def __len__(self) -> int: """Returns the number of spectra in the group.""" @@ -226,7 +241,7 @@ def __post_init__(self) -> None: f" for {i}" ) assert array_w == len( - self.stack_peak_indices + self.stack_peak_indices, ), "Window size is not respected in the stack" @property @@ -313,7 +328,9 @@ def from_group( # Except in cases where the edge of the group is reached, where # the center index is adjusted to the edge of the group slice_q, center_index = slice_from_center( - center=index, window=window, length=len(group.mzs) + center=index, + window=window, + length=len(group.mzs), ) mzs = group.mzs[slice_q] intensities = group.intensities[slice_q] @@ -342,7 +359,7 @@ def from_group( mzs=center_mzs, tolerance=mz_tolerance, tolerance_unit=mz_tolerance_unit, - ) + ), ) if i == center_index: assert xic_outs[-1][0].sum() >= center_intensities.max() @@ -404,6 +421,7 @@ def __init__(self, mzml_file: Path | str, config: DiademConfig) -> None: # TODO check if directly reading the xml is faster ... # also evaluate if that is needed scaninfo = self.adapter.get_scan_info() + ms1_scaninfo = scaninfo[scaninfo.ms_level <= 1] self.config = config if "DEBUG_DIADEM" in os.environ: logger.error("RUNNING DIADEM IN DEBUG MODE (only 700-710 mz iso windows)") @@ -411,13 +429,18 @@ def __init__(self, mzml_file: Path | str, config: DiademConfig) -> None: scaninfo = scaninfo[ [x[0] > 700 and x[0] < 705 for x in scaninfo.iso_window] ] + self.ms1info = ms1_scaninfo self.ms2info = scaninfo[scaninfo.ms_level > 1].copy().reset_index() self.unique_iso_windows = set(np.array(self.ms2info.iso_window)) - def _get_iso_window_group( - self, iso_window_name: str, iso_window: tuple[float, float], chunk: DataFrame - ) -> ScanGroup: - logger.debug(f"Processing iso window {iso_window_name}") + def _preprocess_scangroup( + self, + name: str, + df_chunk: DataFrame, + mz_range: None | tuple[float, float] = None, + progress: bool = True, + ) -> tuple[NDArray, ...]: + logger.debug(f"Processing iso window {name}") window_mzs = [] window_ints = [] @@ -430,7 +453,9 @@ def _get_iso_window_group( npeaks_deisotope = [] for row in tqdm( - chunk.itertuples(), desc=f"Preprocessing spectra for {iso_window_name}" + df_chunk.itertuples(), + desc=f"Preprocessing spectra for {name}", + disable=not progress, ): spec_id = row.spec_id curr_spec: Spectrum = self.adapter[spec_id] @@ -439,6 +464,17 @@ def _get_iso_window_group( curr_spec = curr_spec.filter_top(self.config.run_max_peaks_per_spec) curr_spec = curr_spec.filter_mz_range(*self.config.ion_mz_range) + mzs = curr_spec.mz + intensities = curr_spec.intensity + + if mz_range is not None: + mz_filter = slice( + np.searchsorted(mzs, mz_range[0]), + np.searchsorted(mzs, mz_range[1], "right"), + ) + mzs = mzs[mz_filter] + intensities = intensities[mz_filter] + # Deisotoping! if self.config.run_deconvolute_spectra: # Masses need to be ordered for the deisotoping function! @@ -455,13 +491,12 @@ def _get_iso_window_group( unit=self.config.g_tolerance_units[1], ) npeaks_deisotope.append(len(mzs)) - else: - mzs = curr_spec.mz - intensities = curr_spec.intensity + # TODO evaluate this scaling intensities = np.sqrt(intensities) if len(mzs) == 0: mzs, intensities = np.array([0]), np.array([0]) + bp_index = np.argmax(intensities) bp_mz = mzs[bp_index] bp_int = intensities[bp_index] @@ -474,20 +509,65 @@ def _get_iso_window_group( window_rtinsecs.append(rtinsecs) window_scanids.append(spec_id) + window_bp_mz = np.array(window_bp_mz).astype(np.float32) + window_bp_int = np.array(window_bp_int).astype(np.float32) + window_rtinsecs = np.array(window_rtinsecs).astype(np.float16) + check_sorted(window_rtinsecs) + window_scanids = np.array(window_scanids, dtype="object") + avg_peaks_raw = np.array(npeaks_raw).mean() avg_peaks_deisotope = np.array(npeaks_deisotope).mean() # Create datasets within each group - logger.info(f"Saving group {iso_window_name} with length {len(window_mzs)}") + logger.info(f"Saving group {name} with length {len(window_mzs)}") logger.info( - f"{avg_peaks_raw} peaks/spec; {avg_peaks_deisotope} peaks/spec after" - " deisotoping" + ( + f"{avg_peaks_raw} peaks/spec; {avg_peaks_deisotope} peaks/spec after" + " deisotoping" + ), ) - window_bp_mz = np.array(window_bp_mz).astype(np.float32) - window_bp_int = np.array(window_bp_int).astype(np.float32) - window_rtinsecs = np.array(window_rtinsecs).astype(np.float16) - check_sorted(window_rtinsecs) - window_scanids = np.array(window_scanids, dtype="object") + out = [ + window_mzs, + window_ints, + window_bp_mz, + window_bp_int, + window_rtinsecs, + window_scanids, + ] + return out + + def _get_iso_window_group( + self, + iso_window_name: str, + iso_window: tuple[float, float], + ms2_chunk: DataFrame, + ms1_chunk: DataFrame, + ) -> ScanGroup: + ( + window_mzs, + window_ints, + window_bp_mz, + window_bp_int, + window_rtinsecs, + window_scanids, + ) = self._preprocess_scangroup( + name=iso_window_name, + df_chunk=ms2_chunk, + mz_range=None, + ) + + ( + prec_mzs, + prec_ints, + _, + _, + prec_rtinsecs, + _, + ) = self._preprocess_scangroup( + name="precursors " + iso_window_name, + df_chunk=ms1_chunk, + mz_range=iso_window, + ) group = ScanGroup( iso_window_name=iso_window_name, @@ -498,15 +578,20 @@ def _get_iso_window_group( base_peak_int=window_bp_int, retention_times=window_rtinsecs, scan_ids=window_scanids, + precursor_intensities=prec_ints, + precursor_mzs=prec_mzs, + precursor_rts=prec_rtinsecs, ) return group def get_iso_window_groups( - self, workerpool: None | Parallel = None + self, + workerpool: None | Parallel = None, ) -> list[ScanGroup]: """Returns a list of all ScanGroups in an mzML file.""" grouped = self.ms2info.sort_values("RTinSeconds").groupby("iso_window") iso_windows, chunks = zip(*list(grouped)) + precursor_info = self.ms1info iso_window_names = [ "({:.06f}, {:.06f})".format(*iso_window) for iso_window in iso_windows @@ -515,14 +600,20 @@ def get_iso_window_groups( if workerpool is None: results = [ self._get_iso_window_group( - iso_window_name=iwn, iso_window=iw, chunk=chunk + iso_window_name=iwn, + iso_window=iw, + ms2_chunk=chunk, + ms1_chunk=precursor_info, ) for iwn, iw, chunk in zip(iso_window_names, iso_windows, chunks) ] else: results = workerpool( delayed(self._get_iso_window_group)( - iso_window_name=iwn, iso_window=iw, chunk=chunk + iso_window_name=iwn, + iso_window=iw, + ms2_chunk=chunk, + ms1_chunk=precursor_info, ) for iwn, iw, chunk in zip(iso_window_names, iso_windows, chunks) ) @@ -532,13 +623,17 @@ def get_iso_window_groups( def yield_iso_window_groups(self, progress: bool = False) -> Iterator[ScanGroup]: """Yield scan groups for each unique isolation window.""" grouped = self.ms2info.sort_values("RTinSeconds").groupby("iso_window") + precursor_info = self.ms1info for i, (iso_window, chunk) in enumerate( - tqdm(grouped, disable=not progress, desc="Unique Isolation Windows") + tqdm(grouped, disable=not progress, desc="Unique Isolation Windows"), ): iso_window_name = "({:.06f}, {:.06f})".format(*iso_window) group = self._get_iso_window_group( - iso_window_name=iso_window_name, iso_window=iso_window, chunk=chunk + iso_window_name=iso_window_name, + iso_window=iso_window, + ms2_chunk=chunk, + ms1_chunk=precursor_info, ) yield group diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index f82c467..0637b07 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -74,7 +74,7 @@ def from_group( window: int = 21, mz_tolerance: float = 0.02, mz_tolerance_unit: MassError = "da", - ims_tolerance: float = 0.1, + ims_tolerance: float = 0.03, ims_tolerance_unit: IMSError = "abs", # TODO implement abs and pct ims error ... # maybe just use the mz ppm tolerance an multiply by 10000 ... @@ -120,7 +120,9 @@ def from_group( # Except in cases where the edge of the group is reached, where # the center index is adjusted to the edge of the group slice_q, center_index = slice_from_center( - center=index, window=window, length=len(group.mzs) + center=index, + window=window, + length=len(group.mzs), ) mzs = group.mzs[slice_q] intensities = group.intensities[slice_q] @@ -146,11 +148,15 @@ def from_group( # We collapse all unique mzs, after filtering for IMS tolerance # Note: Getting what indices were used to generate u_mzs[0] # would be np.where(inv == 0) - u_center_mzs, inv = np.unique(center_mzs, return_inverse=True) - u_center_intensities = np.zeros( - len(u_center_mzs), dtype=center_intensities.dtype + # TODO testing equality and uniqueness on floats might not be wise. + # I should change this to an int ... maybe setting a "intensity bin" + # value like comet does (0.02??) + u_center_mzs, u_center_intensities, inv = _bin_spectrum_intensities( + center_mzs, + center_intensities, + bin_width=0.02, + bin_offset=0, ) - np.add.at(u_center_intensities, inv, center_intensities) assert is_sorted(u_center_mzs) xic_outs = [] @@ -164,9 +170,12 @@ def from_group( m = m[t_int_keep] inten = inten[t_int_keep] - u_mzs, inv = np.unique(m, return_inverse=True) - u_intensities = np.zeros(len(u_mzs), dtype=inten.dtype) - np.add.at(u_intensities, inv, inten) + u_mzs, u_intensities, inv = _bin_spectrum_intensities( + m, + inten, + bin_width=0.02, + bin_offset=0, + ) outs, inds = xic( query_mz=u_mzs, @@ -183,7 +192,7 @@ def from_group( for y in inds: if len(y) > 0: collapsed_indices = np.concatenate( - [np.where(inv == w)[0] for w in y] + [np.where(inv == w)[0] for w in y], ) out_inds.append(np.unique(t_int_keep[collapsed_indices])) else: @@ -231,9 +240,35 @@ def from_group( return out +def _bin_spectrum_intensities(mzs, intensities, bin_width=0.02, bin_offset=0): + """Bins the intensities based on the mz values. + + Returns + ------- + new_mzs: + The new mz array + new_intensities + The new intensity array + inv: + Index for the new mzs in the original mzs and intensities + Note: Getting what indices were used to generate new_mzs[0] + would be np.where(inv == 0) + + """ + new_mzs, inv = np.unique( + ((mzs + bin_offset) / bin_width).astype("int"), + return_inverse=True, + ) + new_mzs = (new_mzs * bin_width) - bin_offset + new_intensities = np.zeros(len(new_mzs), dtype=intensities.dtype) + np.add.at(new_intensities, inv, intensities) + return new_mzs, new_intensities, inv + + @dataclass class TimsScanGroup(ScanGroup): imss: list[NDArray] + precursor_imss: list[NDArray] def __post_init__(self) -> None: """Validates that the values in the instance are consistent. @@ -271,21 +306,35 @@ def get_highest_window( return window - def scale_window_intensities( + # This is an implementation of a method used by the parent class + def _scale_spectrum_at( self, - index: int, - scaling: NDArray, - mzs: NDArray, - window_indices: list[list[int]], - window_mzs: NDArray, + spectrum_index: int, + value_indices: NDArray[np.int64], + scaling_factor: float, ) -> None: - super().scale_window_intensities( - index=index, - scaling=scaling, - mzs=mzs, - window_indices=window_indices, - window_mzs=window_mzs, - ) + i = spectrum_index # Alias for brevity in within this function + + if len(value_indices) > 0: + self.intensities[i][value_indices] = ( + self.intensities[i][value_indices] * scaling_factor + ) + else: + return None + + # TODO this is hard-coded right now, change as a param + int_remove = self.intensities[i] < 10 + if np.any(int_remove): + self.intensities[i] = self.intensities[i][np.invert(int_remove)] + self.mzs[i] = self.mzs[i][np.invert(int_remove)] + self.imss[i] = self.imss[i][np.invert(int_remove)] + + if len(self.intensities[i]): + self.base_peak_int[i] = np.max(self.intensities[i]) + self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] + else: + self.base_peak_int[i] = -1 + self.base_peak_mz[i] = -1 def __len__(self) -> int: return len(self.imss) @@ -317,11 +366,38 @@ def lazy_datafile(self) -> TimsTOF: del _datafile def _precursor_iso_window_groups( - self, precursor_index: int, progress: bool = True + self, + precursor_index: int, + progress: bool = True, ) -> dict[str:TimsScanGroup]: + elems = self._precursor_iso_window_elements(precursor_index, progress) + out = {} + for k, v in elems.items(): + prec_info = self._precursor_iso_window_elements( + 0, + progress=progress, + mz_range=v["precursor_range"], + ) + out[k] = TimsScanGroup( + precursor_mzs=prec_info["mzs"], + precursor_intensities=prec_info["intensities"], + precursor_rts=prec_info["retention_times"], + precursor_imss=prec_info["imss"], + **v, + ) + + def _precursor_iso_window_elements( + self, + precursor_index: int, + progress: bool = True, + mz_range: None | tuple[float, float] = None, + ) -> dict[str : dict[str:NDArray]]: with self.lazy_datafile() as datafile: index_win_dicts = _get_precursor_index_windows( - datafile, precursor_index=precursor_index, progress=progress + datafile, + precursor_index=precursor_index, + progress=progress, + mz_range=mz_range, ) out = {} @@ -349,35 +425,31 @@ def _precursor_iso_window_groups( assert len(quad_low) == 1 bp_indices = [np.argmax(x) if len(x) else None for x in ints] - bp_ints = np.array( - [ - x1[x2] if x2 is not None else 0 - for x1, x2 in zip(ints, bp_indices) - ] - ) - bp_mz = np.array( - [ - x1[x2] if x2 is not None else -1 - for x1, x2 in zip(mzs, bp_indices) - ] - ) + bp_ints = [ + x1[x2] if x2 is not None else 0 for x1, x2 in zip(ints, bp_indices) + ] + bp_ints = np.array(bp_ints) + bp_mz = [ + x1[x2] if x2 is not None else -1 for x1, x2 in zip(mzs, bp_indices) + ] + bp_mz = np.array(bp_mz) bp_indices = np.array(bp_indices) # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) rts = np.array([x["rt_values_min"] for x in v]) scan_indices = [str(x["scan_indices"]) for x in v] - x = TimsScanGroup( - precursor_range=(quad_low[0], quad_high[0]), - mzs=mzs, - intensities=ints, - imss=imss, - base_peak_int=bp_ints, - base_peak_mz=bp_mz, - # base_peak_ims=bp_ims, - iso_window_name=k, - retention_times=rts, - scan_ids=scan_indices, - ) + x = { + "precursor_range": (quad_low[0], quad_high[0]), + "mzs": mzs, + "intensities": ints, + "imss": imss, + "base_peak_int": bp_ints, + "base_peak_mz": bp_mz, + # 'base_peak_ims':bp_ims, + "iso_window_name": k, + "retention_times": rts, + "scan_ids": scan_indices, + } out[k] = x return out @@ -456,7 +528,9 @@ def find_neighbors_mzsort( pass elif mz_tol_unit.lower() == "ppm": mz_tol: NDArray[np.float32] = get_tolerance( - mz_tol, theoretical=sorted_mz_values, unit="ppm" + mz_tol, + theoretical=sorted_mz_values, + unit="ppm", ) else: raise ValueError("Only 'Da' and 'ppm' values are supported as mass errors") @@ -485,218 +559,6 @@ def find_neighbors_mzsort( return opts -def propose_boxes( - intensities: NDArray[np.float32], - ims_values: NDArray[np.float32], - mz_values: NDArray[np.float32], - ref_ims: float, - ref_mz: float, - mz_sizes=(0.02,), - ims_sizes=(0.05, 0.1), -): - """Proposes and scores bounding boxes around a peak. - - Details - ------- - Score definition: - The score is more of a loss than a score (since lower is better) - The score is defined as - [ - (max_mz_error + # max_ims_error) + - (weighted_mz_standard_deviation) + - # (weighted_ims_standard_deviation) - ] / total_intensity - - Therefore, boxes with very low variance in their mzs or IMSs will have low score. - And boxes with high intensity will also have lower intensity. - - Returns - ------- - boxes - Each box has 5 number, [min_mz, max_mz, min_ims, max_ims, score] - box_intensities: list[float] - The summed intensity in each of the boxes - box_centroids: list[tuple[float, float]] - Each centroid is a tuple of `mz_centroid, ims_centroid` where the - centroid is a weighted average of that dimension, using the intensities - as weights. - - """ - score_mutiplier = max(mz_sizes) # + max(ims_sizes) - delta_ims = np.abs(ims_values - ref_ims) - delta_mzs = np.abs(mz_values - ref_mz) - - # each box has 5 number, [min_mz, max_mz, min_ims, max_ims, score] - boxes = [] - box_centroids = [] - box_intensities = [] - - for mz_tol in mz_sizes: - for ims_tol in ims_sizes: - box_index = (delta_ims <= ims_tol) & (delta_mzs <= mz_tol) - tmp_mzs = mz_values[box_index] - tmp_ims = ims_values[box_index] - tmp_intensity = intensities[box_index] - - box_intensity = tmp_intensity.sum() - if box_intensity == 0: - raise RuntimeError(f"Box has 0 intensity {box_index}") - mz_centroid = (tmp_mzs * tmp_intensity).sum() / box_intensity - ims_centroid = (tmp_ims * tmp_intensity).sum() / box_intensity - - ims_std = ( - (delta_ims[box_index] ** 2) * tmp_intensity - ).sum() / box_intensity - ims_std = np.sqrt(ims_std) - mz_std = ( - 0 # ((delta_mzs[box_index] ** 2)*tmp_intensity).sum() / box_intensity - ) - score = (score_mutiplier + (mz_std + ims_std)) / box_intensity - boxes.append( - [ - ref_mz - mz_tol, - ref_mz + mz_tol, - ref_ims - ims_tol, - ref_ims + ims_tol, - score, - ] - ) - box_intensities.append(box_intensity) - box_centroids.append((mz_centroid, ims_centroid)) - - return boxes, box_intensities, box_centroids - - -# Code modified from https://pyimagesearch.com/2015/02/16/faster-non-maximum-suppression-python/ -# Malisiewicz et al. -def non_max_suppression_fast(boxes, overlapThresh, eps=1e-5, invert_scores=False): - # Invert_scores=True keeps lowest values for the scores (like a loss) - - # Eps adds a little edge to each side of the boxes - # (making boxes of width or height==0 manageable) - - # if there are no boxes, return an empty list - if len(boxes) == 0: - return [] - # if the bounding boxes integers, convert them to floats -- - # this is important since we'll be doing a bunch of divisions - if boxes.dtype.kind == "i": - boxes = boxes.astype("float") - # initialize the list of picked indexes - pick = [] - # grab the coordinates of the bounding boxes - # Note our boxes are [min(x),max(x),min(y),max(y)], instead of [x,y,x,y] in the orifinal implementation - x1 = boxes[:, 0] - eps - x2 = boxes[:, 1] + eps - y1 = boxes[:, 2] - eps - y2 = boxes[:, 3] + eps - scores = boxes[:, 4] - # compute the area of the bounding boxes and sort the bounding - # boxes by the bottom-right y-coordinate of the bounding box - area = (x2 - x1) * (y2 - y1) - idxs = np.argsort(scores) - if invert_scores: - idxs = idxs[::-1] - - # keep looping while some indexes still remain in the indexes - # list - while len(idxs) > 0: - # grab the last index in the indexes list and add the - # index value to the list of picked indexes - last = len(idxs) - 1 - i = idxs[last] - pick.append(i) - # find the largest (x, y) coordinates for the start of - # the bounding box and the smallest (x, y) coordinates - # for the end of the bounding box - xx1 = np.maximum(x1[i], x1[idxs[:last]]) - yy1 = np.maximum(y1[i], y1[idxs[:last]]) - xx2 = np.minimum(x2[i], x2[idxs[:last]]) - yy2 = np.minimum(y2[i], y2[idxs[:last]]) - # compute the width and height of the bounding box - w = np.maximum(0, xx2 - xx1 + eps) - h = np.maximum(0, yy2 - yy1 + eps) - # compute the ratio of overlap - overlap = (w * h) / area[idxs[:last]] - # delete all indexes from the index list that have - box_inds_delete = np.where(overlap > overlapThresh)[0] - idxs = np.delete(idxs, np.concatenate(([last], box_inds_delete))) - - # return the indices of the best bounding boxes - return pick - - -def bundle_neighbors_mzsorted( - ims_values: NDArray[np.float32], - sorted_mz_values: NDArray[np.float32], - intensities: NDArray[np.float32], - top_n: int = 500, -): - """Warning: - ------- - Output is not necessarily sorted!!!. - """ - BOX_EPS = 1e-7 - - opts = find_neighbors_mzsort( - ims_values, - sorted_mz_values=sorted_mz_values, - intensities=intensities, - top_n=top_n, - ims_tol=0.1, - mz_tol=50, - mz_tol_unit="ppm", - ) - - unambiguous = {k for k, v in opts.items() if len(v) == 1} - ambiguous = {k: v for k, v in opts.items() if len(v) > 1} - - unambiguous = { - "ims": ims_values[list(unambiguous)], - "mz": sorted_mz_values[list(unambiguous)], - "intensity": intensities[list(unambiguous)], - } - - if len(ambiguous) > 0: - all_boxes = [] - all_intensities = [] - all_centroids = [] - for k, v in ambiguous.items(): - boxes, box_intensities, box_centroids = propose_boxes( - intensities[v], - ims_values[v], - mz_values=sorted_mz_values[v], - ref_ims=ims_values[k], - ref_mz=sorted_mz_values[k], - mz_sizes=( - 0.01, - 0.05, - ), - ims_sizes=(0.01, 0.05, 0.1), - # ims_sizes = (0.005, 0.01, 0.02), - ) - all_boxes.extend(boxes) - all_intensities.extend(box_intensities) - all_centroids.extend(box_centroids) - - best_boxes_indices = non_max_suppression_fast( - np.stack(all_boxes), 0, eps=BOX_EPS, invert_scores=True - ) - best_centroids = [all_centroids[x] for x in best_boxes_indices] - best_intensities = [all_intensities[x] for x in best_boxes_indices] - mzs, imss = zip(*best_centroids) - boxed = { - "ims": imss, - "mz": mzs, - "intensity": best_intensities, - } - - final = {k: np.concatenate([unambiguous[k], boxed[k]]) for k in unambiguous} - else: - final = unambiguous - return final - - def get_break_indices( inds: NDArray[np.int64], ) -> tuple[NDArray[np.int64], NDArray[np.int64]]: @@ -716,14 +578,20 @@ def get_break_indices( def _get_precursor_index_windows( - dia_data: TimsTOF, precursor_index: int, progress: bool = True + dia_data: TimsTOF, + precursor_index: int, + progress: bool = True, + mz_range=None, ) -> dict[dict[list]]: PRELIM_N_PEAK_FILTER = 10_000 # noqa - MIN_INTENSITY_KEEP = 100 # noqa + MIN_INTENSITY_KEEP = 150 # noqa MIN_NUM_SEED_BOXES = 1000 # noqa - IMS_TOL = 0.01 - MZ_TOL = 0.01 - MAX_ISOTOPE_CHARGE = 2 + IMS_TOL = 0.01 # noqa + # these tolerances are set narrow on purpose, since they + # only delimit the definition of the neighborhood, which will iteratively + # expanded. + MZ_TOL = 0.01 # noqa + MAX_ISOTOPE_CHARGE = 2 # noqa inds = dia_data[{"precursor_indices": precursor_index}, "raw"] breaks, break_inds = get_break_indices(inds=inds) @@ -761,7 +629,8 @@ def _get_precursor_index_windows( if len(peak_data["corrected_intensity_values"]) > PRELIM_N_PEAK_FILTER: partition_indices = np.argpartition( - peak_data["corrected_intensity_values"], -PRELIM_N_PEAK_FILTER + peak_data["corrected_intensity_values"], + -PRELIM_N_PEAK_FILTER, )[-PRELIM_N_PEAK_FILTER:] peak_data = {k: v[partition_indices] for k, v in peak_data.items()} @@ -770,10 +639,23 @@ def _get_precursor_index_windows( if len(peak_data["corrected_intensity_values"]) == 0: continue + ims_values = peak_data["mobility_values"] + mz_values = peak_data["mz_values"] + intensity_values = peak_data["corrected_intensity_values"] + + if mz_range is not None: + mz_filter = slice( + np.searchsorted(mz_values, mz_range[0]), + np.searchsorted(mz_values, mz_range[1], "right"), + ) + ims_values = ims_values[mz_filter] + mz_values = mz_values[mz_filter] + intensity_values = intensity_values[mz_filter] + f = collapse_ims( - ims_values=peak_data["mobility_values"], - mz_values=peak_data["mz_values"], - intensity_values=peak_data["corrected_intensity_values"], + ims_values=ims_values, + mz_values=mz_values, + intensity_values=intensity_values, top_n=MIN_NUM_SEED_BOXES, ims_tol=IMS_TOL, mz_tol=MZ_TOL, @@ -822,15 +704,16 @@ def _get_precursor_index_windows( # @profile -def collapse_seeds( - seeds: dict[int, list[int]], intensity_values: NDArray[np.float32] +def _collapse_seeds( + seeds: dict[int, list[int]], + intensity_values: NDArray[np.float32], ) -> tuple[dict[int, int], set[int]]: seeds = seeds.copy() seed_keys = list(seeds.keys()) seed_order = np.argsort(-intensity_values[np.array(seed_keys)]) taken = set() out_seeds = {} - max_iter = 3 + MAX_ITER = 5 # noqa for s in seed_order: sk = seed_keys[s] @@ -840,9 +723,9 @@ def collapse_seeds( out_seeds[sk] = set(seeds.pop(sk, {sk})) curr_len = 1 - for _ in range(max_iter): + for _ in range(MAX_ITER): neigh_set = set(chain(*[seeds.pop(x, set()) for x in out_seeds[sk]])).union( - out_seeds[sk] + out_seeds[sk], ) untaken = neigh_set.difference(taken) taken.update(untaken) @@ -866,7 +749,9 @@ def collapse_ims( ims_tol=0.01, mz_tol=0.01, ) -> dict[str, NDArray[np.float32]]: - """Sample output + """Collapses peaks with similar IMS and MZ. + + Sample output ------------- ``` bundled = { @@ -876,6 +761,8 @@ def collapse_ims( } ```. """ + MIN_NEIGHBORS_SEED = 6 # noqa + neighborhoods = find_neighbors_mzsort( ims_vals=ims_values, sorted_mz_values=mz_values, @@ -888,15 +775,18 @@ def collapse_ims( unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} - seeds = {k: v for k, v in ambiguous.items() if len(v) > 5} + seeds = {k: v for k, v in ambiguous.items() if len(v) >= MIN_NEIGHBORS_SEED} if seeds: - out_seeds, taken = collapse_seeds( - seeds=seeds, intensity_values=intensity_values + out_seeds, taken = _collapse_seeds( + seeds=seeds, + intensity_values=intensity_values, ) else: out_seeds, taken = {}, set() + # TODO consider whether I really want to keep ambiduous matches + # In theory I could keep only the expanded seeds. ambiguous_untaken = list(set(ambiguous).difference(taken)) unambiguous_out = { "ims": ims_values[list(chain(unambiguous, ambiguous_untaken))], @@ -912,6 +802,8 @@ def collapse_ims( for i, v in enumerate(out_seeds.values()): v = list(v) v_intensities = intensity_values[v] + # This generates the weighted average of the ims and mz + # as the new values for the peak. bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity @@ -920,21 +812,3 @@ def collapse_ims( k: np.concatenate([unambiguous_out[k], bundled[k]]) for k in unambiguous_out } return final - - -if __name__ == "__main__": - file = "/Users/sebastianpaez/git/diadem/profiling/profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d" - config = DiademConfig() - - data = TimsSpectrumStacker(file, config) - group = next(data.yield_iso_window_groups()) - group.get_highest_window( - 21, - 0.01, - mz_tolerance=0.02, - mz_tolerance_unit="da", - ims_tolerance=0.03, - ims_tolerance_unit="abs", - min_correlation=0.5, - max_peaks=150, - ) diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index c55da1a..ee259c3 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -104,7 +104,10 @@ def mass_unit_fun(x: float, y: float) -> float: return peaks -# TODO this is a function prototype, if it works abstract it with the prototype +# TODO this is a function prototype, if it works abstract it and +# combine with the parent function. + + # @profile def deisotope_with_ims( mz: NDArray[np.float32] | list[float], @@ -124,15 +127,21 @@ def deisotope_with_ims( Parameters ---------- mz, list[float] - A list of the mz values to be deisotoped. + A list of the mz values to be deisotoped. (NEEDS to be sorted) inten, list[float] A list of the intensities that correspond in order to the elements in mz. + imss, list[float] + A list of the ims values to use for the search. max_charge, int Maximum charge to look for in the isotopes. - diff, float + mz_diff, float Tolerance to use when searching (typically 20 for ppm or 0.02 for da) - unit, str + mz_unit, str Unit for the diff. ppm or da + ims_diff, float + Tolerance to use when searching in the IMS dimension. + ims_unit, float + Tolerance unit to use when searching in the ims dimension. track_indices, bool Whether to return the indices of the combined indices as well. @@ -142,9 +151,9 @@ def deisotope_with_ims( >>> my_imss = [0.7, 0.7, 0.8, 0.7, 0.8, 0.7] >>> my_intens = [1-(0.1*i) for i,_ in enumerate(my_mzs)] >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs") - (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), (0.7, 0.7, 0.8)) + (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), array([0.7, 0.7, 0.8])) >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs", track_indices=True) - (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), (0.7, 0.7, 0.8), ([0], [1, 3, 5], [2, 4])) + (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), array([0.7, 0.7, 0.8]), ([0], [1, 3, 5], [2, 4])) """ if mz_unit.lower() == "da": @@ -166,7 +175,14 @@ def ims_unit_fun(x: float, y: float) -> float: peaks = [(mz, intensity, ims) for mz, intensity, ims in zip(mz, inten, imss)] peaks = [ - {"mz": mz, "ims": ims, "intensity": intensity, "envelope": None, "charge": None} + { + "mz": mz, + "ims": ims, + "intensity": intensity, + "ims_range": None, + "envelope": None, + "charge": None, + } for mz, intensity, ims in peaks ] for i in range(len(peaks)): @@ -185,9 +201,23 @@ def ims_unit_fun(x: float, y: float) -> float: iso = NEUTRON / charge # Note, this assumes that the isotope envelope always decreases in # intensity, which is not accurate for high mol weight fragments. + intensity_increases = inten[i] < inten[j] matches_mz = abs(delta - iso) <= mz_tol - if matches_mz and inten[i] < inten[j]: + + if (c_ims_range := peaks[i]["ims_range"]) is None: + c_ims_range = (imss[i] - ims_tol, imss[i] + ims_tol) + matches_envelope_ims = True + else: + matches_envelope_ims = ( + imss[j] >= c_ims_range[0] and imss[j] <= c_ims_range[1] + ) + if matches_mz and intensity_increases and matches_envelope_ims: + # Why does it add intensities before checking the charge? peaks[j]["intensity"] += peaks[i]["intensity"] + peaks[j]["ims_range"] = ( + max(c_ims_range[0], imss[j] - ims_tol), + min(c_ims_range[1], imss[j] + ims_tol), + ) if track_indices: peaks[j]["indices"].extend(peaks[i]["indices"]) if peaks[i]["charge"] and peaks[i]["charge"] != charge: @@ -195,6 +225,7 @@ def ims_unit_fun(x: float, y: float) -> float: peaks[j]["charge"] = charge peaks[i]["charge"] = charge peaks[i]["envelope"] = j + j -= 1 if not track_indices: @@ -205,7 +236,8 @@ def ims_unit_fun(x: float, y: float) -> float: def _filter_peaks( - peaks: dict, extract: tuple[str] + peaks: dict, + extract: tuple[str], ) -> tuple[NDArray[np.float32] | list, ...]: """Filters peaks to remove isotope envelopes. diff --git a/diadem/index/fragment_buckets.py b/diadem/index/fragment_buckets.py index b75b932..d36fec4 100644 --- a/diadem/index/fragment_buckets.py +++ b/diadem/index/fragment_buckets.py @@ -237,8 +237,10 @@ def __getitem__(self, val: int | slice) -> FragmentBucket | FragmentBucketList: return self.buckets[val] else: raise ValueError( - f"Subsetting FragmentBucketList with {type(val)}: {val} is not" - " supported" + ( + f"Subsetting FragmentBucketList with {type(val)}: {val} is not" + " supported" + ), ) @classmethod @@ -286,7 +288,7 @@ def from_arrays( precursor_mzs=precursor_mzs[i : i + chunksize], sorting_level=sorting_level, is_sorted=been_sorted, - ) + ), ) return cls(buckets) @@ -297,7 +299,9 @@ def sort(self, level: SortingLevel) -> None: # @profile def yield_candidates( - self, ms2_range: tuple[float, float], ms1_range: tuple[float, float] + self, + ms2_range: tuple[float, float], + ms1_range: tuple[float, float], ) -> None | Iterator[tuple[int, float, str]]: """Yields fragments that match the passed masses. @@ -329,7 +333,9 @@ def yield_candidates( continue yield from zip( - bucket.precursor_ids, bucket.fragment_mzs, bucket.fragment_series + bucket.precursor_ids, + bucket.fragment_mzs, + bucket.fragment_series, ) @@ -369,7 +375,7 @@ def __init__( self.buckets[k].append(v) iterator = enumerate( - tqdm(self.buckets, disable=not progress, desc="Concatenating buckets") + tqdm(self.buckets, disable=not progress, desc="Concatenating buckets"), ) # This gets progressively updated with the minimum bucket size. @@ -421,7 +427,9 @@ def unpack_bucket(self, bucket: FragmentBucket) -> dict[int, FragmentBucket]: return out def yield_buckets_matching_ms2( - self, min_mz: float, max_mz: float + self, + min_mz: float, + max_mz: float, ) -> Iterator[FragmentBucket]: """Yields buckets that match the passed ms2 range.""" min_index = max(0, int(min_mz * self.prod_num) - 1) @@ -461,10 +469,12 @@ def yield_candidates( min_mz, max_mz = ms2_range last_mz = 0 for x in self.yield_buckets_matching_ms2(min_mz, max_mz): + assert is_sorted(x.fragment_mzs) + last_val = np.searchsorted(x.fragment_mzs, max_mz, "right") for precursor_id, fragmz, fragseries in zip( - x.precursor_ids, x.fragment_mzs, x.fragment_series + x.precursor_ids[:last_val], + x.fragment_mzs[:last_val], + x.fragment_series[:last_val], ): - if fragmz > max_mz: - break assert last_mz <= (last_mz := fragmz), "Fragment mzs not sorted" yield precursor_id, fragmz, fragseries diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index a7ea53c..679003f 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -63,14 +63,15 @@ def _make_score_dict(ions: str) -> dict[str, dict[str, float | list[float]]]: SeqProperties = namedtuple( - "SeqProperties", "fragments, ion_series, prec_mz, proforma_seq, num_frags" + "SeqProperties", + "fragments, ion_series, prec_mz, proforma_seq, num_frags", ) class PeptideScore: """Accumulates elements to calculate the score for a peptide.""" - __slots__ = ("id", "ions", "partial_scores", "tot_peaks") + __slots__ = ("id", "ions", "partial_scores", "tot_peaks", "peak_ids") def __init__(self, id: int, ions: str) -> None: """Accumulates elements to calculate the score for a peptide. @@ -85,20 +86,28 @@ def __init__(self, id: int, ions: str) -> None: Examples -------- >>> score = PeptideScore(1, "by") - >>> score.add_peak('y', mz = 234.22, intensity = 100, error = 0.01) - >>> score.add_peak('y', mz = 534.22, intensity = 200, error = 0.012) + >>> score.add_peak("y", mz=234.22, intensity=100, error=0.01, peak_id=1) + >>> score.add_peak("y", mz=534.22, intensity=200, error=0.012, peak_id=2) >>> outs = score.as_row_entry() - >>> [x for x in outs] # doctest: +NORMALIZE_WHITESPACE - ['id', 'b_intensity', 'b_npeaks', 'b_mzs', 'y_intensity', 'y_npeaks',\ - 'y_mzs', 'log_intensity_sums', 'log_factorial_peak_sum', 'mzs',\ - 'mass_errors', 'avg_abs_dm', 'med_abs_dm'] + >>> [x for x in outs] # doctest: +NORMALIZE_WHITESPACE + ['id', 'b_intensity', 'b_npeaks', 'b_mzs', 'y_intensity', \ + 'y_npeaks', 'y_mzs', 'log_intensity_sums', 'log_factorial_peak_sum', \ + 'mzs', 'mass_errors', 'avg_abs_dm', 'med_abs_dm', 'spec_indices'] """ # noqa self.id: int = id self.ions = ions self.partial_scores = copy.deepcopy(_make_score_dict(ions)) self.tot_peaks = 0 + self.peak_ids = [] - def add_peak(self, ion: str, mz: float, intensity: float, error: float) -> None: + def add_peak( + self, + ion: str, + mz: float, + intensity: float, + error: float, + peak_id: int, + ) -> None: """Adds a peak to the partial score. Check the class docstring for more details. @@ -113,11 +122,15 @@ def add_peak(self, ion: str, mz: float, intensity: float, error: float) -> None: The intensity of the peak to add. error : float The mass error of the peak to add. + peak_id: int + Id of the peak. It is usefull to track what peaks within + a spectrum were a match. """ self.partial_scores[ion]["intensities"] += intensity self.partial_scores[ion]["npeaks"] += 1 self.partial_scores[ion]["mzs"].append(mz) self.partial_scores[ion]["mass_errors"].append(error) + self.peak_ids.append(peak_id) self.tot_peaks += 1 def as_row_entry(self) -> dict[str, float | list[float] | int]: @@ -150,6 +163,7 @@ def as_row_entry(self) -> dict[str, float | list[float] | int]: out["mass_errors"] = mass_errors out["avg_abs_dm"] = np.abs(np.array(mass_errors)).mean() out["med_abs_dm"] = np.median(np.abs(np.array(mass_errors))) + out["spec_indices"] = self.peak_ids return out @@ -167,7 +181,10 @@ class IndexedDb: """ def __init__( - self, chunksize: int, config: DiademConfig = DEFAULT_CONFIG, name: str = "db" + self, + chunksize: int, + config: DiademConfig = DEFAULT_CONFIG, + name: str = "db", ) -> None: """Creates a new IndexedDb object. @@ -245,8 +262,10 @@ def decoys(self) -> list[Peptide]: make_decoy(x) for x in tqdm(targets, desc="Generating Decoys") ] logger.info( - f"Generating database with {len(self.decoys)} decoys," - f" and {len(self.targets)} targets" + ( + f"Generating database with {len(self.decoys)} decoys," + f" and {len(self.targets)} targets" + ), ) return self._decoys @@ -288,7 +307,9 @@ def ms1_filter(pep: Peptide) -> Peptide | None: self.targets = sequences def prefilter_ms1( - self, ms1_range: tuple[float, float], num_decimals: int = 3 + self, + ms1_range: tuple[float, float], + num_decimals: int = 3, ) -> IndexedDb: """Prefilters the database. @@ -315,7 +336,8 @@ def prefilter_ms1( out = copy.copy(self) out.bucketlist = self.bucketlist.prefilter_ms1( - *ms1_range, num_decimals=num_decimals + *ms1_range, + num_decimals=num_decimals, ) out.prefiltered_ms1 = True out.seq_prec_mzs = self.seq_prec_mzs @@ -369,7 +391,7 @@ def index_from_parquet(self, dir: Path | str) -> None: self.seqs = seqs_df["seq_proforma"].values self.target_proforma = set( - (seqs_df["seq_proforma"][np.invert(seqs_df["decoy"])]).values + (seqs_df["seq_proforma"][np.invert(seqs_df["decoy"])]).values, ) frags_df = pd.read_parquet( @@ -379,7 +401,7 @@ def index_from_parquet(self, dir: Path | str) -> None: frags_df = frags_df[frags_df["mz"] < self.config.ion_mz_range[1]] self.target_proforma = set( - (seqs_df["seq_proforma"][np.invert(seqs_df["decoy"])]).values + (seqs_df["seq_proforma"][np.invert(seqs_df["decoy"])]).values, ) self.index_from_arrays( frags_df["mz"].values, @@ -433,7 +455,8 @@ def _dump_peptides_parquet( if seq_file_path.exists(): append = True for seq_id, (frag_mzs, ion_series, prec_mzs, prec_seqs, num_frags) in enumerate( - (self.seq_properties(x) for x in iter_seqs), start=start_id + (self.seq_properties(x) for x in iter_seqs), + start=start_id, ): seq_chunk["seq_id"].append(seq_id) seq_chunk["seq_mz"].append(prec_mzs) @@ -441,7 +464,10 @@ def _dump_peptides_parquet( seq_chunk["decoy"].append(decoy) for w, x, y, z in zip( - [prec_mzs] * num_frags, frag_mzs, ion_series, [seq_id] * num_frags + [prec_mzs] * num_frags, + frag_mzs, + ion_series, + [seq_id] * num_frags, ): frag_chunk["precursor_mz"].append(float(w)) frag_chunk["mz"].append(float(x)) @@ -453,7 +479,9 @@ def _dump_peptides_parquet( append = True write_parquet(seq_file_path, pd.DataFrame(seq_chunk), append=append) write_parquet( - fragment_file_path, pd.DataFrame(frag_chunk), append=append + fragment_file_path, + pd.DataFrame(frag_chunk), + append=append, ) # This just flushes the chunk so next iteration starts @@ -501,7 +529,7 @@ def index_from_sequences(self) -> None: ) frag_mzs, frag_series, prec_mzs, prec_seqs, num_frags = zip( - *(self.seq_properties(x) for x in iter_seqs) + *(self.seq_properties(x) for x in iter_seqs), ) # NOTE: Changing to float16 does not give the correct result @@ -567,13 +595,17 @@ def index_from_arrays( """ if not len(prec_mzs) == len(prec_seqs): raise ValueError( - "The length of the precursor mzs and the precursor sequences needs to" - " be the same." + ( + "The length of the precursor mzs and the precursor sequences needs" + " to be the same." + ), ) if not all(len(frag_mzs) == len(x) for x in [frag_series, frag_to_prec_ids]): raise ValueError( - "The length of the frag_mz, frag_series and frag_to_prec_ids need to be" - " the same" + ( + "The length of the frag_mz, frag_series and frag_to_prec_ids need" + " to be the same" + ), ) if not len(prec_seqs) == len(np.unique(prec_seqs)): raise ValueError("All precursor sequences need to be unique!") @@ -581,11 +613,13 @@ def index_from_arrays( # Sorted externally by ms2 mz with disabled_gc(): logger.debug( - f"Sorting by ms2 mz. {frag_mzs.size} total fragments (if needed)" + f"Sorting by ms2 mz. {frag_mzs.size} total fragments (if needed)", ) if not is_sorted(frag_mzs): sorted_frags, sorted_frag_series, sorted_seq_ids = sort_all( - frag_mzs, frag_series, frag_to_prec_ids + frag_mzs, + frag_series, + frag_to_prec_ids, ) logger.debug("Done sorting (and GC), generating bucketlists") else: @@ -620,13 +654,13 @@ def seq_properties(self, x: Peptide) -> SeqProperties: """Internal method that extracts the peptide properties to build the index.""" masses = { k: np.concatenate( - [x.ion_series(ion_type=k, charge=c) for c in x.config.ion_charges] + [x.ion_series(ion_type=k, charge=c) for c in x.config.ion_charges], ) for k in x.config.ion_series } ion_series = np.concatenate( - [np.full_like(v, k, dtype=str) for k, v in masses.items()] + [np.full_like(v, k, dtype=str) for k, v in masses.items()], ) masses = np.concatenate(list(masses.values())) # TODO move this to the config ... @@ -689,7 +723,7 @@ def score_arrays( ms1_range = precursor_mz else: raise ValueError( - "precursor_mz has to be of length 2 or a single number" + "precursor_mz has to be of length 2 or a single number", ) else: ms1_tol = get_tolerance( @@ -701,7 +735,7 @@ def score_arrays( peaks = [] - for fragment_mz, fragment_intensity in zip(spec_mz, spec_int): + for i, (fragment_mz, fragment_intensity) in enumerate(zip(spec_mz, spec_int)): ms2_tol = get_tolerance( self.config.g_tolerances[1], theoretical=fragment_mz, @@ -715,6 +749,7 @@ def score_arrays( for seq, frag, series in candidates: # Should tolerances be checked here? + # IN THEORY, they should have been filtered in the past. dm = frag - fragment_mz if abs(dm) <= ms2_tol: peaks.append( @@ -724,7 +759,8 @@ def score_arrays( "mz": fragment_mz, "intensity": fragment_intensity, "error": dm, - } + "peak_id": i, + }, ) peptide_ids = np.array([x["seq"] for x in peaks]) @@ -781,8 +817,11 @@ def hyperscore( A dataframe with the top scoring peptides. """ assert len(self.seq_ids) == len(self.seqs) + assert len(self.seq_prec_mzs) == len(self.seqs) scores = self.score_arrays( - precursor_mz=precursor_mz, spec_mz=spec_mz, spec_int=spec_int + precursor_mz=precursor_mz, + spec_mz=spec_mz, + spec_int=spec_int, ) if scores is None or len(scores) == 0: @@ -795,6 +834,8 @@ def hyperscore( score_mean = scores["Score"].mean() score_sd = scores["Score"].std() + # TODO reconsider if I want to do the filtering here. + # If i dont, I could use the precursor information as a filter ... scores = scores.nlargest(top_n, "Score", keep="all") scores.sort_values("Score", ascending=False, inplace=True) @@ -807,10 +848,12 @@ def hyperscore( assert np.allclose(self.seq_ids[indices_seqs_local], scores["id"].values) scores["Peptide"] = self.seqs[indices_seqs_local] + scores["PrecursorMZ"] = self.seq_prec_mzs[indices_seqs_local] scores["decoy"] = [s not in self.target_proforma for s in scores["Peptide"]] try: assert len(np.unique(scores["Peptide"])) == len(scores), np.unique( - scores["Peptide"], return_counts=True + scores["Peptide"], + return_counts=True, ) except AssertionError: # There is a bug that gets detected here where a single peptide gets @@ -818,16 +861,21 @@ def hyperscore( # This issue happens when a sequence is also in the decoys logger.error( - f"{scores} has multiple peptides with the " - "same id (ocasionally happens when it is both a " - "target and a decoy)" + ( + f"{scores} has multiple peptides with the " + "same id (ocasionally happens when it is both a " + "target and a decoy)" + ), ) return scores # @profile def index_prefiltered_from_parquet( - self, cache_path: PathLike, min_mz: float, max_mz: float + self, + cache_path: PathLike, + min_mz: float, + max_mz: float, ) -> IndexedDb: """Generates a pre-filtered index from a parquet cache. @@ -841,7 +889,7 @@ def index_prefiltered_from_parquet( seqs_df = pl.scan_parquet(str(cache_path) + "/seqs.parquet") logger.info( - f"Filtering ms1 ranges {min_mz} to {max_mz} in database {self.name}" + f"Filtering ms1 ranges {min_mz} to {max_mz} in database {self.name}", ) joint_frags = ( @@ -864,7 +912,7 @@ def index_prefiltered_from_parquet( .astype(np.float32), sorting_level="ms2", is_sorted=True, - ) + ), ], num_decimal=2, max_frag_mz=2000, @@ -888,7 +936,7 @@ def index_prefiltered_from_parquet( target_set = set( target_set["seq_proforma"].to_numpy()[ np.invert(target_set["decoy"].to_numpy()) - ] + ], ) if len(target_set) == 0: @@ -898,7 +946,10 @@ def index_prefiltered_from_parquet( def db_from_fasta( - fasta: Path | str, chunksize: int, config: DiademConfig, index: bool = True + fasta: Path | str, + chunksize: int, + config: DiademConfig, + index: bool = True, ) -> tuple[IndexedDb, str]: """Created a peak index database from a fasta file. diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index cae8b5f..82ab6cc 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -1,6 +1,5 @@ from __future__ import annotations -import itertools import time from os import PathLike from pathlib import Path @@ -57,7 +56,7 @@ def search_group( IMS_TOLERANCE = config.g_ims_tolerance # noqa IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa - MAX_NUM_CONSECUTIVE_FAILS = 50 + MAX_NUM_CONSECUTIVE_FAILS = 50 # noqa new_window_kwargs = { "window": WINDOWSIZE, @@ -74,7 +73,7 @@ def search_group( { "ims_tolerance": IMS_TOLERANCE, "ims_tolerance_unit": IMS_TOLERANCE_UNIT, - } + }, ) stack_getter = TimsStackedChromatograms.from_group @@ -105,15 +104,19 @@ def search_group( if num_fails > ALLOWED_FAILS: logger.warning( - "Exiting scoring loop because number of" - f" failes reached the maximum {ALLOWED_FAILS}" + ( + "Exiting scoring loop because number of" + f" failes reached the maximum {ALLOWED_FAILS}" + ), ) break if num_consecutive_fails > MAX_NUM_CONSECUTIVE_FAILS: logger.warning( - "Exiting with early termination due " - f"to consecurtive fails {num_consecutive_fails}" + ( + "Exiting with early termination due " + f"to consecurtive fails {num_consecutive_fails}" + ), ) group_results = group_results[:-num_consecutive_fails] break @@ -126,8 +129,10 @@ def search_group( if last_id == new_stack.parent_index: logger.debug( - "Array generated on same index " - f"{new_stack.parent_index} as last iteration" + ( + "Array generated on same index " + f"{new_stack.parent_index} as last iteration" + ), ) num_fails += 1 else: @@ -155,21 +160,21 @@ def search_group( precursor_mz=group.precursor_range, spec_int=scoring_intensities, spec_mz=new_stack.mzs, - top_n=1, + top_n=100, ) + # TODO add here a filter for precursor evidence and re-rank + logger.error("Sebastian has not implemented this!!") else: scores = None if scores is not None: scores["id"] = match_id - ref_peak_mz = new_stack.mzs[new_stack.ref_index] + match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] + match_indices = np.sort(np.unique(np.array(match_indices))) - mzs = itertools.chain( - *[scores[x].iloc[0] for x in scores.columns if "_mzs" in x] - ) - best_match_mzs = np.sort( - np.array(tuple(itertools.chain(mzs, [ref_peak_mz]))) - ) + scaling_window_indices = [ + [x[y] for y in match_indices] for x in new_stack.stack_peak_indices + ] # Scale based on the inverse of the reference chromatogram normalized_trace = new_stack.ref_trace / new_stack.ref_trace.max() @@ -183,9 +188,7 @@ def search_group( group.scale_window_intensities( index=new_stack.parent_index, scaling=scaling, - mzs=best_match_mzs, - window_indices=new_stack.stack_peak_indices, - window_mzs=new_stack.mzs, + window_indices=scaling_window_indices, ) if (num_peaks % DEBUG_FREQUENCY) == 0: @@ -220,13 +223,15 @@ def search_group( num_consecutive_fails = 0 else: logger.debug(f"{match_id} did not match any peptides, scaling and skipping") - scaling = SCALING_RATIO * np.ones_like(new_stack.ref_trace) + scaling = ( + SCALING_RATIO + * np.ones_like(new_stack.ref_trace) + * MIN_INTENSITY_SCALING + ) group.scale_window_intensities( index=new_stack.parent_index, scaling=scaling, - mzs=new_stack.mzs, window_indices=new_stack.stack_peak_indices, - window_mzs=new_stack.mzs, ) num_fails += 1 num_consecutive_fails += 1 @@ -238,7 +243,7 @@ def search_group( "max_intensity": curr_highest_peak_int, "num_fails": num_fails, "num_scores": len(group_results), - } + }, ) pbar.update(1) if ((et := time.time()) - st) >= 2: @@ -260,8 +265,10 @@ def search_group( tot_candidates += 1 logger.error( - f"Iteration took waaaay too long scores={scores} ;" - f" {tot_candidates} total candidates" + ( + f"Iteration took waaaay too long scores={scores} ;" + f" {tot_candidates} total candidates" + ), ) logger.error(f"{new_stack.mzs.copy()}; len({len(new_stack.mzs)})") st = et @@ -270,16 +277,19 @@ def search_group( pbar.close() plot_to_log( - np.log1p(np.array(intensity_log)), title="Max (log) intensity over time" + np.log1p(np.array(intensity_log)), + title="Max (log) intensity over time", ) plot_to_log(np.array(score_log), title="Score over time") plot_to_log(np.array(index_log), title="Requested index over time") plot_to_log(np.array(fwhm_log), title="FWHM across time") logger.info( - f"Done with window {group.iso_window_name}, " - f"scored {num_peaks} peaks in {len(group.base_peak_int)} spectra. " - f"Intensity of the last scored peak {curr_highest_peak_int} " - f"on index {last_id}" + ( + f"Done with window {group.iso_window_name}, " + f"scored {num_peaks} peaks in {len(group.base_peak_int)} spectra. " + f"Intensity of the last scored peak {curr_highest_peak_int} " + f"on index {last_id}" + ), ) if len(group_results) == 0: @@ -314,7 +324,10 @@ def diadem_main( # Set up database db, cache = db_from_fasta( - fasta=fasta_path, chunksize=None, config=config, index=False + fasta=fasta_path, + chunksize=None, + config=config, + index=False, ) # set up mzml file @@ -359,7 +372,8 @@ def setup_db_and_search( ) results: pd.DataFrame = pd.concat( - [x for x in results if x is not None], ignore_index=True + [x for x in results if x is not None], + ignore_index=True, ) prefix = out_prefix + ".diadem" if out_prefix else "diadem" diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index b125b0e..7548e16 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -3,14 +3,16 @@ cli.setup_logger() cli.diadem_main( - fasta_path="./profiling_data/UP000000625_83333.fasta", - data_path="./profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf", + fasta_path="./profiling_data/UP000005640_9606.fasta", + data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", config=DiademConfig( run_parallelism=1, # Needs to use 1 core for profiling run_max_peaks=20000, run_allowed_fails=500, g_tolerances=(25, 25), g_tolerance_units=("ppm", "ppm"), + g_ims_tolerance=0.03, + g_ims_tolerance_unit="abs", ), out_prefix="lineprofile_results_tims/results", ) diff --git a/pyproject.toml b/pyproject.toml index 7bc9fe4..1edfe27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,7 @@ testpaths = [ [tool.ruff] line-length = 88 -select = ["E", "F", "W", "C", "I", "D", "UP", "N", "ANN", "T20"] +select = ["E", "F", "W", "C", "I", "D", "UP", "N", "ANN", "T20", "COM"] # ANN101 Missing type annotation for `self` in method # D213 Multi-line docstring summary should start at the second lin From 7fd636af96d81dafbe7b4a55f7a3593dd805c54c Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 Apr 2023 12:23:37 -0700 Subject: [PATCH 07/41] new deisotoping abstractions --- diadem/data_io/mzml.py | 71 +++- diadem/data_io/timstof.py | 116 ++++-- diadem/deisotoping.py | 263 +++++++------ diadem/index/fragment_buckets.py | 2 +- diadem/index/indexed_db.py | 2 +- diadem/search/diadem.py | 33 +- diadem/utilities/__init__.py | 0 diadem/utilities/neighborhood.py | 611 +++++++++++++++++++++++++++++++ diadem/{ => utilities}/utils.py | 0 profiling/get_data.bash | 2 + profiling/lineprofile_dia.zsh | 2 +- profiling/run_profile_tims.py | 11 +- pyproject.toml | 1 + tests/test_deisotoping.py | 23 +- 14 files changed, 965 insertions(+), 172 deletions(-) create mode 100644 diadem/utilities/__init__.py create mode 100644 diadem/utilities/neighborhood.py rename diadem/{ => utilities}/utils.py (100%) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 4fe5972..bb9dc47 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -18,7 +18,7 @@ from diadem.data_io.utils import slice_from_center, strictzip, xic from diadem.deisotoping import deisotope from diadem.search.metrics import get_ref_trace_corrs -from diadem.utils import check_sorted, plot_to_log +from diadem.utilities.utils import check_sorted, plot_to_log # TODO re-make some of these classes as ABCs # It would make intent explicit, since they are sub-classed @@ -174,6 +174,60 @@ def _scale_spectrum_at( self.base_peak_int[i] = -1 self.base_peak_mz[i] = -1 + def get_precursor_evidence( + self, + rt: float, + mzs: NDArray[np.float32], + mz_tolerance: float, + mz_tolerance_unit="ppm", + ): + # NOTE: This is a first implementation of the functionality, + # therefore it is very simple and prone to optimization and + # rework. + + # 1. Find the closest RT. + # 2. Find if there are peaks that match the mzs. + # 3. Return a list of dm and a list of intensities for each. + + index = np.searchsorted(self.precursor_rts, rt) + slc, center_index = slice_from_center( + index, + window=11, + length=len(self.precursor_mzs), + ) + q_mzs = self.precursor_mzs[index] + + q_intensities = self.precursor_intensities[index] + # TODO change preprocessing of the MS1 level to make it more + # permissive, cleaner spectra is not critical here. + + out_ints = [] + out_dms = [] + + if len(q_intensities) > 0: + for q_mzs, q_intensities in zip( + self.precursor_mzs[slc], + self.precursor_intensities[slc], + ): + intensities, indices = xic( + query_mz=q_mzs, + query_int=q_intensities, + mzs=mzs, + tolerance=mz_tolerance, + tolerance_unit=mz_tolerance_unit, + ) + dms = [q_mzs[inds] - match_mz for inds, match_mz in zip(indices, mzs)] + out_ints.append(intensities) + out_dms.append(dms) + + intensities = np.stack(out_ints, axis=0).sum(axis=0) + dms = out_dms[center_index] + else: + intensities = np.zeros_like(mzs) + dms = [[] for _ in range(len(mzs))] + + return intensities, dms + def __len__(self) -> int: """Returns the number of spectra in the group.""" return len(self.intensities) @@ -483,13 +537,14 @@ def _preprocess_scangroup( mzs = curr_spec.mz[order] intensities = curr_spec.intensity[order] - mzs, intensities = deisotope( - mzs, - intensities, - max_charge=5, - diff=self.config.g_tolerances[1], - unit=self.config.g_tolerance_units[1], - ) + if len(mzs) > 0: + mzs, intensities = deisotope( + mzs, + intensities, + max_charge=5, + diff=self.config.g_tolerances[1], + unit=self.config.g_tolerance_units[1], + ) npeaks_deisotope.append(len(mzs)) # TODO evaluate this scaling diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 0637b07..8d66d11 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -24,9 +24,10 @@ StackedChromatograms, ) from diadem.data_io.utils import slice_from_center, xic -from diadem.deisotoping import deisotope_with_ims + +# from diadem.deisotoping import deisotope_with_ims from diadem.search.metrics import get_ref_trace_corrs -from diadem.utils import is_sorted +from diadem.utilities.utils import is_sorted IMSError = Literal["abs", "pct"] @@ -240,7 +241,12 @@ def from_group( return out -def _bin_spectrum_intensities(mzs, intensities, bin_width=0.02, bin_offset=0): +def _bin_spectrum_intensities( + mzs: NDArray, + intensities: NDArray, + bin_width: float = 0.02, + bin_offset: float = 0.0, +) -> tuple[NDArray, NDArray, list[list[int]]]: """Bins the intensities based on the mz values. Returns @@ -256,7 +262,7 @@ def _bin_spectrum_intensities(mzs, intensities, bin_width=0.02, bin_offset=0): """ new_mzs, inv = np.unique( - ((mzs + bin_offset) / bin_width).astype("int"), + np.rint((mzs + bin_offset) / bin_width), return_inverse=True, ) new_mzs = (new_mzs * bin_width) - bin_offset @@ -373,18 +379,39 @@ def _precursor_iso_window_groups( elems = self._precursor_iso_window_elements(precursor_index, progress) out = {} for k, v in elems.items(): + # Note: precursor id of 0 means inactive quadrupole + # and collision cell. prec_info = self._precursor_iso_window_elements( 0, progress=progress, mz_range=v["precursor_range"], ) + assert len(list(prec_info)) == 1 + prec_key = list(prec_info)[0] + prec_info = prec_info[prec_key] + inten_filter = prec_info["base_peak_int"] > 0 + + precursor_mzs = [ + k for i, k in enumerate(prec_info["mzs"]) if inten_filter[i] + ] + precursor_intensities = [ + k for i, k in enumerate(prec_info["intensities"]) if inten_filter[i] + ] + precursor_imss = [ + k for i, k in enumerate(prec_info["imss"]) if inten_filter[i] + ] + precursor_rts = prec_info["retention_times"][inten_filter] + + assert is_sorted(precursor_rts) + out[k] = TimsScanGroup( - precursor_mzs=prec_info["mzs"], - precursor_intensities=prec_info["intensities"], - precursor_rts=prec_info["retention_times"], - precursor_imss=prec_info["imss"], + precursor_mzs=precursor_mzs, + precursor_intensities=precursor_intensities, + precursor_rts=precursor_rts, + precursor_imss=precursor_imss, **v, ) + return out def _precursor_iso_window_elements( self, @@ -543,7 +570,7 @@ def find_neighbors_mzsort( opts = {} for i1, (ims1, mz1) in enumerate(zip(ims_vals, sorted_mz_values)): - if top_indices is None or i1 not in top_indices: + if top_indices is not None and i1 not in top_indices: opts.setdefault(i1, []).append(i1) continue @@ -561,6 +588,8 @@ def find_neighbors_mzsort( def get_break_indices( inds: NDArray[np.int64], + min_diff: float | int = 1, + break_values: NDArray = None, ) -> tuple[NDArray[np.int64], NDArray[np.int64]]: """Gets the incides and break values for an increasing array. @@ -570,7 +599,9 @@ def get_break_indices( >>> get_break_indices(tmp) (array([0, 4, 7, 9]), array([ 1, 7, 11, 13])) """ - breaks = 1 + np.where(np.diff(inds) > 1)[0] + if break_values is None: + break_values = inds + breaks = 1 + np.where(np.diff(break_values) > min_diff)[0] breaks = np.concatenate([np.array([0]), breaks, np.array([inds.size - 1])]) break_indices = inds[breaks] @@ -581,19 +612,20 @@ def _get_precursor_index_windows( dia_data: TimsTOF, precursor_index: int, progress: bool = True, - mz_range=None, + mz_range: None | tuple[float, float] = None, ) -> dict[dict[list]]: PRELIM_N_PEAK_FILTER = 10_000 # noqa - MIN_INTENSITY_KEEP = 150 # noqa - MIN_NUM_SEED_BOXES = 1000 # noqa + MIN_INTENSITY_KEEP = 50 # noqa + MIN_NUM_SEEDS = 10_000 # noqa IMS_TOL = 0.01 # noqa # these tolerances are set narrow on purpose, since they # only delimit the definition of the neighborhood, which will iteratively # expanded. - MZ_TOL = 0.01 # noqa - MAX_ISOTOPE_CHARGE = 2 # noqa + MZ_TOL = 0.02 # noqa + MAX_ISOTOPE_CHARGE = 3 # noqa inds = dia_data[{"precursor_indices": precursor_index}, "raw"] + breaks, break_inds = get_break_indices(inds=inds) push_data = dia_data.convert_from_indices( @@ -656,7 +688,7 @@ def _get_precursor_index_windows( ims_values=ims_values, mz_values=mz_values, intensity_values=intensity_values, - top_n=MIN_NUM_SEED_BOXES, + top_n=MIN_NUM_SEEDS, ims_tol=IMS_TOL, mz_tol=MZ_TOL, ) @@ -668,16 +700,21 @@ def _get_precursor_index_windows( new_order = np.argsort(f["mz"]) f = {k: v[new_order] for k, v in f.items()} - d_out["mz"], d_out["intensity"], d_out["ims"] = deisotope_with_ims( - ims_diff=IMS_TOL, - ims_unit="abs", - imss=f["ims"], - inten=f["intensity"], - mz=f["mz"], - mz_unit="da", - track_indices=False, - mz_diff=MZ_TOL, - max_charge=MAX_ISOTOPE_CHARGE, + # d_out["mz"], d_out["intensity"], d_out["ims"] = deisotope_with_ims( + # ims_diff=IMS_TOL, + # ims_unit="abs", + # imss=f["ims"], + # inten=f["intensity"], + # mz=f["mz"], + # mz_unit="da", + # track_indices=False, + # mz_diff=MZ_TOL, + # max_charge=MAX_ISOTOPE_CHARGE, + # ) + d_out["mz"], d_out["intensity"], d_out["ims"] = ( + f["mz"], + f["intensity"], + f["ims"], ) pct_compression = round(len(d_out["mz"]) / start_len, ndigits=2) final_len = len(f["intensity"]) @@ -761,7 +798,8 @@ def collapse_ims( } ```. """ - MIN_NEIGHBORS_SEED = 6 # noqa + MIN_NEIGHBORS_SEED = 3 # noqa + assert is_sorted(mz_values) neighborhoods = find_neighbors_mzsort( ims_vals=ims_values, @@ -773,7 +811,7 @@ def collapse_ims( mz_tol=mz_tol, ) - unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} + # unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} seeds = {k: v for k, v in ambiguous.items() if len(v) >= MIN_NEIGHBORS_SEED} @@ -785,15 +823,7 @@ def collapse_ims( else: out_seeds, taken = {}, set() - # TODO consider whether I really want to keep ambiduous matches - # In theory I could keep only the expanded seeds. ambiguous_untaken = list(set(ambiguous).difference(taken)) - unambiguous_out = { - "ims": ims_values[list(chain(unambiguous, ambiguous_untaken))], - "mz": mz_values[list(chain(unambiguous, ambiguous_untaken))], - "intensity": intensity_values[list(chain(unambiguous, ambiguous_untaken))], - } - bundled = { "ims": np.zeros(len(out_seeds), dtype=np.float32), "mz": np.zeros(len(out_seeds), dtype=np.float32), @@ -808,7 +838,21 @@ def collapse_ims( bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity + # # # TODO consider whether I really want to keep ambiduous matches + # # # In theory I could keep only the expanded seeds. + # unambiguous_out = { + # "ims": ims_values[list(chain(unambiguous, ambiguous_untaken))], + # "mz": mz_values[list(chain(unambiguous, ambiguous_untaken))], + # "intensity": intensity_values[list(chain(unambiguous, ambiguous_untaken))], + # } + + unambiguous_out = { + "ims": ims_values[list(ambiguous_untaken)], + "mz": mz_values[list(ambiguous_untaken)], + "intensity": intensity_values[list(ambiguous_untaken)], + } final = { k: np.concatenate([unambiguous_out[k], bundled[k]]) for k in unambiguous_out } + return final diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index ee259c3..6e41aed 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -1,6 +1,8 @@ import numpy as np from numpy.typing import NDArray +from diadem.utilities.neighborhood import multidim_neighbor_search + NEUTRON = 1.00335 @@ -21,11 +23,114 @@ def ppm_to_delta_mass(obs: float, ppm: float) -> float: return ppm * obs / 1_000_000.0 +def _deisotope_with_ims_arrays( + mzs, + intensities, + imss=None, + max_charge=5, + ims_tolerance=0.01, + mz_tolerance=0.01, + track_indices=False, +): + """Deisotope a spectrum with IMS data. + + The current implementation allows only absolute values for the ims and mz + (no ppm). + + This function assumes that the IMS data has already been centroided (collapsed?) + and that the 3 arrays share ordering. + + If imss is None, will assume there is no IMS dimension in the data. + """ + if imss is None: + peak_iter = zip(mzs, intensities) + peaks = [ + { + "mz": mz, + "intensity": intensity, + "orig_intensity": intensity, + "envelope": None, + "charge": None, + } + for mz, intensity in peak_iter + ] + dim_order = ["mz"] + extract_values = ["mz", "intensity"] + spec = {"mz": mzs, "intensity": intensities} + else: + peak_iter = zip(mzs, intensities, imss) + peaks = [ + { + "mz": mz, + "ims": ims, + "intensity": intensity, + "orig_intensity": intensity, + "envelope": None, + "charge": None, + } + for mz, intensity, ims in peak_iter + ] + dim_order = ["mz", "ims"] + extract_values = ["mz", "intensity", "ims"] + spec = {"mz": mzs, "intensity": intensities, "ims": imss} + + if track_indices: + extract_values.append("indices") + for i, peak in enumerate(peaks): + peak["indices"] = [i] + + for charge in range(max_charge + 1, 0, -1): + dist_ranges = { + "mz": ( + (NEUTRON / charge) - mz_tolerance, + (NEUTRON / charge) + mz_tolerance, + ), + "ims": (-ims_tolerance, ims_tolerance), + } + + out = multidim_neighbor_search( + spec, + spec, + dist_ranges=dist_ranges, + order=dim_order, + force_vectorized=True, # This is up to optimization. + ) + + isotope_graph = out.left_neighbors + for i in np.argsort(-spec["mz"]): + if i not in isotope_graph: + continue + + for j in isotope_graph[i]: + # 0.5 is a magical number ... it is meant to account + # for the fact that intensity should in theory always increase + # (except for envelopes of high mass) but should also have some + # wiggle room for noise. + intensity_inrange = ( + 0.5 * (peaks[j]["orig_intensity"]) < peaks[i]["orig_intensity"] + ) + if intensity_inrange and peaks[j]["envelope"] is None: + peaks[i]["intensity"] += peaks[j]["intensity"] + peaks[j]["charge"] = charge + peaks[i]["charge"] = charge + peaks[j]["envelope"] = i + if track_indices: + peaks[i]["indices"].extend(peaks[j]["indices"]) + # At the end of this, all peaks that belong to an envelope + # have a value for "envelope". + # Therefore, peaks that need to be filtered out (since their + # intensity now bleongs to another peak). + + f_peaks = _filter_peaks(peaks, extract=extract_values) + f_peaks = {k: v for k, v in zip(extract_values, f_peaks)} + return f_peaks + + # Ported implementation from sage # @profile def deisotope( - mz: NDArray[np.float32] | list[float], - inten: NDArray[np.float32] | list[float], + mz: NDArray[np.float32], + inten: NDArray[np.float32], max_charge: int, diff: float, unit: str, @@ -52,56 +157,33 @@ def deisotope( Examples -------- - >>> my_mzs = [800.9, 803.408, 804.4108, 805.4106] - >>> my_intens = [1-(0.1*i) for i,_ in enumerate(my_mzs)] + >>> my_mzs = np.array([800.9, 803.408, 804.4108, 805.4106]) + >>> my_intens = np.array([1-(0.1*i) for i,_ in enumerate(my_mzs)]) >>> deisotope(my_mzs, my_intens, max_charge=2, diff=5.0, unit="ppm") (array([800.9 , 803.408]), array([1. , 2.4])) >>> deisotope(my_mzs, my_intens, max_charge=2, diff=5.0, unit="ppm", track_indices=True) (array([800.9 , 803.408]), array([1. , 2.4]), ([0], [1, 2, 3])) """ if unit.lower() == "da": - - def mass_unit_fun(x: float, y: float) -> float: - return y + mz_tolerance = diff elif unit.lower() == "ppm": - mass_unit_fun = ppm_to_delta_mass + # Might give wider tolerances than wanted to the lower end of the values + # but should be good enough for most cases. + mz_tolerance = ppm_to_delta_mass(mz.max(), diff) else: raise NotImplementedError("Masses need to be either 'da' or 'ppm'") - peaks = [(mz, intensity) for mz, intensity in zip(mz, inten)] - peaks = [ - {"mz": mz, "intensity": intensity, "envelope": None, "charge": None} - for mz, intensity in peaks - ] - for i in range(len(peaks)): - peaks[i]["indices"] = [i] - - for i in range(len(mz) - 1, -1, -1): - j = i - 1 - while j >= 0 and mz[i] - mz[j] <= NEUTRON + mass_unit_fun(mz[i], diff): - delta = mz[i] - mz[j] - tol = mass_unit_fun(mz[i], diff) - for charge in range(1, max_charge + 1): - iso = NEUTRON / charge - # Note, this assumes that the isotope envelope always decreases in - # intensity, which is not accurate for high mol weight fragments. - if abs(delta - iso) <= tol and inten[i] < inten[j]: - peaks[j]["intensity"] += peaks[i]["intensity"] - if track_indices: - peaks[j]["indices"].extend(peaks[i]["indices"]) - if peaks[i]["charge"] and peaks[i]["charge"] != charge: - continue - peaks[j]["charge"] = charge - peaks[i]["charge"] = charge - peaks[i]["envelope"] = j - j -= 1 + peaks = _deisotope_with_ims_arrays( + imss=None, + mzs=mz, + intensities=inten, + max_charge=max_charge, + mz_tolerance=mz_tolerance, + track_indices=track_indices, + ) - if not track_indices: - peaks = _filter_peaks(peaks, extract=("mz", "intensity")) - else: - peaks = _filter_peaks(peaks, extract=("mz", "intensity", "indices")) - return peaks + return tuple(peaks.values()) # TODO this is a function prototype, if it works abstract it and @@ -110,9 +192,9 @@ def mass_unit_fun(x: float, y: float) -> float: # @profile def deisotope_with_ims( - mz: NDArray[np.float32] | list[float], - inten: NDArray[np.float32] | list[float], - imss: NDArray[np.float32] | list[float], + mz: NDArray[np.float32], + inten: NDArray[np.float32], + imss: NDArray[np.float32], max_charge: int, mz_diff: float, mz_unit: str, @@ -147,96 +229,45 @@ def deisotope_with_ims( Examples -------- - >>> my_mzs = [800.9, 803.408, 803.409, 804.4108, 804.4109, 805.4106] - >>> my_imss = [0.7, 0.7, 0.8, 0.7, 0.8, 0.7] - >>> my_intens = [1-(0.1*i) for i,_ in enumerate(my_mzs)] - >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs") + >>> my_mzs = np.array([800.9, 803.408, 803.409, 804.4108, 804.4109, 805.4106]) + >>> my_imss = np.array([0.7, 0.7, 0.8, 0.7, 0.8, 0.7]) + >>> my_intens = np.array([1-(0.1*i) for i,_ in enumerate(my_mzs)]) + >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, + ... mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs") (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), array([0.7, 0.7, 0.8])) - >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs", track_indices=True) + >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, + ... mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs", track_indices=True) (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), array([0.7, 0.7, 0.8]), ([0], [1, 3, 5], [2, 4])) """ if mz_unit.lower() == "da": - - def mass_unit_fun(x: float, y: float) -> float: - return y + mz_tolerance = mz_diff elif mz_unit.lower() == "ppm": - mass_unit_fun = ppm_to_delta_mass + mz_tolerance = ppm_to_delta_mass(mz.max(), mz_diff) else: raise NotImplementedError("Masses need to be either 'da' or 'ppm'") if ims_unit.lower() == "abs": - - def ims_unit_fun(x: float, y: float) -> float: - return y + ims_tolerance = ims_diff else: raise NotImplementedError("only abs is supported as an IMS difference") - peaks = [(mz, intensity, ims) for mz, intensity, ims in zip(mz, inten, imss)] - peaks = [ - { - "mz": mz, - "ims": ims, - "intensity": intensity, - "ims_range": None, - "envelope": None, - "charge": None, - } - for mz, intensity, ims in peaks - ] - for i in range(len(peaks)): - peaks[i]["indices"] = [i] - - for i in range(len(mz) - 1, -1, -1): - j = i - 1 - while j >= 0 and mz[i] - mz[j] <= NEUTRON + mass_unit_fun(mz[i], mz_diff): - delta = mz[i] - mz[j] - ims_delta = imss[i] - imss[j] - mz_tol = mass_unit_fun(mz[i], mz_diff) - ims_tol = ims_unit_fun(imss[i], ims_diff) - matches_ims = abs(ims_delta) <= ims_tol - if matches_ims: - for charge in range(1, max_charge + 1): - iso = NEUTRON / charge - # Note, this assumes that the isotope envelope always decreases in - # intensity, which is not accurate for high mol weight fragments. - intensity_increases = inten[i] < inten[j] - matches_mz = abs(delta - iso) <= mz_tol - - if (c_ims_range := peaks[i]["ims_range"]) is None: - c_ims_range = (imss[i] - ims_tol, imss[i] + ims_tol) - matches_envelope_ims = True - else: - matches_envelope_ims = ( - imss[j] >= c_ims_range[0] and imss[j] <= c_ims_range[1] - ) - if matches_mz and intensity_increases and matches_envelope_ims: - # Why does it add intensities before checking the charge? - peaks[j]["intensity"] += peaks[i]["intensity"] - peaks[j]["ims_range"] = ( - max(c_ims_range[0], imss[j] - ims_tol), - min(c_ims_range[1], imss[j] + ims_tol), - ) - if track_indices: - peaks[j]["indices"].extend(peaks[i]["indices"]) - if peaks[i]["charge"] and peaks[i]["charge"] != charge: - continue - peaks[j]["charge"] = charge - peaks[i]["charge"] = charge - peaks[i]["envelope"] = j - - j -= 1 - - if not track_indices: - peaks = _filter_peaks(peaks, extract=("mz", "intensity", "ims")) - else: - peaks = _filter_peaks(peaks, extract=("mz", "intensity", "ims", "indices")) - return peaks + peaks = _deisotope_with_ims_arrays( + imss=imss, + mzs=mz, + intensities=inten, + max_charge=max_charge, + mz_tolerance=mz_tolerance, + ims_tolerance=ims_tolerance, + track_indices=track_indices, + ) + + return tuple(peaks.values()) def _filter_peaks( - peaks: dict, + peaks: list[dict], extract: tuple[str], ) -> tuple[NDArray[np.float32] | list, ...]: """Filters peaks to remove isotope envelopes. diff --git a/diadem/index/fragment_buckets.py b/diadem/index/fragment_buckets.py index d36fec4..9883510 100644 --- a/diadem/index/fragment_buckets.py +++ b/diadem/index/fragment_buckets.py @@ -8,7 +8,7 @@ from numpy.typing import NDArray from tqdm.auto import tqdm -from diadem.utils import get_slice_inds, is_sorted +from diadem.utilities.utils import get_slice_inds, is_sorted SortingLevel = Literal["ms1", "ms2"] diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 679003f..e9d0945 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -26,7 +26,7 @@ FragmentBucketList, PrefilteredMS1BucketList, ) -from diadem.utils import disabled_gc, is_sorted, make_decoy +from diadem.utilities.utils import disabled_gc, is_sorted, make_decoy # Pre-calculating factorials so I do not need # to calculate them repeatedly while scoring diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 82ab6cc..2d0686d 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -17,7 +17,7 @@ from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta from diadem.search.search_utils import make_pin -from diadem.utils import plot_to_log +from diadem.utilities.utils import plot_to_log # @profile @@ -54,6 +54,9 @@ def search_group( MS2_TOLERANCE = config.g_tolerances[1] # noqa MS2_TOLERANCE_UNIT = config.g_tolerance_units[1] # noqa + MS1_TOLERANCE = config.g_tolerances[0] # noqa + MS1_TOLERANCE_UNIT = config.g_tolerance_units[0] # noqa + IMS_TOLERANCE = config.g_ims_tolerance # noqa IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa MAX_NUM_CONSECUTIVE_FAILS = 50 # noqa @@ -88,6 +91,8 @@ def search_group( index_log = [] fwhm_log = [] num_peaks = 0 + num_targets = 0 + num_decoys = 0 # Fail related variables num_fails = 0 @@ -162,13 +167,33 @@ def search_group( spec_mz=new_stack.mzs, top_n=100, ) - # TODO add here a filter for precursor evidence and re-rank - logger.error("Sebastian has not implemented this!!") + + # if scores is not None: + # rt = group.retention_times[new_stack.ref_index] + # prec_intensity, prec_dms = group.get_precursor_evidence( + # rt, + # mzs=scores["PrecursorMZ"].values, + # mz_tolerance=MS1_TOLERANCE, + # mz_tolerance_unit=MS1_TOLERANCE_UNIT, + # ) + # scores["PrecursorIntensity"] = prec_intensity + # scores.drop(scores[scores.PrecursorIntensity < 100].index, inplace = True) + # scores.reset_index(drop=True, inplace=True) + # scores["rank"] = scores["Score"].rank(ascending=False, method="min") + # if len(scores) == 0: + # logger.debug("All scores were removed due to precursor filtering") + # scores = None + else: scores = None if scores is not None: scores["id"] = match_id + if scores["decoy"].iloc[0]: + num_decoys += 1 + else: + num_targets += 1 + match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) @@ -243,6 +268,8 @@ def search_group( "max_intensity": curr_highest_peak_int, "num_fails": num_fails, "num_scores": len(group_results), + "n_targets": num_targets, + "n_decoys": num_decoys, }, ) pbar.update(1) diff --git a/diadem/utilities/__init__.py b/diadem/utilities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py new file mode 100644 index 0000000..df3a177 --- /dev/null +++ b/diadem/utilities/neighborhood.py @@ -0,0 +1,611 @@ +"""Contians and implements ways to look for and represent neighbors. + +This module contains implementations related to finding "neighbors" +and representations for those neighbors. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +import numpy as np +from numpy.typing import NDArray + + +@dataclass +class IndexBipartite: + """Simple bipartite graph structure. + + This dataclass is meant to contain connections between indices + in other structures. + + Examples + -------- + >>> bg = IndexBipartite() + >>> bg.add_connection(1,2) + >>> bg.add_connection(1,3) + >>> bg.add_connection(2,2) + >>> bg + IndexBipartite(left_neighbors={1: [2, 3], 2: [2]}, + right_neighbors={2: [1, 2], 3: [1]}) + >>> bg.get_neighbor_indices() + (array([1, 2]), array([2, 3])) + """ + + left_neighbors: dict = field(default_factory=dict) + right_neighbors: dict = field(default_factory=dict) + + def add_connection(self, left: int, right: int) -> None: + """Adds a connection to the graph. + + See the class docstring for details. + """ + self.left_neighbors.setdefault(left, []).append(right) + self.right_neighbors.setdefault(right, []).append(left) + + def get_neighbor_indices(self) -> tuple[NDArray, NDArray]: + """Returns the neighborhoods as two arrays. + + This returns two arrays that represent all indices tht have + a neghbor in their corresponding counterpart. + + Returns + ------- + x, y: tuple[NDArray, NDAarray] + an array representing the indices that have a neighbor. + In other words, all elements with index xi will have a + neighbor in y. + + Every element in x and every element in y is unique. + + Examples + -------- + >>> bg = IndexBipartite() + >>> bg.add_connection(1,1) + >>> bg.add_connection(1,2) + >>> bg.add_connection(1,3) + >>> bg.add_connection(2,2) + >>> bg.get_neighbor_indices() + (array([1, 2]), array([1, 2, 3])) + """ + x = np.sort(np.array(list(self.left_neighbors))) + y = np.sort(np.array(list(self.right_neighbors))) + return x, y + + def get_matching_indices(self) -> tuple[NDArray, NDArray]: + """Returns the matching indices in the neighborhood as two arrays. + + Note: this differs from the `get_neighbor_indices` function + because it assures that the lengths of both arrays are the same. + Therefore + + This returns two arrays that represent all indices tht have + a neghbor in their corresponding counterpart. + + Returns + ------- + x,y: tuple[NDArray, NDArray] + Returns two arrays where every element i in array x is + a neighbor of element i in array y. + + This entails that there will be duplicates if for instance + an element in x has many neighbors in y. + + Examples + -------- + >>> bg = IndexBipartite() + >>> bg.add_connection(1,1) + >>> bg.add_connection(1,2) + >>> bg.add_connection(1,3) + >>> bg.add_connection(2,2) + >>> bg.get_matching_indices() + (array([1, 1, 1, 2]), array([1, 2, 3, 2])) + """ + out_x = [] + out_y = [] + + for x in self.left_neighbors.keys(): + for xn in self.left_neighbors[x]: + out_x.append(x) + out_y.append(xn) + + return np.array(out_x), np.array(out_y) + + @classmethod + def from_arrays(cls, x: NDArray, y: NDArray) -> IndexBipartite: + """Builds a bipartite index from two arrays. + + Arguments: + --------- + x, y: NDArray + Two arrays of the same length that contain the indices + that match between them. For example element i in the + array y is a neighbor of element i in array x. + + Examples: + -------- + >>> a = np.array([1,1,1,51]) + >>> b = np.array([2,3,4,1]) + >>> IndexBipartite.from_arrays(a,b) + IndexBipartite(left_neighbors={1: [2, 3, 4], 51: [1]}, + right_neighbors={2: [1], 3: [1], 4: [1], 1: [51]}) + """ + if len(x) != len(y): + raise ValueError("x and y must be the same length") + + new = cls() + for x1, y1 in zip(x, y): + new.add_connection(x1, y1) + + return new + + def intersect(self, other: IndexBipartite) -> IndexBipartite: + """Returns the intersection of two bipartite graphs. + + Arguments: + --------- + other: IndexBipartite + The other bipartite graph to intersect with. + + Returns: + ------- + IndexBipartite + A new bipartite graph that contains the intersection of + both graphs. + + Examples: + -------- + >>> bg1 = IndexBipartite() + >>> bg1.add_connection(1,1) + >>> bg1.add_connection(1,2) + >>> bg1.add_connection(1,3) + >>> bg1.add_connection(1,4) + >>> bg1.add_connection(2,2) + >>> bg2 = IndexBipartite() + >>> bg2.add_connection(1,1) + >>> bg2.add_connection(1,2) + >>> bg2.add_connection(1,3) + >>> bg2.add_connection(2,2) + >>> bg2.add_connection(2,3) + >>> bg1.intersect(bg2) + IndexBipartite(left_neighbors={1: [1, 2, 3], 2: [2]}, + right_neighbors={1: [1], 2: [1, 2], 3: [1]}) + """ + new = IndexBipartite() + + for x in self.left_neighbors.keys(): + if x in other.left_neighbors: + for xn in self.left_neighbors[x]: + if xn in other.left_neighbors[x]: + new.add_connection(x, xn) + + return new + + +def _default_dist_fun(x: float, y: float) -> float: + """Default distance function to use.""" + return y - x + + +@dataclass +class NeighborFinder: + """Class to find neighbors in a multidimensional space. + + This class is used to find neighbors in a multidimensional space. + + Parameters + ---------- + dist_ranges: dict[str, tuple[float, float]] + A dictionary that contains the ranges of the distances + for each dimension. The keys of the dictionary are the + names of the dimensions and the values are tuples that + contain the minimum and maximum distance for that dimension. + dist_funs: dict[str, callable] + A dictionary that contains the distance functions for each + dimension. The keys of the dictionary are the names of the + dimensions and the values are the distance functions. + The distance functions must take two arguments and return + a float. + order: tuple[str] + The order in which the dimensions should be searched. + If this is None then the order will be the same as the + order of the keys in the dist_ranges dictionary. + force_vectorized: bool + This forces the search to use a vectorized implementation that + might be faster depending on the use case, in theory does more + operations but those operations happen in cpu cache. (test it ...) + """ + + dist_ranges: dict[str, tuple[float, float]] + dist_funs: dict[str, callable] + order: tuple[str] + force_vectorized: bool + + def __post_init__(self) -> None: + """Post init function.""" + if self.order is None: + self.order = tuple(self.dist_ranges.keys()) + + if self.dist_funs is None: + self.dist_funs = {k: _default_dist_fun for k in self.dist_ranges} + + if not set(self.dist_ranges.keys()) == set(self.dist_funs.keys()): + raise ValueError("dist_ranges and dist_funs must have the same keys") + + def find_neighbors( + self, + elems1: dict[str, NDArray], + elems2: dict[str, NDArray], + ) -> IndexBipartite: + """Finds neighbors in a multidimensional space.""" + out = multidim_neighbor_search( + elems1=elems1, + elems2=elems2, + dist_ranges=self.dist_ranges, + dist_funs=self.dist_funs, + order=self.order, + force_vectorized=self.force_vectorized, + ) + return out + + +def multidim_neighbor_search( + elems1: dict[str, NDArray], + elems2: dict[str, NDArray], + dist_ranges: dict[str, tuple[float, float]], + dist_funs: None | dict[str, callable] = None, + order: None | tuple[str] = None, + force_vectorized: bool = False, +) -> IndexBipartite: + """Searches for neighbors in multiple dimensions. + + Arguments: + --------- + elems1 and elems2, dict[str,NDArray]: + A dictionary of arrays. + All arrays within one of those elements need to have the same + length. + dist_ranges, dict[str, tuple[float, float]]: + maximum and minimum ranges for each of the dimensions. + dist_funs: + Dictionary of functions used to calculate distances. + For details check the documentation of `find_neighbors_sorted` + order, optional str: + Optional tuple of strings denoting what dimensions to use. + force_vectorized, bool + Whether to force the use of the vectorized version of the + matching functions. + This in theory will be slower, but bypasses iterating in cpython. + + Examples: + -------- + >>> x1 = {"d1": np.array([1000., 1000., 2001., 3000.]), + ... "d2": np.array([1000., 1000.3, 2000., 3000.01])} + >>> x2 = {"d1": np.array([1000.01, 1000.01, 2000., 3000.]), + ... "d2": np.array([1000.01, 1000.01, 2000., 3001.01])} + >>> d_funs = {"d1": lambda x,y: 1e6 * (y-x)/x, "d2": lambda x,y: y-x} + >>> d_ranges = {"d1": (-10, 10), "d2": (-0.02, 0.02)} + >>> multidim_neighbor_search( + ... x1, x2, d_ranges, d_funs + ... ) + IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) + >>> multidim_neighbor_search( + ... x1, x2, d_ranges, d_funs, force_vectorized=True + ... ) + IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) + """ + if order is None: + order = list(elems1) + + # This makes sure all elements are the same length + array_length_x = len(elems1[order[0]]) + array_length_y = len(elems1[order[0]]) + assert all(len(elems1[e]) == array_length_x for e in order) + assert all(len(elems2[e]) == array_length_y for e in order) + + if not force_vectorized: + xi = np.arange(array_length_x) + yi = np.arange(array_length_y) + last_graph = None + + for curr in order: + x = elems1[curr] + y = elems2[curr] + dist_fun = _default_dist_fun if dist_funs is None else dist_funs[curr] + + # Filter to keep only the allowable matches + x = x[xi] + y = y[yi] + + # Sort the x and y arrays, keeping track of the original order + # For example: + # if the array x is [6,7,2] + # the sorted array would [2,6,7] + # the order would be [2, 0, 1] + # and therefore to get the original index of element 1 in the sorted + # array (6) one would use ... + # order_array[i] (0 in this case) + x_order = np.argsort(x) + y_order = np.argsort(y) + neighbors = find_neighbors_sorted( + x[x_order], + y[y_order], + dist_fun=dist_fun, + low_dist=dist_ranges[curr][0], + high_dist=dist_ranges[curr][1], + ) + t_xi, t_yi = neighbors.get_matching_indices() + if len(t_xi) == 0 or len(t_yi) == 0: + # Handles the case where no neighbors are possible + return IndexBipartite() + o_xi = xi[x_order[t_xi]] + o_yi = yi[y_order[t_yi]] + + xi = xi[x_order[np.unique(t_xi)]] + yi = yi[y_order[np.unique(t_yi)]] + + orig_index_graph = IndexBipartite.from_arrays(o_xi, o_yi) + + # Get the indices of x and y in the raw array + if last_graph is None: + last_graph = orig_index_graph + else: + last_graph = last_graph.intersect(orig_index_graph) + + else: + # Since the vectorzed version greedily computes all distances, + # This version can be optimized by not subsetting on every iteration. + # And just calculating all distances and checking which are in range. + # And then combining the results. + # This is in theory faster because it avoids the overhead of the + # subsetting and tracking indices. + if dist_funs is None: + dist_funs = {o: _default_dist_fun for o in order} + dist_funs = [dist_funs[o] for o in order] + dist_ranges = [dist_ranges[o] for o in order] + low_ranges = [d[0] for d in dist_ranges] + high_ranges = [d[1] for d in dist_ranges] + xs = [elems1[o] for o in order] + ys = [elems2[o] for o in order] + + neighbors = find_neighbors_multi_vectorized( + xs, + ys, + dist_funs=dist_funs, + low_dists=low_ranges, + high_dists=high_ranges, + ) + last_graph = neighbors + + return last_graph + + +def find_neighbors_sorted( + x: NDArray, + y: NDArray, + dist_fun: callable, + low_dist: float, + high_dist: float, +) -> IndexBipartite: + """Finds neighbors between to sorted arrays. + + Arguments: + --------- + x, NDArray: + First array to use to find neighbors + y, NDArray: + Second array to use to find neighbors + dist_fun: + Function to calculate the distance between an element + in `x` and an element in `y`. + Note that this asumes directionality and should increase in value. + In other words ... + dist_fun(x[0], y[0]) < dist_fun(x[0], y[1]); assuming that y[1] > y[0] + low_dist: + Lowest value allowable as a distance for two elements + to be considered neighbors + low_dist: + Highest value allowable as a distance for two elements + to be considered neighbors + + Examples: + -------- + >>> x = np.array([1.,2.,3.,4.,5.,15.,25.]) + >>> y = np.array([1.1, 2.3, 3.1, 4., 25., 25.1]) + >>> dist_fun = lambda x,y: y - x + >>> low_dist = -0.11 + >>> high_dist = 0.11 + >>> find_neighbors_sorted(x,y,dist_fun,low_dist, high_dist) + IndexBipartite(left_neighbors={0: [0], 2: [2], 3: [3], 6: [4, 5]}, + right_neighbors={0: [0], 2: [2], 3: [3], 4: [6], 5: [6]}) + """ + assert low_dist < high_dist + neighbors = IndexBipartite() + + ii = 0 + for i in range(len(x)): + x_val = x[i] + last_diff = None + for j in range(ii, len(y)): + y_val = y[j] + + diff = dist_fun(x_val, y_val) + + # TODO disable this for performance ... + if last_diff is not None: + assert diff >= last_diff + last_diff = diff + + if diff < low_dist: + ii = j + continue + if diff > high_dist: + break + + assert diff <= high_dist and diff >= low_dist + neighbors.add_connection(i, j) + return neighbors + + +def find_neighbors_multi_vectorized( + x: list[NDArray], + y: list[NDArray], + dist_funs: list[callable], + low_dists: list[float], + high_dists: list[float], +) -> IndexBipartite: + """Finds Neighbors in multiple dimensions. + + This is the generalized version of `find_neighbors_vectorized`. + This function allows for multiple dimensions to be used to find neighbors. + + It is more efficient than calling the `find_neighbors_vectorized` function + multiple times because it does not require many intermediate representations + of the data. + + In addition it (in theory) does some of the merging operations in a vectorized + manner, which should be faster due to the overhead of cpu-cache moving overhead. + + Arguments: + --------- + x, y list[NDArray]: + List of arrays to use to find neighbors. + the two lists need to be the same length and the length of each element in + each of the lists needs to be the same length. + For example: if x is a list of 5 arrays, each of length 200; then y needs to + be a list of 5 arrays, but the length of each sub-array can be any length + (as long as they are all the same...). + dist_funs, list[callable]: + List of functions to calculate the distance between an element + in `x` and an element in `y`. + The list should be the same length as `x` and `y`. + Note that this asumes directionality and should increase in value. + + In other words ... + dist_fun(x[0], y[0]) < dist_fun(x[0], y[1]); assuming that y[1] > y[0] + low_dists, list[float]: + List of lowest value allowable as a distance for two elements + to be considered neighbors. + The list should be the same length as `x` and `y`. + high_dists, list[float]: + List of highest value allowable as a distance for two elements + to be considered neighbors. + + Returns: + ------- + IndexBipartite: + The neighbors found between the two arrays. + """ + neighbors = IndexBipartite() + final_in_range = None + + my_iter = zip( + x, + y, + dist_funs, + low_dists, + high_dists, + ) + for xi, yi, dist_fun, low_dist, high_dist in my_iter: + diffs = _apply_vectorized(xi, yi, dist_fun) + in_range = (diffs > low_dist) & (diffs < high_dist) + if final_in_range is None: + final_in_range = in_range + else: + final_in_range = final_in_range & in_range + + inds = np.where(final_in_range) + neighbors = IndexBipartite.from_arrays(*inds) + return neighbors + + +def find_neighbors_vectorized( + x: NDArray, + y: NDArray, + dist_fun: callable, + low_dist: float, + high_dist: float, +) -> IndexBipartite: + """Finds neighbors between to sorted arrays. + + This version uses a vectorized version of the calculation. + In theory it should take longer, since it calculates all + distances between elements, BUT due to vectorization, cpu caching + and not going through the iteration in python. + + Arguments: + --------- + x, NDArray: + First array to use to find neighbors + y, NDArray: + Second array to use to find neighbors + dist_fun: + Function to calculate the distance between an element + in `x` and an element in `y`. + Note that this asumes directionality and should increase in value. + In other words ... + dist_fun(x[0], y[0]) < dist_fun(x[0], y[1]); assuming that y[1] > y[0] + low_dist: + Lowest value allowable as a distance for two elements + to be considered neighbors + low_dist: + Highest value allowable as a distance for two elements + to be considered neighbors + + Examples: + -------- + >>> x = np.array([1.,2.,3.,4.,5.,15.,25.]) + >>> y = np.array([1.1, 2.3, 3.1, 4., 25., 25.1]) + >>> dist_fun = lambda x,y: y - x + >>> low_dist = -0.11 + >>> high_dist = 0.11 + >>> find_neighbors_vectorized(x,y,dist_fun,low_dist, high_dist) + IndexBipartite(left_neighbors={0: [0], 2: [2], 3: [3], 6: [4, 5]}, + right_neighbors={0: [0], 2: [2], 3: [3], 4: [6], 5: [6]}) + >>> low_dist = -1.11 + >>> high_dist = -0.98 + >>> out = find_neighbors_vectorized(x,y,dist_fun,low_dist, high_dist) + >>> out + IndexBipartite(left_neighbors={4: [3]}, right_neighbors={3: [4]}) + >>> [[f"{x[k]} matches {y[w]}" for w in v] for k, v in out.left_neighbors.items()] + [['5.0 matches 4.0']] + """ + assert low_dist < high_dist + neighbors = IndexBipartite() + + diffs = _apply_vectorized(x, y, dist_fun) + in_range = (diffs > low_dist) & (diffs < high_dist) + inds = np.where(in_range) + neighbors = IndexBipartite.from_arrays(*inds) + return neighbors + + +def _apply_vectorized(x: NDArray, y: NDArray, fun: callable) -> NDArray: + """Applies a function to all combinations of elements in x and y. + + Arguments: + --------- + x, y: NDArray + One dimensional Arrays to apply the function to + fun: callable + Function to apply to all combinations of elements in x and y + + Returns: + ------- + NDArray + Array of shape (len(x), len(y)) with the results of the function + applied to all combinations of elements in x and y + + Examples: + -------- + >>> x = np.array([1.,5.,15.,25.]) + >>> y = np.array([1.1, 25.1]) + >>> fun = lambda x,y: y - x + >>> _apply_vectorized(x,y,fun) + array([[ 0.1, 24.1], + [ -3.9, 20.1], + [-13.9, 10.1], + [-23.9, 0.1]]) + """ + outs = fun(np.tile(np.expand_dims(x, axis=-1), len(y)), y) + return outs diff --git a/diadem/utils.py b/diadem/utilities/utils.py similarity index 100% rename from diadem/utils.py rename to diadem/utilities/utils.py diff --git a/profiling/get_data.bash b/profiling/get_data.bash index cdcbf57..8f1f3cf 100644 --- a/profiling/get_data.bash +++ b/profiling/get_data.bash @@ -1,6 +1,8 @@ #!/bin/bash mkdir -p profiling_data + +# TODO replace for brian's data aws s3 cp --profile mfa s3://data-pipeline-mzml-bucket/221229_ChessFest_Plate3/Chessfest_Plate3_RH4_DMSO_DIA.mzML.gz ./profiling_data/. aws s3 cp --profile mfa s3://data-pipeline-metadata-bucket/uniprot_human_sp_canonical_2021-11-19_crap.fasta ./profiling_data/. curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_PASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_PASEF.d.zip diff --git a/profiling/lineprofile_dia.zsh b/profiling/lineprofile_dia.zsh index af43837..1f6aab8 100644 --- a/profiling/lineprofile_dia.zsh +++ b/profiling/lineprofile_dia.zsh @@ -3,7 +3,7 @@ # what you want profiled mkdir -p lineprofile_results sed -ie "s/# @profile/@profile/g" ../diadem/**/*.py -python -m pip install -e ../. +python -m pip install ../. sed -ie "s/@profile/# @profile/g" ../diadem/**/*.py set -x diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 7548e16..1354d90 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -4,14 +4,15 @@ cli.setup_logger() cli.diadem_main( fasta_path="./profiling_data/UP000005640_9606.fasta", - data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", + # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", + data_path="./profiling_data/Hela_25ng_22min_6x3_short_S2-A4_1_118.hdf", config=DiademConfig( run_parallelism=1, # Needs to use 1 core for profiling run_max_peaks=20000, - run_allowed_fails=500, - g_tolerances=(25, 25), - g_tolerance_units=("ppm", "ppm"), - g_ims_tolerance=0.03, + run_allowed_fails=5000, + g_tolerances=(0.03, 0.03), + g_tolerance_units=("da", "da"), + g_ims_tolerance=0.04, g_ims_tolerance_unit="abs", ), out_prefix="lineprofile_results_tims/results", diff --git a/pyproject.toml b/pyproject.toml index 1edfe27..f39c1eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ include = ["diadem"] [tool.pytest.ini_options] minversion = "6.0" addopts = "--doctest-modules -v" +doctest_optionflags = "NORMALIZE_WHITESPACE" testpaths = [ "diadem", "tests", diff --git a/tests/test_deisotoping.py b/tests/test_deisotoping.py index b1a79b2..29a41f9 100644 --- a/tests/test_deisotoping.py +++ b/tests/test_deisotoping.py @@ -1,7 +1,27 @@ import numpy as np +from numpy import array # noqa: F401 from diadem.deisotoping import NEUTRON, deisotope +single_clean_envelope_text = """ +{ + "mz": array( + [470.2321, 469.5762, 469.2416, 469.9097, + 470.2435, 470.5771, 467.7124, 467.2108, 471.6956, ] + ), + "ims": array( + [0.77803, 0.79777, 0.79688, 0.79611, 0.79404, + 0.79568, 0.80016, 0.79466, 0.81096, ] + ), + "intensity": array( + [92.0, 13729.0, 18491.0, 7138.0, 3081.0, + 1739.0, 751.0, 356.0, 293.0] + ), +} +""" + +single_clean_envelope = eval(single_clean_envelope_text) + def test_deisotoping() -> None: """Tests that isotopes are collapsed correctly.""" @@ -15,7 +35,8 @@ def test_deisotoping() -> None: 812.0, # Envelope 812.0 + NEUTRON / 2.0, ] - inten = [1.0, 4.0, 3.0, 2.0, 1.0, 1.0, 9.0, 4.5] + mz = np.array(mz) + inten = np.array([1.0, 4.0, 3.0, 2.0, 1.0, 1.0, 9.0, 4.5]) out_mz, out_inten = deisotope(mz, inten, 2, 5.0, "ppm") assert np.allclose(out_inten, np.array([1.0, 10.0, 1.0, 13.5])) assert np.allclose(out_mz, np.array([800.9, 803.408, 810.0, 812.0])) From d37cdd42d28ba84a2ae5ef27923bcff0ba51ab7e Mon Sep 17 00:00:00 2001 From: Carolyn Allen Date: Tue, 11 Apr 2023 13:31:18 -0700 Subject: [PATCH 08/41] initial rt model --- diadem/rt_alignment/__init__.py | 0 diadem/rt_alignment/rt_model.py | 244 ++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 diadem/rt_alignment/__init__.py create mode 100644 diadem/rt_alignment/rt_model.py diff --git a/diadem/rt_alignment/__init__.py b/diadem/rt_alignment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diadem/rt_alignment/rt_model.py b/diadem/rt_alignment/rt_model.py new file mode 100644 index 0000000..e7fd51f --- /dev/null +++ b/diadem/rt_alignment/rt_model.py @@ -0,0 +1,244 @@ +"""This module contains a matrix factorization model.""" +import logging + +import numpy as np +import pandas as pd +import torch +import torch.nn as nn +from tqdm import trange + +LOGGER = logging.getLogger(__name__) + + +class MatrixModel(nn.Module): + """The PyTorch matrix factorization model. + + Parameters + ---------- + n_peptides : int + The number of peptides. + n_runs : int + The number of runs. + n_factors: int + The number of latent factors. + """ + def __init__(self, n_peptides: int, n_runs: int, n_factors: int) -> None: + """Initialize an ImputerModel.""" + super().__init__() + self.n_peptides = n_peptides + self.n_runs = n_runs + self.n_factors = n_factors + + torch.manual_seed(0) + + # The model: + self.peptide_factors = torch.randn(n_peptides, n_factors, requires_grad=True) + self.run_factors = torch.randn(n_runs, n_factors, requires_grad=True) + + def forward(self, X, non_zero_mask) -> torch.Tensor: + """Reconstruct the matrix and determine prediction error. + + Parameters + ---------- + X : torch.Tensor of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as -1. + non_zero_mask: torch.Tensor of shape (n_peptides, n_runs) + Tensor of boolean values where missing values are False. + + Returns + ------- + torch.Tensor of shape (1, 1) + Difference between the expected and predicted RTs. + """ + + predicted = torch.mm(self.peptide_factors, self.run_factors.T) + + diff = (X - predicted)**2 + prediction_error = torch.sum(diff*non_zero_mask) + + return prediction_error + + def get_params(self): + """Return the peptide and run factors.""" + return self.peptide_factors, self.run_factors + + + +class RTImputer: + """A retention time prediction model. + + This is a PyTorch model wrapped in a sklearn-like API. + + Parameters + ---------- + n_factors : int + The number of latent factors. + max_iter : int, optional + The maximum number of training iterations + tol : float, optional + The percent improvement over the previous loss required to + continue trianing. Used in conjuction with ``n_iter_no_change`` + to trigger early stopping. Set to ``None`` to disable. + n_iter_no_change : int, optional + The number of iterations to wait before triggering early + stopping. + device : str or torch.Device + A valid PyTorch device on which to perform the optimization. + + """ + + def __init__( + self, + n_factors: int, + max_iter: int = 1000, + tol: float = 1e-4, + n_iter_no_change: int = 20, + device: str | torch.device = "cpu", + ): + """Initialize the RTImputer""" + # Parameters: + self.n_factors = n_factors + self.max_iter = max_iter + self.tol = tol + self.n_iter_no_change = n_iter_no_change + self.device = device + + # Set during fit: + self._model = None + self._history = None + self._shape = None + self._std = None + self._means = None + + @property + def model_(self) -> MatrixModel: + """The underlying PyTorch model.""" + return self._model + + @property + def history_(self) -> pd.DataFrame: + """The training history.""" + return pd.DataFrame( + self._history, + columns=["iteration", "train_loss"], + ) + + def predict(self, only_missing: bool, non_zero_mask: torch.Tensor, X: torch.Tensor=None) -> torch.Tensor: + """Reconstruct the matrix. + + Parameters + ---------- + only_missing : bool + True if predicted model should only predict missing values. + False if predicted model should return predicted values for all peptides/runs. + non_zero_mask: torch.Tensor of shape (n_peptides, n_runs) + Tensor of boolean values where missing values are False. + X : torch.Tensor of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as -1. + + Returns + ------- + torch.Tensor of shape (n_peptides, n_runs) + The predicted retention time matrix. + """ + + peptide_factors, run_factors = self._model.get_params() + if only_missing: + pred = torch.mm(peptide_factors, run_factors.T).detach().numpy() + try: + X = X.copy() + except AttributeError: + res = X.clone().numpy() + res[~non_zero_mask] = pred[~non_zero_mask] + return res + + return torch.mm(self.peptide_factors, self.run_factors.T).detach().numpy() + + + def fit(self, X: np.ndarray, only_missing: bool=True) -> torch.Tensor: + """Fit the RTImputer. + + Parameters + ---------- + X : numpy.ndarray of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as -1. + only_missing : bool + True if predicted model should only predict missing values. + False if predicted model should return predicted values for all peptides/runs. + + Returns + ------- + torch.Tensor of shape (n_peptides, n_runs) + The predicted retention time matrix. + """ + + LOGGER.info("Training retention time predictor...") + + # Set seed: + torch.manual_seed(0) + + # Prepare the input and initialize model + X = to_tensor(X) + X = X.to(self.device, torch.float32) + X[torch.isnan(X)] = -1 + + self._shape = X.shape + self._model = MatrixModel(*X.shape, self.n_factors).to(self.device) + self._history = [] + + # Get actual values to compare model against + non_zero_mask = (X != -1) + + optimizer = torch.optim.RMSprop(self._model.get_params(), lr=.07) + log_interval = max(1, self.max_iter // 20) + LOGGER.info("Logging every %i iterations...", log_interval) + + # The main training loop: + best_loss = np.inf + early_stopping_counter = 0 + LOGGER.info("Iteration | Train Loss") + LOGGER.info("----------------------") + bar = trange(self.max_iter) + for epoch in bar: + optimizer.zero_grad() + loss = self._model(X, non_zero_mask) + loss.backward() + optimizer.step() + self._history.append((epoch, loss.item())) + + if not epoch % log_interval: + LOGGER.info("%9i | %10.4f", epoch, loss.item()) + + bar.set_postfix(loss=f'{loss:,.3f}') + if self.tol is not None: + if loss < best_loss: + best_loss = loss.item() + early_stopping_counter = 0 + continue + early_stopping_counter += 1 + if early_stopping_counter >= self.n_iter_no_change: + LOGGER.info("Stopping...") + break + + LOGGER.info("DONE!") + + return self.predict(only_missing, non_zero_mask, X) + + +def to_tensor(array: np.ndarray | torch.Tensor) -> torch.Tensor: + """Transform an array into a PyTorch Tensor, copying the data. + + Parameters + ---------- + array : numpy.ndarray or torch.Tensor + The array to transform + + Returns + ------- + torch.Tensor + The converted PyTorch tensor. + """ + if isinstance(array, torch.Tensor): + return array.to("cpu").clone().detach() + + return torch.tensor(X) \ No newline at end of file From 9502f4d058182d94eba2b026e512fc3fb527c929 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 18 Apr 2023 10:58:24 -0700 Subject: [PATCH 09/41] (wip,bugfix) changed neighborhood search and fixed splitting of TIMS spectra --- diadem/config.py | 2 +- diadem/data_io/timstof.py | 385 ++++++++++++++++++-------- diadem/deisotoping.py | 26 +- diadem/index/indexed_db.py | 29 +- diadem/search/diadem.py | 6 +- diadem/utilities/neighborhood.py | 223 ++++++++++++++- profiling/run_profile_multithread.zsh | 3 + profiling/run_profile_tims.py | 7 +- 8 files changed, 530 insertions(+), 151 deletions(-) diff --git a/diadem/config.py b/diadem/config.py index 38fcf8f..a79094f 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -178,7 +178,7 @@ class DiademConfig(DiademIndexConfig): # noqa # Min intensity to consider for matching and extracting run_min_intensity_ratio: float = 0.01 - run_min_correlation_score: float = 0.5 + run_min_correlation_score: float = 0.2 run_scaling_ratio = 0.001 run_scalin_limits: tuple[float, float] = (0.001, 0.999) diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 8d66d11..2e34bc3 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -9,6 +9,7 @@ from typing import Literal import numpy as np +import pandas as pd from alphatims.bruker import TimsTOF from joblib import Parallel, delayed from loguru import logger @@ -24,9 +25,9 @@ StackedChromatograms, ) from diadem.data_io.utils import slice_from_center, xic - -# from diadem.deisotoping import deisotope_with_ims +from diadem.deisotoping import deisotope_with_ims from diadem.search.metrics import get_ref_trace_corrs +from diadem.utilities.neighborhood import multidim_neighbor_search from diadem.utilities.utils import is_sorted IMSError = Literal["abs", "pct"] @@ -155,7 +156,7 @@ def from_group( u_center_mzs, u_center_intensities, inv = _bin_spectrum_intensities( center_mzs, center_intensities, - bin_width=0.02, + bin_width=0.001, bin_offset=0, ) assert is_sorted(u_center_mzs) @@ -227,6 +228,7 @@ def from_group( ref_id = np.argmax(stacked_arr[..., center_index]) bp_int = stacked_arr[ref_id, center_index] + # TODO: This might be a good place to plot the stacked chromatogram out = TimsStackedChromatograms( array=stacked_arr, @@ -463,6 +465,8 @@ def _precursor_iso_window_elements( bp_indices = np.array(bp_indices) # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) rts = np.array([x["rt_values_min"] for x in v]) + assert is_sorted(rts) + scan_indices = [str(x["scan_indices"]) for x in v] x = { @@ -574,10 +578,15 @@ def find_neighbors_mzsort( opts.setdefault(i1, []).append(i1) continue - candidates = np.where(np.abs(sorted_mz_values - mz1) <= mz_tol)[0] + candidates = np.arange( + np.searchsorted(sorted_mz_values, mz1 - mz_tol), + np.searchsorted(sorted_mz_values, mz1 + mz_tol, side="right"), + ) + tmp_ims = ims_vals[candidates] - match_indices = np.where(np.abs(tmp_ims - ims1) <= ims_tol)[0] + match_indices = np.abs(tmp_ims - ims1) <= ims_tol + match_indices = np.where(match_indices)[0] for i2 in candidates[match_indices]: opts.setdefault(i1, [i1]).append(i2) opts.setdefault(i2, [i2]).append(i1) @@ -596,8 +605,11 @@ def get_break_indices( Example: ------- >>> tmp = np.array([1,2,3,4,7,8,9,11,12,13]) - >>> get_break_indices(tmp) - (array([0, 4, 7, 9]), array([ 1, 7, 11, 13])) + >>> bi = get_break_indices(tmp) + >>> bi + (array([ 0, 4, 7, 10]), array([ 1, 7, 11, 13])) + >>> [tmp[si: ei] for si, ei in zip(bi[0][:-1], bi[0][1:])] + [array([1, 2, 3, 4]), array([7, 8, 9]), array([11, 12, 13])] """ if break_values is None: break_values = inds @@ -605,141 +617,213 @@ def get_break_indices( breaks = np.concatenate([np.array([0]), breaks, np.array([inds.size - 1])]) break_indices = inds[breaks] + breaks[-1] += 1 + return breaks, break_indices +# @profile def _get_precursor_index_windows( dia_data: TimsTOF, precursor_index: int, progress: bool = True, mz_range: None | tuple[float, float] = None, ) -> dict[dict[list]]: - PRELIM_N_PEAK_FILTER = 10_000 # noqa - MIN_INTENSITY_KEEP = 50 # noqa - MIN_NUM_SEEDS = 10_000 # noqa - IMS_TOL = 0.01 # noqa - # these tolerances are set narrow on purpose, since they - # only delimit the definition of the neighborhood, which will iteratively - # expanded. - MZ_TOL = 0.02 # noqa - MAX_ISOTOPE_CHARGE = 3 # noqa - inds = dia_data[{"precursor_indices": precursor_index}, "raw"] - breaks, break_inds = get_break_indices(inds=inds) - - push_data = dia_data.convert_from_indices( - break_inds[:-1], - raw_indices_sorted=True, - return_rt_values_min=True, - # I think this is a better value than the actual RT - # since accounts for accumulation time. - return_quad_mz_values=True, - return_scan_indices=True, - ) + break_indices, break_values = get_break_indices(inds=inds) + # This will generate chunks of contiguous indices + # Which are fast to query. + # They do not assure that there will be only one quad window in + # them pbar = tqdm( - enumerate(zip(breaks[:-1], breaks[1:])), - total=len(breaks), + zip(break_indices[:-1], break_indices[1:]), + total=len(break_indices), disable=not progress, desc=f"Collecting spectra for precursor={precursor_index}", + bar_format="{l_bar}{bar:10}{r_bar}{bar:-10b}", ) quad_splits = {} - for bi, (startind, endind) in pbar: - curr_push_data = {k: v[bi] for k, v in push_data.items()} - peak_data = dia_data.convert_from_indices( + for startind, endind in pbar: + contig_peak_data = dia_data.convert_from_indices( inds[startind:endind], raw_indices_sorted=True, return_mz_values=True, return_corrected_intensity_values=True, return_mobility_values=True, + return_rt_values_min=True, + # I think this is a better value than the actual RT + # since accounts for accumulation time. + return_quad_mz_values=True, + return_quad_indices=True, + # return_scan_indices=True, + ) + # Scan indices are the same for the same rt-ims-quad combination + # so not very useful in our case. + # Since we want groups of peaks that share rt-quad values, regardless + # of the IMS value. + + df_peak_data = pd.DataFrame(contig_peak_data) + grouping_vals = [ + "quad_low_mz_values", + "quad_high_mz_values", + "rt_values_min", + "quad_indices", + ] + peak_data_vals = ["mz_values", "corrected_intensity_values", "mobility_values"] + + g_inds, g_vals = get_break_indices( + df_peak_data["quad_indices"].array, min_diff=0 ) - start_len = len(peak_data["mz_values"]) - # keep_intens = peak_data["corrected_intensity_values"] > MIN_INTENSITY_KEEP - # peak_data = {k: v[keep_intens] for k, v in peak_data.items()} - - if len(peak_data["corrected_intensity_values"]) > PRELIM_N_PEAK_FILTER: - partition_indices = np.argpartition( - peak_data["corrected_intensity_values"], - -PRELIM_N_PEAK_FILTER, - )[-PRELIM_N_PEAK_FILTER:] - peak_data = {k: v[partition_indices] for k, v in peak_data.items()} + for si, ei in zip(g_inds[:-1], g_inds[1:]): + curr_peak_data = df_peak_data.iloc[si:ei] + assert all( + np.abs(np.max(curr_peak_data[x]) - np.min(curr_peak_data[x])) < 1e-3 + for x in grouping_vals + ) + current_chunk_data = {k: curr_peak_data[k].values[0] for k in grouping_vals} - sort_idx = np.argsort(peak_data["mz_values"]) - peak_data = {k: v[sort_idx] for k, v in peak_data.items()} - if len(peak_data["corrected_intensity_values"]) == 0: - continue + current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] - ims_values = peak_data["mobility_values"] - mz_values = peak_data["mz_values"] - intensity_values = peak_data["corrected_intensity_values"] + peak_data = dict( + zip(peak_data_vals, curr_peak_data[peak_data_vals].T.values) + ) - if mz_range is not None: - mz_filter = slice( - np.searchsorted(mz_values, mz_range[0]), - np.searchsorted(mz_values, mz_range[1], "right"), + # # TODO check if it would be more efficient to check for breaks in + # # the quad values and then iterate over the breaks, instead if making it + # # a pandas df and grouping. + # grouped = df_peak_data.groupby(grouping_vals) + # for i, curr_peak_data in grouped: + # current_chunk_data = { k:v for k,v in zip(grouping_vals, i) } + # current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] + + # curr_peak_data.reset_index(drop=True, inplace=True) + # peak_data = dict( + # zip(curr_peak_data.columns, curr_peak_data.T.values) + # ) + start_len = len(peak_data["mz_values"]) + + if not len(peak_data["mz_values"]): + starting_min_intensity = 0 + starting_max_intensity = 0 + else: + starting_min_intensity = np.min(peak_data["corrected_intensity_values"]) + starting_max_intensity = np.max(peak_data["corrected_intensity_values"]) + + mz, intensity, ims = _preprocess_ims( + ims_values=peak_data["mobility_values"], + mz_values=peak_data["mz_values"], + intensity_values=peak_data["corrected_intensity_values"], + mz_range=mz_range, ) - ims_values = ims_values[mz_filter] - mz_values = mz_values[mz_filter] - intensity_values = intensity_values[mz_filter] - f = collapse_ims( - ims_values=ims_values, - mz_values=mz_values, - intensity_values=intensity_values, - top_n=MIN_NUM_SEEDS, - ims_tol=IMS_TOL, - mz_tol=MZ_TOL, - ) - filter = f["intensity"] > MIN_INTENSITY_KEEP - f = {k: v[filter] for k, v in f.items()} - - d_out = {"mz": None, "intensity": None, "ims": None} - if len(f["mz"]) < 0: - new_order = np.argsort(f["mz"]) - f = {k: v[new_order] for k, v in f.items()} - - # d_out["mz"], d_out["intensity"], d_out["ims"] = deisotope_with_ims( - # ims_diff=IMS_TOL, - # ims_unit="abs", - # imss=f["ims"], - # inten=f["intensity"], - # mz=f["mz"], - # mz_unit="da", - # track_indices=False, - # mz_diff=MZ_TOL, - # max_charge=MAX_ISOTOPE_CHARGE, - # ) - d_out["mz"], d_out["intensity"], d_out["ims"] = ( - f["mz"], - f["intensity"], - f["ims"], - ) - pct_compression = round(len(d_out["mz"]) / start_len, ndigits=2) - final_len = len(f["intensity"]) - pbar.set_postfix( - { + d_out = {"mz": mz, "intensity": intensity, "ims": ims} + ## updating the progress bar + if not len(mz): + pct_compression = 0 + final_len = 0 + f_min = 0 + f_max = 0 + else: + pct_compression = round(100 * len(d_out["mz"]) / start_len) + final_len = len(d_out["mz"]) + f_min = int(np.min(d_out["intensity"])) + f_max = int(np.max(d_out["intensity"])) + current_chunk_data.update(d_out) + quad_name = ( + f"{current_chunk_data['quad_low_mz_values']:.6f}," + f" {current_chunk_data['quad_high_mz_values']:.6f}" + ) + quad_splits.setdefault(quad_name, []).append(current_chunk_data) + + postfix_dict = { "peaks": start_len, "f_peaks": final_len, "pct": pct_compression, - "min": np.min(peak_data["corrected_intensity_values"]), - "max": np.max(peak_data["corrected_intensity_values"]), - "f_min": int(np.min(d_out["intensity"])) if final_len else 0, - "f_max": int(np.max(d_out["intensity"])) if final_len else 0, - }, - refresh=False, - ) + "min": starting_min_intensity, + "max": starting_max_intensity, + "f_min": f_max, + "f_max": f_min, + } + postfix_dict = {k: f"{v:.1e}" for k, v in postfix_dict.items()} + pbar.set_postfix( + postfix_dict, + refresh=True, + ) - curr_push_data.update(d_out) - quad_name = ( - f"{curr_push_data['quad_low_mz_values']:.6f}," - f" {curr_push_data['quad_high_mz_values']:.6f}" - ) - quad_splits.setdefault(quad_name, []).append(curr_push_data) return quad_splits +# @profile +def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): + # TODO make all of these arguments + # or make this callable class. + PRELIM_N_PEAK_FILTER = 10_000 # noqa + MIN_INTENSITY_KEEP = 50 # noqa + MIN_NUM_SEEDS = 5_000 # noqa + IMS_TOL = 0.01 # noqa + # these tolerances are set narrow on purpose, since they + # only delimit the definition of the neighborhood, which will iteratively + # expanded. + MZ_TOL = 0.02 # noqa + MAX_ISOTOPE_CHARGE = 3 # noqa + if len(intensity_values) > PRELIM_N_PEAK_FILTER: + partition_indices = np.argpartition( + intensity_values, + -PRELIM_N_PEAK_FILTER, + )[-PRELIM_N_PEAK_FILTER:] + + ims_values = ims_values[partition_indices] + mz_values = mz_values[partition_indices] + intensity_values = intensity_values[partition_indices] + + sort_idx = np.argsort(mz_values) + + ims_values = ims_values[sort_idx] + mz_values = mz_values[sort_idx] + intensity_values = intensity_values[sort_idx] + + if mz_range is not None: + mz_filter = slice( + np.searchsorted(mz_values, mz_range[0]), + np.searchsorted(mz_values, mz_range[1], "right"), + ) + ims_values = ims_values[mz_filter] + mz_values = mz_values[mz_filter] + intensity_values = intensity_values[mz_filter] + + # This is the bottleneck of the whole issue... + f = collapse_ims( + ims_values=ims_values, + mz_values=mz_values, + intensity_values=intensity_values, + top_n=MIN_NUM_SEEDS, + ims_tol=IMS_TOL, + mz_tol=MZ_TOL, + ) + filter = f["intensity"] > MIN_INTENSITY_KEEP + f = {k: v[filter] for k, v in f.items()} + + if len(f["mz"]) < 0: + new_order = np.argsort(f["mz"]) + f = {k: v[new_order] for k, v in f.items()} + + mz, intensity, ims = deisotope_with_ims( + ims_diff=IMS_TOL, + ims_unit="abs", + imss=f["ims"], + inten=f["intensity"], + mz=f["mz"], + mz_unit="da", + track_indices=False, + mz_diff=MZ_TOL, + max_charge=MAX_ISOTOPE_CHARGE, + ) + return mz, intensity, ims + + # @profile def _collapse_seeds( seeds: dict[int, list[int]], @@ -752,7 +836,7 @@ def _collapse_seeds( out_seeds = {} MAX_ITER = 5 # noqa - for s in seed_order: + for s in seed_order.tolist(): sk = seed_keys[s] if sk in taken: continue @@ -761,9 +845,8 @@ def _collapse_seeds( curr_len = 1 for _ in range(MAX_ITER): - neigh_set = set(chain(*[seeds.pop(x, set()) for x in out_seeds[sk]])).union( - out_seeds[sk], - ) + neigh_set = set(chain(*[seeds.pop(x, set()) for x in out_seeds[sk]])) + neigh_set = neigh_set.union(out_seeds[sk]) untaken = neigh_set.difference(taken) taken.update(untaken) out_seeds[sk].update(untaken) @@ -801,16 +884,74 @@ def collapse_ims( MIN_NEIGHBORS_SEED = 3 # noqa assert is_sorted(mz_values) - neighborhoods = find_neighbors_mzsort( - ims_vals=ims_values, - sorted_mz_values=mz_values, - intensities=intensity_values, - top_n=top_n, - top_n_pct=top_n_pct, - ims_tol=ims_tol, - mz_tol=mz_tol, + # TODO refactor using the neighborhood module in utils + + ## New implementation start + top_n = int(max(len(intensity_values) * top_n_pct, top_n)) + if len(intensity_values) > top_n: + top_indices = np.argpartition(intensity_values, -top_n)[-top_n:] + else: + top_indices = None + + elems1 = { + "ims": ims_values, + "mz": mz_values, + } + if top_indices is not None: + top_ims = ims_values[top_indices] + top_mz = mz_values[top_indices] + elems2 = { + "ims": top_ims, + "mz": top_mz, + } + else: + elems2 = None + + tmp = multidim_neighbor_search( + elems1=elems1, + elems2=elems2, + dist_ranges={"ims": [-ims_tol, ims_tol], "mz": [-mz_tol, mz_tol]}, + dimension_order=["mz", "ims"], ) + if top_indices is not None: + tmp_neighborhoods = {top_indices[k]: v for k, v in tmp.right_neighbors.items()} + for k in tmp_neighborhoods: + tmp_neighborhoods[k].add(k) + else: + tmp_neighborhoods = tmp.right_neighbors + + ## New implementation end + + # TODO delete this section one the new implementation is stably tested + # neighborhoods = find_neighbors_mzsort( + # ims_vals=ims_values, + # sorted_mz_values=mz_values, + # intensities=intensity_values, + # top_n=top_n, + # top_n_pct=top_n_pct, + # ims_tol=ims_tol, + # mz_tol=mz_tol, + # ) + # for k, v in tmp_neighborhoods.items(): + # try: + # assert set(neighborhoods[k]) == set(v) + # except AssertionError: + # print( + # "new implementation key: ", k, + # "new implementation values: ", v, + # "old implementation neighbors: ", neighborhoods[k], + # "new implementation errors: ", mz_values[k] - mz_values[list(v)], + # "old implementation errors: ", mz_values[k] - mz_values[list(neighborhoods[k])], + # "old implementation mistakes: ", np.abs(mz_values[k] - mz_values[list(neighborhoods[k])]) < mz_tol, + # "new implementation errors: ", ims_values[k] - ims_values[list(v)], + # "old implementation errors: ", ims_values[k] - ims_values[list(neighborhoods[k])], + # "old implementation mistakes: ", np.abs(ims_values[k] - ims_values[list(neighborhoods[k])]) < ims_tol, + # sep="\n" + # ) + # breakpoint() + neighborhoods = tmp_neighborhoods + # unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} seeds = {k: v for k, v in ambiguous.items() if len(v) >= MIN_NEIGHBORS_SEED} @@ -832,11 +973,15 @@ def collapse_ims( for i, v in enumerate(out_seeds.values()): v = list(v) v_intensities = intensity_values[v] + highest_intensity = v_intensities.argmax() # This generates the weighted average of the ims and mz # as the new values for the peak. bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) - bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity - bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity + bundled["ims"][i] = ims_values[v[highest_intensity]] + bundled["mz"][i] = mz_values[v[highest_intensity]] + + # bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity + # bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity # # # TODO consider whether I really want to keep ambiduous matches # # # In theory I could keep only the expanded seeds. diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 6e41aed..71ced90 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -1,7 +1,7 @@ import numpy as np from numpy.typing import NDArray -from diadem.utilities.neighborhood import multidim_neighbor_search +from diadem.utilities.neighborhood import _multidim_neighbor_search NEUTRON = 1.00335 @@ -23,6 +23,7 @@ def ppm_to_delta_mass(obs: float, ppm: float) -> float: return ppm * obs / 1_000_000.0 +# @profile def _deisotope_with_ims_arrays( mzs, intensities, @@ -79,6 +80,17 @@ def _deisotope_with_ims_arrays( for i, peak in enumerate(peaks): peak["indices"] = [i] + dist_funs = {k: lambda x, y: y - x for k in dim_order} + + # sort all elements by their first dimension + spec_order = np.argsort(spec["mz"]) + spec = {k: v[spec_order] for k, v in spec.items()} + + spec_indices = np.arange(len(spec["mz"])) + + # It might be faster to just generate an expanded + # array with all the charge variants and then do a + # single search. for charge in range(max_charge + 1, 0, -1): dist_ranges = { "mz": ( @@ -88,12 +100,14 @@ def _deisotope_with_ims_arrays( "ims": (-ims_tolerance, ims_tolerance), } - out = multidim_neighbor_search( - spec, - spec, + out = _multidim_neighbor_search( + elems_1=spec, + elems_2=spec, + elems_1_indices=spec_indices, + elems_2_indices=spec_indices, + dist_funs=dist_funs, dist_ranges=dist_ranges, - order=dim_order, - force_vectorized=True, # This is up to optimization. + dimension_order=dim_order, ) isotope_graph = out.left_neighbors diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index e9d0945..8e83cfa 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -746,22 +746,24 @@ def score_arrays( ms1_range=ms1_range, ms2_range=(fragment_mz - ms2_tol, fragment_mz + ms2_tol), ) + candidates = list(candidates) + dms = np.array([x[1] for x in candidates]) + dms = np.abs(dms - fragment_mz) + candidates = [x for x, y in zip(candidates, dms <= ms2_tol) if y] - for seq, frag, series in candidates: + for (seq, frag, series), dm in zip(candidates, dms): # Should tolerances be checked here? # IN THEORY, they should have been filtered in the past. - dm = frag - fragment_mz - if abs(dm) <= ms2_tol: - peaks.append( - { - "seq": seq, - "ion": series, - "mz": fragment_mz, - "intensity": fragment_intensity, - "error": dm, - "peak_id": i, - }, - ) + peaks.append( + { + "seq": seq, + "ion": series, + "mz": fragment_mz, + "intensity": fragment_intensity, + "error": dm, + "peak_id": i, + }, + ) peptide_ids = np.array([x["seq"] for x in peaks]) ids, counts = np.unique(peptide_ids, return_counts=True) @@ -899,6 +901,7 @@ def index_prefiltered_from_parquet( .sort(pl.col("mz")) .collect() ) + logger.debug(f"Loaded {len(joint_frags)} fragments from parquet cache.") out = copy.copy(self) out.bucketlist = PrefilteredMS1BucketList( diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 2d0686d..bebf12f 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -273,6 +273,8 @@ def search_group( }, ) pbar.update(1) + + # TODO move this so it is disabled without the debug flag ... if ((et := time.time()) - st) >= 2: from ms2ml.utils.mz_utils import get_tolerance @@ -294,7 +296,9 @@ def search_group( logger.error( ( f"Iteration took waaaay too long scores={scores} ;" - f" {tot_candidates} total candidates" + f" {tot_candidates} total candidates for precursor range " + f"{group.precursor_range} and m/z range " + f"and ms2 range {(mz - ms2_tol, mz + ms2_tol)}" ), ) logger.error(f"{new_stack.mzs.copy()}; len({len(new_stack.mzs)})") diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index df3a177..898430c 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -11,6 +11,8 @@ import numpy as np from numpy.typing import NDArray +from diadem.utilities.utils import is_sorted + @dataclass class IndexBipartite: @@ -249,6 +251,7 @@ def find_neighbors( return out +# @profile def multidim_neighbor_search( elems1: dict[str, NDArray], elems2: dict[str, NDArray], @@ -314,8 +317,8 @@ def multidim_neighbor_search( dist_fun = _default_dist_fun if dist_funs is None else dist_funs[curr] # Filter to keep only the allowable matches - x = x[xi] - y = y[yi] + # x = x[xi] + # y = y[yi] # Sort the x and y arrays, keeping track of the original order # For example: @@ -325,14 +328,33 @@ def multidim_neighbor_search( # and therefore to get the original index of element 1 in the sorted # array (6) one would use ... # order_array[i] (0 in this case) - x_order = np.argsort(x) - y_order = np.argsort(y) + x_order = np.argsort(x, kind="stable") + y_order = np.argsort(y, kind="stable") + + # This allowed neighbors have indices that correspond to the original + # array, not the sorted array, therefore we need to use the order array + # to get the correct indices. + if last_graph is None: + allowed_neighbors = None + else: + allowed_neighbors = last_graph.left_neighbors + # x_order[k] being k 1 is the index of the lowest element, + # not the position of element k in the sorted array + + # So we need an inverted order array + inv_x_order = np.argsort(x_order, kind="stable") + inv_y_order = np.argsort(y_order, kind="stable") + allowed_neighbors = { + inv_x_order[k]: inv_y_order[v] for k, v in allowed_neighbors.items() + } + neighbors = find_neighbors_sorted( x[x_order], y[y_order], dist_fun=dist_fun, low_dist=dist_ranges[curr][0], high_dist=dist_ranges[curr][1], + allowed_neighbors=allowed_neighbors, ) t_xi, t_yi = neighbors.get_matching_indices() if len(t_xi) == 0 or len(t_yi) == 0: @@ -341,8 +363,8 @@ def multidim_neighbor_search( o_xi = xi[x_order[t_xi]] o_yi = yi[y_order[t_yi]] - xi = xi[x_order[np.unique(t_xi)]] - yi = yi[y_order[np.unique(t_yi)]] + # xi = xi[x_order[np.unique(t_xi)]] + # yi = yi[y_order[np.unique(t_yi)]] orig_index_graph = IndexBipartite.from_arrays(o_xi, o_yi) @@ -380,12 +402,14 @@ def multidim_neighbor_search( return last_graph +# @profile def find_neighbors_sorted( x: NDArray, y: NDArray, dist_fun: callable, low_dist: float, high_dist: float, + allowed_neighbors: dict[int, set[int]] | None = None, ) -> IndexBipartite: """Finds neighbors between to sorted arrays. @@ -418,17 +442,43 @@ def find_neighbors_sorted( >>> find_neighbors_sorted(x,y,dist_fun,low_dist, high_dist) IndexBipartite(left_neighbors={0: [0], 2: [2], 3: [3], 6: [4, 5]}, right_neighbors={0: [0], 2: [2], 3: [3], 4: [6], 5: [6]}) + >>> find_neighbors_sorted(x,y,dist_fun,low_dist, high_dist, allowed_neighbors={6: {5, 4}}) + IndexBipartite(left_neighbors={6: [4, 5]}, right_neighbors={4: [6], 5: [6]}) """ + assert is_sorted(x) + assert is_sorted(y) assert low_dist < high_dist + assert dist_fun(low_dist, high_dist) > 0 + assert dist_fun(high_dist, low_dist) < 0 + neighbors = IndexBipartite() ii = 0 - for i in range(len(x)): + + if allowed_neighbors is None: + iter_x = range(len(x)) + else: + iter_x = sorted(list(allowed_neighbors.keys())) + + for i in iter_x: x_val = x[i] + # if (abs(x_val - 0.8112) < 1e-4): + # breakpoint() last_diff = None - for j in range(ii, len(y)): + + if allowed_neighbors is None: + iter_y = range(ii, len(y)) + else: + iter_y = sorted(list(allowed_neighbors[i])) + + for j in iter_y: y_val = y[j] + # if (abs(x_val - 401.7911) < 1e-4) & (abs(y_val - 401.8036) < 1e-4): + # breakpoint() + + # if (abs(x_val - 0.8112) < 1e-4) & (abs(y_val - 0.8085) < 1e-4): + # breakpoint() diff = dist_fun(x_val, y_val) # TODO disable this for performance ... @@ -609,3 +659,160 @@ def _apply_vectorized(x: NDArray, y: NDArray, fun: callable) -> NDArray: """ outs = fun(np.tile(np.expand_dims(x, axis=-1), len(y)), y) return outs + + +# @profile +def multidim_neighbor_search( + elems1: dict[str, NDArray], + elems2: dict[str, NDArray] | None, + dist_ranges: dict[str, tuple[float, float]], + dist_funs: None | dict[str, callable] = None, + dimension_order: None | tuple[str] = None, +) -> IndexBipartite: + """Searches for neighbors in multiple dimensions. + + Arguments: + --------- + elems1 and elems2, dict[str,NDArray]: + A dictionary of arrays. + All arrays within one of those elements need to have the same + length. + dist_ranges, dict[str, tuple[float, float]]: + maximum and minimum ranges for each of the dimensions. + dist_funs: + Dictionary of functions used to calculate distances. + For details check the documentation of `find_neighbors_sorted` + dimension_order, optional str: + Optional tuple of strings denoting what dimensions to use. + + Examples: + -------- + >>> x1 = {"d1": np.array([1000., 1000., 2001., 3000.]), + ... "d2": np.array([1000., 1000.3, 2000., 3000.01])} + >>> x2 = {"d1": np.array([1000.01, 1000.01, 2000., 3000.]), + ... "d2": np.array([1000.01, 1000.01, 2000., 3001.01])} + >>> d_funs = {"d1": lambda x,y: 1e6 * (y-x)/abs(x), "d2": lambda x,y: y-x} + >>> d_ranges = {"d1": (-10, 10), "d2": (-0.02, 0.02)} + >>> multidim_neighbor_search( + ... x1, x2, d_ranges, d_funs + ... ) + IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) + """ + if dimension_order is None: + dimension_order = list(elems1.keys()) + + elems_1_indices = np.arange(len(elems1[dimension_order[0]])) + + if dist_funs is None: + dist_funs = {k: lambda x, y: y - x for k in dimension_order} + + # sort all elements by their first dimension + elems_1_order = np.argsort(elems1[dimension_order[0]]) + + elems_1 = {k: v[elems_1_order] for k, v in elems1.items()} + + # The original indices are also sorted by the same dimension + elems_1_indices = elems_1_indices[elems_1_order] + + if elems2 is not None: + elems_2_indices = np.arange(len(elems2[dimension_order[0]])) + elems_2_order = np.argsort(elems2[dimension_order[0]]) + elems_2 = {k: v[elems_2_order] for k, v in elems2.items()} + elems_2_indices = elems_2_indices[elems_2_order] + else: + elems_2 = elems_1 + elems_2_indices = elems_1_indices + + # Set up the graph where the neighbors will be stored + out = _multidim_neighbor_search( + elems_1=elems_1, + elems_2=elems_2, + elems_1_indices=elems_1_indices, + elems_2_indices=elems_2_indices, + dist_ranges=dist_ranges, + dist_funs=dist_funs, + dimension_order=dimension_order, + ) + return out + + +# @profile +def _multidim_neighbor_search( + elems_1: dict[str, NDArray], + elems_2: dict[str, NDArray], + elems_1_indices: NDArray, + elems_2_indices: NDArray, + dist_ranges: dict[str, tuple[float, float]], + dist_funs: dict[str, callable], + dimension_order: tuple[str], +): + neighbors = IndexBipartite() + # ii = 0 + + for i in range(len(elems_1[dimension_order[0]])): + # Allowable indices is a list that maps the indices that + # are still viable to use, mapping to the original indices + allowable_indices = None + for dimension in dimension_order: + curr_dimension_matches = [] + dist_fun = dist_funs[dimension] + low_dist, high_dist = dist_ranges[dimension] + x = elems_1[dimension] + y = elems_2[dimension] + + x_val = x[i] + + assert low_dist < high_dist + assert dist_fun(low_dist, high_dist) > 0 + assert dist_fun(high_dist, low_dist) < 0 + + # we generate an iterable that yields the indices of + # the original array in increasing order of the current + # dimension + if allowable_indices is not None: + if len(allowable_indices) == 0: + break + # we can only search in the allowable indices + # from the previous dimension + allowed_y = y[allowable_indices] + + match_indices = allowed_y >= x_val + low_dist + match_indices = match_indices & (allowed_y <= x_val + high_dist) + curr_dimension_matches = allowable_indices[match_indices] + else: + curr_dimension_matches = np.arange( + np.searchsorted(y, x_val + low_dist), + np.searchsorted(y, x_val + high_dist), + ) + + # Speed test + match_indices = y >= x_val + low_dist + + # Surprisingly, this is slower ... + # ii = np.searchsorted(y[ii:], x_val + low_dist) + ii + # oi = np.searchsorted(y[ii:], x_val + high_dist) + ii + # curr_dimension_matches_o = np.arange(ii, oi) + # assert np.all(curr_dimension_matches == curr_dimension_matches_o) + + # if allowable_indices is not None: + # assert all(x in allowable_indices for x in curr_dimension_matches) + allowable_indices = curr_dimension_matches + + # After going though all dimensions, we have the allowable indices + # for the current element in the first array + for j in curr_dimension_matches: + neighbors.add_connection(i, j) + + # Now we have to map the indices back to the original indices + # TODO check if vectorizing this is faster + neighbors = IndexBipartite( + left_neighbors={ + elems_1_indices[k]: {elems_2_indices[w] for w in v} + for k, v in neighbors.left_neighbors.items() + }, + right_neighbors={ + elems_2_indices[k]: {elems_1_indices[w] for w in v} + for k, v in neighbors.right_neighbors.items() + }, + ) + return neighbors diff --git a/profiling/run_profile_multithread.zsh b/profiling/run_profile_multithread.zsh index a939e69..60b6a58 100644 --- a/profiling/run_profile_multithread.zsh +++ b/profiling/run_profile_multithread.zsh @@ -18,3 +18,6 @@ R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithrea # Tuning some parameters, changing threads to 4 # [INFO] - Found 16790 peptides with q<=0.01 # 2023-02-21 17:46:55.441 | INFO | diadem.search.diadem:diadem_main:312 - Elapsed time: 6328.449725866318 + +# [INFO] - Found 27119 peptides with q<=0.01 +# 2023-04-13 13:33:17.309 | INFO | diadem.search.diadem:diadem_main:425 - Elapsed time: 8089.781619787216 diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 1354d90..43a043e 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -10,10 +10,13 @@ run_parallelism=1, # Needs to use 1 core for profiling run_max_peaks=20000, run_allowed_fails=5000, - g_tolerances=(0.03, 0.03), + g_tolerances=(0.02, 0.02), g_tolerance_units=("da", "da"), - g_ims_tolerance=0.04, + g_ims_tolerance=0.02, g_ims_tolerance_unit="abs", + run_min_correlation_score=0.5, + run_min_intensity_ratio=0.01, + peptide_mz_range=(400, 2000), ), out_prefix="lineprofile_results_tims/results", ) From 978e618c4a32a2712388427ab0506a676830e8bc Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 19 Apr 2023 19:52:33 -0700 Subject: [PATCH 10/41] (bugfix) fixed bug on deisotoping indexing --- diadem/deisotoping.py | 12 ++++++------ diadem/utilities/neighborhood.py | 1 + profiling/lineprofile_timstof.zsh | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 71ced90..d0520ca 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -43,6 +43,7 @@ def _deisotope_with_ims_arrays( If imss is None, will assume there is no IMS dimension in the data. """ + if imss is None: peak_iter = zip(mzs, intensities) peaks = [ @@ -75,18 +76,17 @@ def _deisotope_with_ims_arrays( extract_values = ["mz", "intensity", "ims"] spec = {"mz": mzs, "intensity": intensities, "ims": imss} - if track_indices: - extract_values.append("indices") - for i, peak in enumerate(peaks): - peak["indices"] = [i] - dist_funs = {k: lambda x, y: y - x for k in dim_order} - # sort all elements by their first dimension spec_order = np.argsort(spec["mz"]) + peaks = [peaks[i] for i in spec_order] spec = {k: v[spec_order] for k, v in spec.items()} spec_indices = np.arange(len(spec["mz"])) + if track_indices: + extract_values.append("indices") + for i, peak in enumerate(peaks): + peak["indices"] = [i] # It might be faster to just generate an expanded # array with all the charge variants and then do a diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index 898430c..56e861e 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -748,6 +748,7 @@ def _multidim_neighbor_search( ): neighbors = IndexBipartite() # ii = 0 + assert is_sorted(elems_1[dimension_order[0]]) for i in range(len(elems_1[dimension_order[0]])): # Allowable indices is a list that maps the indices that diff --git a/profiling/lineprofile_timstof.zsh b/profiling/lineprofile_timstof.zsh index 463df20..239e85a 100644 --- a/profiling/lineprofile_timstof.zsh +++ b/profiling/lineprofile_timstof.zsh @@ -23,4 +23,4 @@ R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/resul R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_tims/iter_score_plot.png", plot = g)' mokapot lineprofile_results_tims/results.diadem.tsv.pin --test_fdr 0.05 --keep_decoys -d lineprofile_results_tims -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/mokapot.peptides.txt") ; foo2 = readr::read_tsv("mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/td_plot.png", plot = g)' +R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/mokapot.peptides.txt") ; foo2 = readr::read_tsv("lineprofile_results_tims/mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/td_plot.png", plot = g)' From bb7a4ce103c1715cf2af1fda3e68c82ba34a8c34 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 25 Apr 2023 12:43:10 -0700 Subject: [PATCH 11/41] (wip) progress todards merge --- .pre-commit-config.yaml | 4 +- benchmarking_tests/conftest.py | 2 +- benchmarking_tests/test_deisotoping.py | 524 +++++++++++++++++++++++ benchmarking_tests/test_scoring_speed.py | 12 +- diadem/cli.py | 14 +- diadem/data_io/__init__.py | 3 +- diadem/data_io/mzml.py | 22 + diadem/data_io/timstof.py | 116 ++++- diadem/deisotoping.py | 1 - diadem/index/protein_index.py | 4 +- diadem/search/dda.py | 10 +- diadem/search/metrics.py | 3 +- diadem/search/search_utils.py | 5 +- diadem/utilities/neighborhood.py | 155 +------ profiling/.gitignore | 1 + profiling/run_profile_tims.py | 1 + tests/test_database_scoring.py | 6 +- tests/test_dia_search.py | 3 +- 18 files changed, 701 insertions(+), 185 deletions(-) create mode 100644 benchmarking_tests/test_deisotoping.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9fd1550..e621c1e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: - id: trailing-whitespace - id: detect-private-key - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black language_version: python3.9 @@ -18,7 +18,7 @@ repos: - id: isort name: isort (python) - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.256 + rev: v0.0.263 hooks: - id: ruff args: ['--fix'] diff --git a/benchmarking_tests/conftest.py b/benchmarking_tests/conftest.py index 955c08c..c59510c 100644 --- a/benchmarking_tests/conftest.py +++ b/benchmarking_tests/conftest.py @@ -33,7 +33,7 @@ def make_protein(prot_length: int) -> str: """Makes the sequence of a fake protein of the passed length.""" return "".join( - sample(list(aa_counts.keys()), counts=list(aa_counts.values()), k=prot_length) + sample(list(aa_counts.keys()), counts=list(aa_counts.values()), k=prot_length), ) diff --git a/benchmarking_tests/test_deisotoping.py b/benchmarking_tests/test_deisotoping.py new file mode 100644 index 0000000..bbb9a33 --- /dev/null +++ b/benchmarking_tests/test_deisotoping.py @@ -0,0 +1,524 @@ +import numpy as np +from numpy import array + +# These are just some hard-coded distributions +average_distributions = { + 0: array([1.000]), + 200: array([1.000, 0.108, 0.011, 0.001, 0.000, 0.000]), + 400: array([1.000, 0.219, 0.033, 0.004, 0.000, 0.000, 0.000]), + 600: array([1.000, 0.326, 0.068, 0.011, 0.001, 0.000, 0.000]), + 800: array([1.000, 0.436, 0.116, 0.023, 0.004, 0.000, 0.000, 0.000]), + 1000: array([1.000, 0.536, 0.167, 0.038, 0.007, 0.001, 0.000, 0.000, 0.000]), + 1200: array([1.000, 0.645, 0.238, 0.064, 0.014, 0.003, 0.000, 0.000, 0.000]), + 1400: array( + [ + 1.000, + 0.757, + 0.367, + 0.133, + 0.039, + 0.009, + 0.002, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 1600: array( + [ + 1.000, + 0.868, + 0.461, + 0.181, + 0.057, + 0.015, + 0.003, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 1800: array( + [ + 1.000, + 0.976, + 0.565, + 0.242, + 0.083, + 0.024, + 0.006, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 2000: array( + [ + 0.923, + 1.000, + 0.629, + 0.290, + 0.107, + 0.033, + 0.009, + 0.002, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 2200: array( + [ + 0.837, + 1.000, + 0.680, + 0.336, + 0.133, + 0.044, + 0.013, + 0.003, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 2400: array( + [ + 0.768, + 1.000, + 0.731, + 0.386, + 0.163, + 0.058, + 0.018, + 0.005, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 2600: array( + [ + 0.708, + 1.000, + 0.784, + 0.442, + 0.198, + 0.074, + 0.024, + 0.007, + 0.002, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 2800: array( + [ + 0.662, + 1.000, + 0.831, + 0.494, + 0.233, + 0.092, + 0.031, + 0.009, + 0.003, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 3000: array( + [ + 0.617, + 1.000, + 0.884, + 0.557, + 0.277, + 0.115, + 0.041, + 0.013, + 0.004, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 3200: array( + [ + 0.578, + 1.000, + 0.936, + 0.622, + 0.327, + 0.143, + 0.054, + 0.018, + 0.005, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 3400: array( + [ + 0.543, + 1.000, + 0.990, + 0.692, + 0.381, + 0.175, + 0.069, + 0.024, + 0.008, + 0.002, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 3600: array( + [ + 0.493, + 0.959, + 1.000, + 0.735, + 0.424, + 0.203, + 0.084, + 0.031, + 0.010, + 0.003, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), + 3800: array( + [ + 0.444, + 0.913, + 1.000, + 0.770, + 0.464, + 0.233, + 0.101, + 0.038, + 0.013, + 0.004, + 0.001, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + 0.000, + ] + ), +} + +max_len = max([len(x) for x in average_distributions.values()]) +average_distributions_array = np.array( + [np.pad(x, (0, max_len - len(x))) for x in average_distributions.values()] +) + + +def mass_to_dist(mass): + """Convert a mass to a distribution. + + It is a hard coded-ugly way of doing it that approximates + to the closest 200 Da. + Will only work for masses lower than 4000. + """ + return average_distributions_array[(mass // 200)] + + +def make_isotope_envelope_vectorized( + mz, charge, intensity, min_intensity, ims: np.ndarray | None = None +): + """Make an isotopic envelope for a given mz, charge, and intensity. + + Returns a tuple of (mz, intensity) arrays. + + Examples + -------- + >>> mzs = np.array([300., 300.]) + >>> ints = np.array([1000., 1000.]) + >>> charges = np.array([1, 2]) + >>> out = make_isotope_envelope_vectorized(mzs, charges, ints, 100) + >>> out + (array([300. , 301.003 , 300. , 300.5015]), array([1000., 108., 1000., 326.])) + >>> out = make_isotope_envelope_vectorized(mzs, charges, ints, 100, ims=np.array([1.0, 2.0])) + >>> out + (array([300. , 301.003 , 300. , 300.5015]), array([1000., 108., 1000., 326.]), array([1., 1., 2., 2.])) + """ + dist = average_distributions_array[((mz * charge) // 200).astype(int)] + dist = np.einsum("ij, i -> ij", dist, intensity) + + mz_offset = np.expand_dims(np.arange(dist.shape[1]), axis=-1) * 1.003 / charge + # shape (num_isotopes, 1) + mz_dist = (mz_offset + np.expand_dims(mz, axis=0)).T + + intensities = dist.flatten() + mz_dists = mz_dist.flatten() + + mask = intensities > min_intensity + + mz_dist = mz_dists[mask] + intensities = intensities[mask] + if ims is not None: + ims = np.repeat(ims, dist.shape[1]) + ims = ims[mask] + return mz_dist, intensities, ims + + return mz_dist, intensities + + +def simulate_isotopes( + min_mz, max_mz, num_peaks, min_charge, max_charge, min_intensity, max_intensity +): + """Simulate a set of isotopic peaks.""" + mzs = np.random.uniform(min_mz, max_mz, num_peaks) + ints = np.random.uniform(min_intensity, max_intensity, size=num_peaks) + charges = np.random.randint(min_charge, max_charge, size=num_peaks) + + mzs, intensities = make_isotope_envelope_vectorized( + mzs, charges, ints, min_intensity + ) + + return mzs, intensities + + +def simulate_isotopes_ims( + min_mz, + max_mz, + num_peaks, + min_charge, + max_charge, + min_intensity, + max_intensity, + min_ims, + max_ims, +): + """Simulate a set of isotopic peaks.""" + mzs = np.random.uniform(min_mz, max_mz, num_peaks) + ints = np.random.uniform(min_intensity, max_intensity, size=num_peaks) + charges = np.random.randint(min_charge, max_charge, size=num_peaks) + + mzs, intensities = make_isotope_envelope_vectorized( + mzs, charges, ints, min_intensity + ) + + return mzs, intensities + + +def _split_ims(ims: float, intensity: float, ims_std: float, ims_binwidth=0.002): + """Split an ims peak into multiple peaks. + + The number of peaks is the integer square root of the intensity. + + The new ims is drawn from a normal distribution with a mean of the + provided ims, with the provided standard deviation. + + The intensity of each peak is drawn from a uniform distribution between 0 + and 2*(intensity)/len(num of peaks). + """ + ims_out = np.random.normal(ims, ims_std, size=int(np.sqrt(intensity)) + 1) + ims_intensity = np.random.uniform( + 0, 2 * (intensity) / len(ims_out), size=len(ims_out) + ) + ims_intensity_out = np.histogram( + ims_out, + bins=np.arange(ims.min(), ims.max(), ims_binwidth), + weights=ims_intensity, + ) + return ims_out, ims_intensity_out + + +def extend_ims(seed_mzs, intensities, ims_start=0.7, ims_end=1.2, std_ims=0.01): + """Extend a set of peaks to include IMS coordinates. + + A random ims is assigned to every peak and a distribution of peaks is generated + splitting the intensity of the peak between the new peaks. (the new distribution + is random so the total might not be the same). + + The IMS coordinates are drawn from a normal distribution with a mean of the + average IMS value and a standard deviation of the standard deviation of the + IMS values. + + Returns + ------- + mzs : np.ndarray + The m/z values of the peaks. + intensities : np.ndarray + The intensities of the peaks. + ims : np.ndarray + The IMS values of the peaks. + indices : np.ndarray + This indices correspond to the originally passed seed mzs and intensities. + """ + num_ims = len(seed_mzs) + ims = np.random.uniform(ims_start, ims_end, size=num_ims) + ims_inten_pairs = [ + _split_ims(im, inten, ims_std=std_ims, ims_binwidth=0.002) + for im, inten in zip(ims, intensities) + ] + + out_imss = [] + out_intens = [] + out_mzs = [] + out_indices = [] + + for i, (imss, intens, seed_mz) in enumerate(zip(*ims_inten_pairs, seed_mzs)): + out_imss.extend(imss) + out_intens.extend(intens) + out_mzs.extend([seed_mz] * len(imss)) + out_indices.extend([i] * len(imss)) + + return ( + np.array(out_mzs), + np.array(out_intens), + np.array(out_imss), + np.array(out_indices), + ) + + +def simulate_ims_isotopes( + min_mz, + max_mz, + num_peaks, + min_charge, + max_charge, + min_intensity, + max_intensity, + min_ims, + max_ims, +): + pass + + +def add_noise(values, snr): + """Add noise to a set of peaks. + + The noise is added by adding a random number to each peak. The random number is + drawn from a normal distribution with a standard deviation equal to the intensity + of the peak divided by the SNR. + """ + noise = np.random.normal(0, values / snr) + return values + noise + + +def jitter_values(values, std): + """Jitter a set of values. + + The values are jittered by adding a random number to each value. The random number is + drawn from a normal distribution with a standard deviation equal to the standard deviation + of the values. + """ + noise = np.random.normal(0, std, size=len(values)) + return values + noise + + +def get_noise_peaks(mz_min, mz_max, ints, quantile, pct): + """Generates noise peaks from the distribution of intensities. + + Adds uniformly distributed peaks in the given mz range that do not belong to + an isotope envelope. Using the lowest quantile of the intensity distribution, + a threshold is determined below which no peaks are added. + """ + noise_intensity = np.quantile(ints, quantile) + noise_mzs = np.random.uniform(mz_min, mz_max, size=int(len(ints) * pct)) + noise_ints = np.random.uniform(0, noise_intensity, size=len(noise_mzs)) + + return noise_mzs, noise_ints + + +# clean simple spectrum +def clean_simple_spectrum(): + mzs, ints = simulate_isotopes(1000, 2000, 100, 1, 5, 1000, 10000) + ints = add_noise(ints, 100) + return mzs, ints + + +# clean complicated spectrum +def clean_complicated_spectrum(): + mzs, ints = simulate_isotopes(1000, 2000, 100, 1, 5, 1000, 10000) + ints = add_noise(ints, 100) + mzs = jitter_values(mzs, 0.01) + mzs2, ints2 = get_noise_peaks(1000, 2000, ints, 0.1, 0.1) + mzs = np.concatenate([mzs, mzs2]) + ints = np.concatenate([ints, ints2]) + return mzs, ints + + +# noisy simple spectrum +def noisy_simple_spectrum(): + pass + + +# noisy complicated spectrum +def noisy_complicated_spectrum(): + npeaks = 5_000 + pct_noise = 0.2 + + mzs, ints = simulate_isotopes( + 1000, + 2000, + npeaks, + 1, + 5, + min_intensity=1_000, + max_intensity=100_000, + ) + + +if __name__ == "__main__": + out = simulate_isotopes(1000, 2000, 3, 1, 2, 1000, 10000) diff --git a/benchmarking_tests/test_scoring_speed.py b/benchmarking_tests/test_scoring_speed.py index 0a6383c..666f1c7 100644 --- a/benchmarking_tests/test_scoring_speed.py +++ b/benchmarking_tests/test_scoring_speed.py @@ -12,7 +12,9 @@ def fake_database(fake_5k_fasta, request): Right not it is parametrized so it uses multiple chunk sizes. """ db = db_from_fasta( - fake_5k_fasta, chunksize=request.param, config=DiademConfig(run_parallelism=1) + fake_5k_fasta, + chunksize=request.param, + config=DiademConfig(run_parallelism=1), ) return db @@ -42,7 +44,9 @@ def test_db_scoring_speed_unfiltered(fake_database, fake_spectra_tuples_100, ben def test_db_scoring_speed_filtered( - fake_prefiltered_database, fake_spectra_tuples_100, benchmark + fake_prefiltered_database, + fake_spectra_tuples_100, + benchmark, ): """Benchmarks how long it takes to search 100 spectra. @@ -55,7 +59,9 @@ def score_all_specs_closed(db: IndexedDb, specs): """Runs a closed search on all spectra passed.""" for mzs, ints, prec_mz in tqdm(specs): db.hyperscore( - precursor_mz=(prec_mz - 0.01, prec_mz + 0.01), spec_int=ints, spec_mz=mzs + precursor_mz=(prec_mz - 0.01, prec_mz + 0.01), + spec_int=ints, + spec_mz=mzs, ) diff --git a/diadem/cli.py b/diadem/cli.py index d2ce5a9..e1128d5 100644 --- a/diadem/cli.py +++ b/diadem/cli.py @@ -44,7 +44,9 @@ def main_cli() -> None: @click.option("--fasta", help="fasta file to use as an input") @click.option("--out_prefix", help="Prefix to add to all output files") @click.option( - "--mode", type=click.Choice(["dda", "dia"], case_sensitive=False), default="dia" + "--mode", + type=click.Choice(["dda", "dia"], case_sensitive=False), + default="dia", ) @click.option("--config", help="Path to the config toml configuration file to use.") def search(data_path, fasta, out_prefix, mode, config) -> None: @@ -56,11 +58,17 @@ def search(data_path, fasta, out_prefix, mode, config) -> None: config = DiademConfig() if mode == "dia": diadem_main( - fasta_path=fasta, data_path=data_path, config=config, out_prefix=out_prefix + fasta_path=fasta, + data_path=data_path, + config=config, + out_prefix=out_prefix, ) elif mode == "dda": dda_main( - mzml_path=data_path, fasta_path=fasta, config=config, out_prefix=out_prefix + mzml_path=data_path, + fasta_path=fasta, + config=config, + out_prefix=out_prefix, ) else: raise NotImplementedError diff --git a/diadem/data_io/__init__.py b/diadem/data_io/__init__.py index 0d4b1b2..746b74e 100644 --- a/diadem/data_io/__init__.py +++ b/diadem/data_io/__init__.py @@ -6,7 +6,8 @@ def read_raw_data( - filepath: PathLike, config: DiademConfig + filepath: PathLike, + config: DiademConfig, ) -> TimsSpectrumStacker | SpectrumStacker: """Generic function to read data for DIA. diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index bb9dc47..947c41c 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -6,6 +6,7 @@ from pathlib import Path import numpy as np +import pandas as pd from joblib import Parallel, delayed from loguru import logger from ms2ml import Spectrum @@ -70,6 +71,27 @@ def __post_init__(self) -> None: title=f"Base peak chromatogram for the Group in {self.iso_window_name}", ) + def as_dataframe(self): + """Returns a dataframe with the data in the group. + + The dataframe has the following columns: + - mzs: list of mzs for each spectrum + - intensities: list of intensities for each spectrum + - retention_times: retention times for each spectrum + - precursor_start: start of the precursor range + - precursor_end: end of the precursor range + """ + out = pd.DataFrame( + { + "mzs": self.mzs, + "intensities": self.intensities, + "rts": self.retention_times, + }, + ) + out["precursor_start"] = min(self.precursor_range) + out["precursor_end"] = max(self.precursor_range) + return out + def get_highest_window( self, window: int, diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 2e34bc3..2f303a4 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -287,6 +287,21 @@ def __post_init__(self) -> None: if len(self.imss) != len(self.mzs): raise ValueError("IMS values do not have the same lenth as the MZ values") + def as_dataframe(self): + """Returns a dataframe with the data in the group. + + The dataframe has the following columns: + - mzs: list of mzs for each spectrum + - intensities: list of intensities for each spectrum + - retention_times: retention times for each spectrum + - precursor_start: start of the precursor range + - precursor_end: end of the precursor range + - ims: list of ims values for each spectrum + """ + out = super().as_dataframe() + out["ims"] = self.imss + return out + def get_highest_window( self, window: int, @@ -509,6 +524,7 @@ def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGro for i in self.unique_precursor_indices: nested_results = self._precursor_iso_window_groups(i, progress=progress) for r in nested_results.values(): + breakpoint() yield r @@ -645,7 +661,7 @@ def _get_precursor_index_windows( bar_format="{l_bar}{bar:10}{r_bar}{bar:-10b}", ) quad_splits = {} - for startind, endind in pbar: + for i, (startind, endind) in enumerate(pbar): contig_peak_data = dia_data.convert_from_indices( inds[startind:endind], raw_indices_sorted=True, @@ -674,7 +690,8 @@ def _get_precursor_index_windows( peak_data_vals = ["mz_values", "corrected_intensity_values", "mobility_values"] g_inds, g_vals = get_break_indices( - df_peak_data["quad_indices"].array, min_diff=0 + df_peak_data["quad_indices"].array, + min_diff=0, ) for si, ei in zip(g_inds[:-1], g_inds[1:]): @@ -688,7 +705,7 @@ def _get_precursor_index_windows( current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] peak_data = dict( - zip(peak_data_vals, curr_peak_data[peak_data_vals].T.values) + zip(peak_data_vals, curr_peak_data[peak_data_vals].T.values), ) # # TODO check if it would be more efficient to check for breaks in @@ -756,12 +773,18 @@ def _get_precursor_index_windows( return quad_splits +import random + +from matplotlib import pyplot as plt + + # @profile def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): # TODO make all of these arguments # or make this callable class. - PRELIM_N_PEAK_FILTER = 10_000 # noqa - MIN_INTENSITY_KEEP = 50 # noqa + PRELIM_N_PEAK_FILTER = 5_000 # noqa + FINAL_N_PEAK_FILTER = 1000 # noqa + MIN_INTENSITY_KEEP = 500 # noqa MIN_NUM_SEEDS = 5_000 # noqa IMS_TOL = 0.01 # noqa # these tolerances are set narrow on purpose, since they @@ -785,6 +808,26 @@ def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): mz_values = mz_values[sort_idx] intensity_values = intensity_values[sort_idx] + # Find highest peak, plot 4 mz and 0.3 IMS around it + if "PLOTDIADEM" in os.environ: + mz_range_val = 3 + ims_range_val = 0.3 + top_intense = np.argmax(intensity_values) + top_mz = mz_values[top_intense] + top_ims = ims_values[top_intense] + plot_mz_range = (top_mz - mz_range_val, top_mz + mz_range_val) + plot_ims_range = (top_ims - ims_range_val, top_ims + ims_range_val) + + top_plot_filter = ( + (mz_values > plot_mz_range[0]) + & (mz_values < plot_mz_range[1]) + & (ims_values > plot_ims_range[0]) + & (ims_values < plot_ims_range[1]) + ) + o_plot_ims_values = ims_values[top_plot_filter] + o_plot_mz_values = mz_values[top_plot_filter] + o_plot_intensity_values = intensity_values[top_plot_filter] + if mz_range is not None: mz_filter = slice( np.searchsorted(mz_values, mz_range[0]), @@ -803,9 +846,6 @@ def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): ims_tol=IMS_TOL, mz_tol=MZ_TOL, ) - filter = f["intensity"] > MIN_INTENSITY_KEEP - f = {k: v[filter] for k, v in f.items()} - if len(f["mz"]) < 0: new_order = np.argsort(f["mz"]) f = {k: v[new_order] for k, v in f.items()} @@ -821,6 +861,56 @@ def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): mz_diff=MZ_TOL, max_charge=MAX_ISOTOPE_CHARGE, ) + intensity = np.array(intensity) + + if len(mz) < 0: + filter = intensity > MIN_INTENSITY_KEEP + mz = mz[filter] + intensity = intensity[filter] + ims = ims[filter] + + if "PLOTDIADEM" in os.environ: + top_plot_filter = ( + (mz > plot_mz_range[0]) + & (mz < plot_mz_range[1]) + & (ims > plot_ims_range[0]) + & (ims < plot_ims_range[1]) + ) + plot_ims_values = ims[top_plot_filter] + plot_mz_values = mz[top_plot_filter] + plot_intensity_values = intensity[top_plot_filter] + + plt.clf() + plt.scatter( + o_plot_mz_values, + o_plot_ims_values, + c="gray", + s=np.sqrt(o_plot_intensity_values), + ) + plt.scatter( + plot_mz_values, + plot_ims_values, + c=plot_intensity_values, + cmap="viridis", + s=np.sqrt(plot_intensity_values), + alpha=0.8, + ) + if np.sum(plot_intensity_values) > 1000: + if random.random() > 0.01: + plt.pause(0.1) + else: + plt.show() + + if len(intensity) > FINAL_N_PEAK_FILTER: + partition_indices = np.argpartition( + intensity, + -FINAL_N_PEAK_FILTER, + )[-FINAL_N_PEAK_FILTER:] + + ims = ims[partition_indices] + mz = mz[partition_indices] + intensity = intensity[partition_indices] + return mz, intensity, ims @@ -973,15 +1063,15 @@ def collapse_ims( for i, v in enumerate(out_seeds.values()): v = list(v) v_intensities = intensity_values[v] - highest_intensity = v_intensities.argmax() # This generates the weighted average of the ims and mz # as the new values for the peak. bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) - bundled["ims"][i] = ims_values[v[highest_intensity]] - bundled["mz"][i] = mz_values[v[highest_intensity]] + # highest_intensity = v_intensities.argmax() + # bundled["ims"][i] = ims_values[v[highest_intensity]] + # bundled["mz"][i] = mz_values[v[highest_intensity]] - # bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity - # bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity + bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity + bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity # # # TODO consider whether I really want to keep ambiduous matches # # # In theory I could keep only the expanded seeds. diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index d0520ca..74e8483 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -43,7 +43,6 @@ def _deisotope_with_ims_arrays( If imss is None, will assume there is no IMS dimension in the data. """ - if imss is None: peak_iter = zip(mzs, intensities) peaks = [ diff --git a/diadem/index/protein_index.py b/diadem/index/protein_index.py index cc31cd0..d1cc6f3 100644 --- a/diadem/index/protein_index.py +++ b/diadem/index/protein_index.py @@ -62,7 +62,9 @@ def search_ngram(self, entry: str) -> list[str]: @staticmethod def from_fasta( - fasta_file: PathLike | str, ngram_size: int = 4, progress: bool = True + fasta_file: PathLike | str, + ngram_size: int = 4, + progress: bool = True, ) -> ProteinNGram: """Builds a protein n-gram from a fasta file. diff --git a/diadem/search/dda.py b/diadem/search/dda.py index bf7e1b7..920f365 100644 --- a/diadem/search/dda.py +++ b/diadem/search/dda.py @@ -25,7 +25,10 @@ def score(db: IndexedDb, spec: Spectrum, mzml_stem: str) -> DataFrame | None: if spec is None: return None spec_results = db.hyperscore( - spec.precursor_mz, spec_mz=spec.mz, spec_int=spec.intensity, top_n=10 + spec.precursor_mz, + spec_mz=spec.mz, + spec_int=spec.intensity, + top_n=10, ) if spec_results is not None: spec_results["ScanID"] = f"{mzml_stem}::{spec.extras['id']}" @@ -75,7 +78,10 @@ def out_hook(spec: Spectrum) -> Spectrum | None: if spec is None: continue spec_results = db.hyperscore( - spec.precursor_mz, spec_mz=spec.mz, spec_int=spec.intensity, top_n=10 + spec.precursor_mz, + spec_mz=spec.mz, + spec_int=spec.intensity, + top_n=10, ) if spec_results is not None: spec_results["ScanID"] = f"{mzml_stem}::{spec.extras['id']}" diff --git a/diadem/search/metrics.py b/diadem/search/metrics.py index 549b81e..de8394e 100644 --- a/diadem/search/metrics.py +++ b/diadem/search/metrics.py @@ -81,7 +81,8 @@ def get_ref_trace_corrs(arr: NDArray[np.float32], ref_idx: int) -> NDArray[np.fl # ref_trace = np.stack([ref_trace, ref_trace[..., ::-1]]).min(axis=0) # ref_trace = np.stack([ref_trace, ref_trace[..., ::-1]]).min(axis=0) spec_angle_weights = spectral_angle( - normalized_arr.astype("float"), ref_trace.astype("float") + normalized_arr.astype("float"), + ref_trace.astype("float"), ) return spec_angle_weights diff --git a/diadem/search/search_utils.py b/diadem/search/search_utils.py index 6fdd459..feb4c8e 100644 --- a/diadem/search/search_utils.py +++ b/diadem/search/search_utils.py @@ -8,7 +8,10 @@ def make_pin( - results: DataFrame, fasta_path: PathLike, mzml_path: PathLike, pin_path: PathLike + results: DataFrame, + fasta_path: PathLike, + mzml_path: PathLike, + pin_path: PathLike, ) -> None: """Makes a '.pin' file from a dataframe of results. diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index 56e861e..45f22e2 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -251,157 +251,6 @@ def find_neighbors( return out -# @profile -def multidim_neighbor_search( - elems1: dict[str, NDArray], - elems2: dict[str, NDArray], - dist_ranges: dict[str, tuple[float, float]], - dist_funs: None | dict[str, callable] = None, - order: None | tuple[str] = None, - force_vectorized: bool = False, -) -> IndexBipartite: - """Searches for neighbors in multiple dimensions. - - Arguments: - --------- - elems1 and elems2, dict[str,NDArray]: - A dictionary of arrays. - All arrays within one of those elements need to have the same - length. - dist_ranges, dict[str, tuple[float, float]]: - maximum and minimum ranges for each of the dimensions. - dist_funs: - Dictionary of functions used to calculate distances. - For details check the documentation of `find_neighbors_sorted` - order, optional str: - Optional tuple of strings denoting what dimensions to use. - force_vectorized, bool - Whether to force the use of the vectorized version of the - matching functions. - This in theory will be slower, but bypasses iterating in cpython. - - Examples: - -------- - >>> x1 = {"d1": np.array([1000., 1000., 2001., 3000.]), - ... "d2": np.array([1000., 1000.3, 2000., 3000.01])} - >>> x2 = {"d1": np.array([1000.01, 1000.01, 2000., 3000.]), - ... "d2": np.array([1000.01, 1000.01, 2000., 3001.01])} - >>> d_funs = {"d1": lambda x,y: 1e6 * (y-x)/x, "d2": lambda x,y: y-x} - >>> d_ranges = {"d1": (-10, 10), "d2": (-0.02, 0.02)} - >>> multidim_neighbor_search( - ... x1, x2, d_ranges, d_funs - ... ) - IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) - >>> multidim_neighbor_search( - ... x1, x2, d_ranges, d_funs, force_vectorized=True - ... ) - IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) - """ - if order is None: - order = list(elems1) - - # This makes sure all elements are the same length - array_length_x = len(elems1[order[0]]) - array_length_y = len(elems1[order[0]]) - assert all(len(elems1[e]) == array_length_x for e in order) - assert all(len(elems2[e]) == array_length_y for e in order) - - if not force_vectorized: - xi = np.arange(array_length_x) - yi = np.arange(array_length_y) - last_graph = None - - for curr in order: - x = elems1[curr] - y = elems2[curr] - dist_fun = _default_dist_fun if dist_funs is None else dist_funs[curr] - - # Filter to keep only the allowable matches - # x = x[xi] - # y = y[yi] - - # Sort the x and y arrays, keeping track of the original order - # For example: - # if the array x is [6,7,2] - # the sorted array would [2,6,7] - # the order would be [2, 0, 1] - # and therefore to get the original index of element 1 in the sorted - # array (6) one would use ... - # order_array[i] (0 in this case) - x_order = np.argsort(x, kind="stable") - y_order = np.argsort(y, kind="stable") - - # This allowed neighbors have indices that correspond to the original - # array, not the sorted array, therefore we need to use the order array - # to get the correct indices. - if last_graph is None: - allowed_neighbors = None - else: - allowed_neighbors = last_graph.left_neighbors - # x_order[k] being k 1 is the index of the lowest element, - # not the position of element k in the sorted array - - # So we need an inverted order array - inv_x_order = np.argsort(x_order, kind="stable") - inv_y_order = np.argsort(y_order, kind="stable") - allowed_neighbors = { - inv_x_order[k]: inv_y_order[v] for k, v in allowed_neighbors.items() - } - - neighbors = find_neighbors_sorted( - x[x_order], - y[y_order], - dist_fun=dist_fun, - low_dist=dist_ranges[curr][0], - high_dist=dist_ranges[curr][1], - allowed_neighbors=allowed_neighbors, - ) - t_xi, t_yi = neighbors.get_matching_indices() - if len(t_xi) == 0 or len(t_yi) == 0: - # Handles the case where no neighbors are possible - return IndexBipartite() - o_xi = xi[x_order[t_xi]] - o_yi = yi[y_order[t_yi]] - - # xi = xi[x_order[np.unique(t_xi)]] - # yi = yi[y_order[np.unique(t_yi)]] - - orig_index_graph = IndexBipartite.from_arrays(o_xi, o_yi) - - # Get the indices of x and y in the raw array - if last_graph is None: - last_graph = orig_index_graph - else: - last_graph = last_graph.intersect(orig_index_graph) - - else: - # Since the vectorzed version greedily computes all distances, - # This version can be optimized by not subsetting on every iteration. - # And just calculating all distances and checking which are in range. - # And then combining the results. - # This is in theory faster because it avoids the overhead of the - # subsetting and tracking indices. - if dist_funs is None: - dist_funs = {o: _default_dist_fun for o in order} - dist_funs = [dist_funs[o] for o in order] - dist_ranges = [dist_ranges[o] for o in order] - low_ranges = [d[0] for d in dist_ranges] - high_ranges = [d[1] for d in dist_ranges] - xs = [elems1[o] for o in order] - ys = [elems2[o] for o in order] - - neighbors = find_neighbors_multi_vectorized( - xs, - ys, - dist_funs=dist_funs, - low_dists=low_ranges, - high_dists=high_ranges, - ) - last_graph = neighbors - - return last_graph - - # @profile def find_neighbors_sorted( x: NDArray, @@ -458,7 +307,7 @@ def find_neighbors_sorted( if allowed_neighbors is None: iter_x = range(len(x)) else: - iter_x = sorted(list(allowed_neighbors.keys())) + iter_x = sorted(allowed_neighbors.keys()) for i in iter_x: x_val = x[i] @@ -469,7 +318,7 @@ def find_neighbors_sorted( if allowed_neighbors is None: iter_y = range(ii, len(y)) else: - iter_y = sorted(list(allowed_neighbors[i])) + iter_y = sorted(allowed_neighbors[i]) for j in iter_y: y_val = y[j] diff --git a/profiling/.gitignore b/profiling/.gitignore index 397a8b5..746ead5 100644 --- a/profiling/.gitignore +++ b/profiling/.gitignore @@ -9,3 +9,4 @@ line_profile*.txt profile*.txt **mokapot*.txt +*.png diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 43a043e..1533848 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -17,6 +17,7 @@ run_min_correlation_score=0.5, run_min_intensity_ratio=0.01, peptide_mz_range=(400, 2000), + run_scalin_limits=(0, 0.8), ), out_prefix="lineprofile_results_tims/results", ) diff --git a/tests/test_database_scoring.py b/tests/test_database_scoring.py index b8a13cd..acc4c7c 100644 --- a/tests/test_database_scoring.py +++ b/tests/test_database_scoring.py @@ -15,7 +15,9 @@ def test_fasta_shows_in_db(shared_datadir): config = DiademConfig() ms2ml_config = config.ms2ml_config adapter = FastaAdapter( - shared_datadir / "BSA.fasta", config=ms2ml_config, only_unique=True + shared_datadir / "BSA.fasta", + config=ms2ml_config, + only_unique=True, ) sequences = list(adapter.parse()) @@ -31,5 +33,5 @@ def test_fasta_shows_in_db(shared_datadir): score_df = db.hyperscore(ms1_range, spec_mz=mzs, spec_int=intens, top_n=2) score_df = score_df[np.invert(score_df["decoy"])] assert s.to_proforma() in list( - score_df["Peptide"] + score_df["Peptide"], ), f"Peptide i={i} {s.to_proforma()} not in db" diff --git a/tests/test_dia_search.py b/tests/test_dia_search.py index 489f0e1..8acf705 100644 --- a/tests/test_dia_search.py +++ b/tests/test_dia_search.py @@ -26,7 +26,8 @@ def test_dia_search_works_mzml(tmpdir, shared_datadir, parallel): peptides = {Peptide.from_sequence(x).stripped_sequence for x in df.Peptide.unique()} theo_table = pd.read_csv( - shared_datadir / "mzml/FGFR1_600_800_5min_peptide_table.tsv", sep="\t" + shared_datadir / "mzml/FGFR1_600_800_5min_peptide_table.tsv", + sep="\t", ) theo_table = theo_table[ theo_table["MS2 chromatographic points group_0_sample_0"] > 0 From b7037341559408a29bba1f7760674fa72fc4fd5e Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 25 Apr 2023 16:47:38 -0700 Subject: [PATCH 12/41] (wip) ruff compliance --- benchmarking_tests/test_deisotoping.py | 67 +++++++++----- benchmarking_tests/test_scoring_speed.py | 2 +- diadem/data_io/__init__.py | 2 + diadem/data_io/mzml.py | 63 ++++++++++--- diadem/data_io/timstof.py | 111 +++++++++-------------- diadem/deisotoping.py | 24 ++--- diadem/index/indexed_db.py | 2 +- diadem/search/diadem.py | 10 +- diadem/utilities/neighborhood.py | 62 +++++++++---- diadem/utilities/utils.py | 3 +- pyproject.toml | 20 ++-- 11 files changed, 221 insertions(+), 145 deletions(-) diff --git a/benchmarking_tests/test_deisotoping.py b/benchmarking_tests/test_deisotoping.py index bbb9a33..c671548 100644 --- a/benchmarking_tests/test_deisotoping.py +++ b/benchmarking_tests/test_deisotoping.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np from numpy import array @@ -25,7 +27,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 1600: array( [ @@ -42,7 +44,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 1800: array( [ @@ -60,7 +62,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 2000: array( [ @@ -78,7 +80,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 2200: array( [ @@ -97,7 +99,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 2400: array( [ @@ -116,7 +118,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 2600: array( [ @@ -136,7 +138,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 2800: array( [ @@ -156,7 +158,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 3000: array( [ @@ -176,7 +178,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 3200: array( [ @@ -197,7 +199,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 3400: array( [ @@ -218,7 +220,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 3600: array( [ @@ -239,7 +241,7 @@ 0.000, 0.000, 0.000, - ] + ], ), 3800: array( [ @@ -261,13 +263,13 @@ 0.000, 0.000, 0.000, - ] + ], ), } max_len = max([len(x) for x in average_distributions.values()]) average_distributions_array = np.array( - [np.pad(x, (0, max_len - len(x))) for x in average_distributions.values()] + [np.pad(x, (0, max_len - len(x))) for x in average_distributions.values()], ) @@ -282,7 +284,11 @@ def mass_to_dist(mass): def make_isotope_envelope_vectorized( - mz, charge, intensity, min_intensity, ims: np.ndarray | None = None + mz, + charge, + intensity, + min_intensity, + ims: np.ndarray | None = None, ): """Make an isotopic envelope for a given mz, charge, and intensity. @@ -299,7 +305,7 @@ def make_isotope_envelope_vectorized( >>> out = make_isotope_envelope_vectorized(mzs, charges, ints, 100, ims=np.array([1.0, 2.0])) >>> out (array([300. , 301.003 , 300. , 300.5015]), array([1000., 108., 1000., 326.]), array([1., 1., 2., 2.])) - """ + """ # noqa: E501 dist = average_distributions_array[((mz * charge) // 200).astype(int)] dist = np.einsum("ij, i -> ij", dist, intensity) @@ -323,7 +329,13 @@ def make_isotope_envelope_vectorized( def simulate_isotopes( - min_mz, max_mz, num_peaks, min_charge, max_charge, min_intensity, max_intensity + min_mz, + max_mz, + num_peaks, + min_charge, + max_charge, + min_intensity, + max_intensity, ): """Simulate a set of isotopic peaks.""" mzs = np.random.uniform(min_mz, max_mz, num_peaks) @@ -331,7 +343,10 @@ def simulate_isotopes( charges = np.random.randint(min_charge, max_charge, size=num_peaks) mzs, intensities = make_isotope_envelope_vectorized( - mzs, charges, ints, min_intensity + mzs, + charges, + ints, + min_intensity, ) return mzs, intensities @@ -354,7 +369,10 @@ def simulate_isotopes_ims( charges = np.random.randint(min_charge, max_charge, size=num_peaks) mzs, intensities = make_isotope_envelope_vectorized( - mzs, charges, ints, min_intensity + mzs, + charges, + ints, + min_intensity, ) return mzs, intensities @@ -373,7 +391,9 @@ def _split_ims(ims: float, intensity: float, ims_std: float, ims_binwidth=0.002) """ ims_out = np.random.normal(ims, ims_std, size=int(np.sqrt(intensity)) + 1) ims_intensity = np.random.uniform( - 0, 2 * (intensity) / len(ims_out), size=len(ims_out) + 0, + 2 * (intensity) / len(ims_out), + size=len(ims_out), ) ims_intensity_out = np.histogram( ims_out, @@ -459,9 +479,9 @@ def add_noise(values, snr): def jitter_values(values, std): """Jitter a set of values. - The values are jittered by adding a random number to each value. The random number is - drawn from a normal distribution with a standard deviation equal to the standard deviation - of the values. + The values are jittered by adding a random number to each value. The random number + is drawn from a normal distribution with a standard deviation equal to the standard + deviation of the values. """ noise = np.random.normal(0, std, size=len(values)) return values + noise @@ -507,7 +527,6 @@ def noisy_simple_spectrum(): # noisy complicated spectrum def noisy_complicated_spectrum(): npeaks = 5_000 - pct_noise = 0.2 mzs, ints = simulate_isotopes( 1000, diff --git a/benchmarking_tests/test_scoring_speed.py b/benchmarking_tests/test_scoring_speed.py index 666f1c7..0a09283 100644 --- a/benchmarking_tests/test_scoring_speed.py +++ b/benchmarking_tests/test_scoring_speed.py @@ -31,7 +31,7 @@ def fake_prefiltered_database(fake_database: IndexedDb, request): def score_all_specs_open(db: IndexedDb, specs): """Helper function that scores all the spectra passed as tuples with a database.""" - for mzs, ints, prec_mz in tqdm(specs): + for mzs, ints, _prec_mz in tqdm(specs): db.hyperscore(precursor_mz=(700.0, 720.0), spec_int=ints, spec_mz=mzs) diff --git a/diadem/data_io/__init__.py b/diadem/data_io/__init__.py index 746b74e..fca2b25 100644 --- a/diadem/data_io/__init__.py +++ b/diadem/data_io/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from os import PathLike from diadem.config import DiademConfig diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 947c41c..2b1d53b 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -4,6 +4,7 @@ from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path +from typing import Literal import numpy as np import pandas as pd @@ -71,7 +72,7 @@ def __post_init__(self) -> None: title=f"Base peak chromatogram for the Group in {self.iso_window_name}", ) - def as_dataframe(self): + def as_dataframe(self) -> pd.DataFrame: """Returns a dataframe with the data in the group. The dataframe has the following columns: @@ -201,16 +202,36 @@ def get_precursor_evidence( rt: float, mzs: NDArray[np.float32], mz_tolerance: float, - mz_tolerance_unit="ppm", - ): - # NOTE: This is a first implementation of the functionality, - # therefore it is very simple and prone to optimization and - # rework. + mz_tolerance_unit: Literal["ppm", "Da"] = "ppm", + ) -> tuple[NDArray[np.float32], list[NDArray[np.float32]]]: + """Finds precursor information for a given RT and m/z. - # 1. Find the closest RT. - # 2. Find if there are peaks that match the mzs. - # 3. Return a list of dm and a list of intensities for each. + NOTE: This is a first implementation of the functionality, + therefore it is very simple and prone to optimization and + rework. + 1. Find the closest RT. + 2. Find if there are peaks that match the mzs. + 3. Return a list of dm and a list of intensities for each. + + Parameters + ---------- + rt : float + The retention time to find precursor information for. + mzs : NDArray[np.float32] + The m/z values to find precursor information for. + mz_tolerance : float + The m/z tolerance to use when finding precursor information. + mz_tolerance_unit : str, optional + The unit of the m/z tolerance, by default "ppm" + + Returns + ------- + tuple[NDArray[np.float32], list[NDArray[np.float32]]]] + A array with the list of intensities and a list arrays, + each of which is the for the values integrated for the + intensity values. + """ index = np.searchsorted(self.precursor_rts, rt) slc, center_index = slice_from_center( index, @@ -620,6 +641,24 @@ def _get_iso_window_group( ms2_chunk: DataFrame, ms1_chunk: DataFrame, ) -> ScanGroup: + """Gets all spectra that share the same window. + + Gets all spectra that share the same iso window + and creates a ScanGroup for them. + + Meant for internal usage. + + Parameters + ---------- + iso_window_name : str + Name of the isolation window. + iso_window : tuple[float, float] + Isolation window. + ms2_chunk : DataFrame + DataFrame containing all MS2 spectra. + ms1_chunk : DataFrame + DataFrame containing all MS1 spectra. + """ ( window_mzs, window_ints, @@ -702,8 +741,10 @@ def yield_iso_window_groups(self, progress: bool = False) -> Iterator[ScanGroup] grouped = self.ms2info.sort_values("RTinSeconds").groupby("iso_window") precursor_info = self.ms1info - for i, (iso_window, chunk) in enumerate( - tqdm(grouped, disable=not progress, desc="Unique Isolation Windows"), + for iso_window, chunk in tqdm( + grouped, + disable=not progress, + desc="Unique Isolation Windows", ): iso_window_name = "({:.06f}, {:.06f})".format(*iso_window) diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 2f303a4..45717a1 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -30,6 +30,11 @@ from diadem.utilities.neighborhood import multidim_neighbor_search from diadem.utilities.utils import is_sorted +if "PLOTDIADEM" in os.environ: + import random # noqa: I001 + + from matplotlib import pyplot as plt + IMSError = Literal["abs", "pct"] @@ -287,7 +292,7 @@ def __post_init__(self) -> None: if len(self.imss) != len(self.mzs): raise ValueError("IMS values do not have the same lenth as the MZ values") - def as_dataframe(self): + def as_dataframe(self) -> pd.DataFrame: """Returns a dataframe with the data in the group. The dataframe has the following columns: @@ -501,6 +506,22 @@ def _precursor_iso_window_elements( return out def get_iso_window_groups(self, workerpool: None | Parallel) -> list[TimsScanGroup]: + """Get scan groups for each unique isolation window. + + Parameters + ---------- + workerpool : None | Parallel + If None, the function will be run in serial mode. + If Parallel, the function will be run in parallel mode. + The Parallel is created using joblib.Parallel. + + Returns + ------- + list[TimsScanGroup] + A list of TimsScanGroup objects. + Each of them corresponding to an unique isolation window from + the quadrupole. + """ results = [] if workerpool is None: @@ -521,10 +542,12 @@ def get_iso_window_groups(self, workerpool: None | Parallel) -> list[TimsScanGro return results def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGroup]: + """Yield scan groups for each unique isolation window.""" for i in self.unique_precursor_indices: nested_results = self._precursor_iso_window_groups(i, progress=progress) for r in nested_results.values(): - breakpoint() + # TODO add here a the export of the cached windows + # maybe ... or just add a full interface to read this from cache... yield r @@ -661,7 +684,7 @@ def _get_precursor_index_windows( bar_format="{l_bar}{bar:10}{r_bar}{bar:-10b}", ) quad_splits = {} - for i, (startind, endind) in enumerate(pbar): + for startind, endind in pbar: contig_peak_data = dia_data.convert_from_indices( inds[startind:endind], raw_indices_sorted=True, @@ -708,18 +731,6 @@ def _get_precursor_index_windows( zip(peak_data_vals, curr_peak_data[peak_data_vals].T.values), ) - # # TODO check if it would be more efficient to check for breaks in - # # the quad values and then iterate over the breaks, instead if making it - # # a pandas df and grouping. - # grouped = df_peak_data.groupby(grouping_vals) - # for i, curr_peak_data in grouped: - # current_chunk_data = { k:v for k,v in zip(grouping_vals, i) } - # current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] - - # curr_peak_data.reset_index(drop=True, inplace=True) - # peak_data = dict( - # zip(curr_peak_data.columns, curr_peak_data.T.values) - # ) start_len = len(peak_data["mz_values"]) if not len(peak_data["mz_values"]): @@ -773,13 +784,13 @@ def _get_precursor_index_windows( return quad_splits -import random - -from matplotlib import pyplot as plt - - # @profile -def _preprocess_ims(ims_values, mz_values, intensity_values, mz_range=None): +def _preprocess_ims( + ims_values: NDArray[np.float32], + mz_values: NDArray[np.float32], + intensity_values: NDArray[np.float32], + mz_range: None | tuple[float, float] = None, +) -> tuple[NDArray[np.float32], NDArray[np.float32], NDArray[np.float32]]: # TODO make all of these arguments # or make this callable class. PRELIM_N_PEAK_FILTER = 5_000 # noqa @@ -951,13 +962,13 @@ def _collapse_seeds( # @profile def collapse_ims( - ims_values, - mz_values, - intensity_values, - top_n=500, - top_n_pct=0.2, - ims_tol=0.01, - mz_tol=0.01, + ims_values: NDArray[np.float32], + mz_values: NDArray[np.float32], + intensity_values: NDArray[np.float32], + top_n: int = 500, + top_n_pct: float = 0.2, + ims_tol: float = 0.01, + mz_tol: float = 0.01, ) -> dict[str, NDArray[np.float32]]: """Collapses peaks with similar IMS and MZ. @@ -1005,42 +1016,12 @@ def collapse_ims( ) if top_indices is not None: - tmp_neighborhoods = {top_indices[k]: v for k, v in tmp.right_neighbors.items()} - for k in tmp_neighborhoods: - tmp_neighborhoods[k].add(k) + neighborhoods = {top_indices[k]: v for k, v in tmp.right_neighbors.items()} + for k in neighborhoods: + # TODO check if passing the set directly works + neighborhoods[k].add(k) else: - tmp_neighborhoods = tmp.right_neighbors - - ## New implementation end - - # TODO delete this section one the new implementation is stably tested - # neighborhoods = find_neighbors_mzsort( - # ims_vals=ims_values, - # sorted_mz_values=mz_values, - # intensities=intensity_values, - # top_n=top_n, - # top_n_pct=top_n_pct, - # ims_tol=ims_tol, - # mz_tol=mz_tol, - # ) - # for k, v in tmp_neighborhoods.items(): - # try: - # assert set(neighborhoods[k]) == set(v) - # except AssertionError: - # print( - # "new implementation key: ", k, - # "new implementation values: ", v, - # "old implementation neighbors: ", neighborhoods[k], - # "new implementation errors: ", mz_values[k] - mz_values[list(v)], - # "old implementation errors: ", mz_values[k] - mz_values[list(neighborhoods[k])], - # "old implementation mistakes: ", np.abs(mz_values[k] - mz_values[list(neighborhoods[k])]) < mz_tol, - # "new implementation errors: ", ims_values[k] - ims_values[list(v)], - # "old implementation errors: ", ims_values[k] - ims_values[list(neighborhoods[k])], - # "old implementation mistakes: ", np.abs(ims_values[k] - ims_values[list(neighborhoods[k])]) < ims_tol, - # sep="\n" - # ) - # breakpoint() - neighborhoods = tmp_neighborhoods + neighborhoods = tmp.right_neighbors # unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} @@ -1066,10 +1047,6 @@ def collapse_ims( # This generates the weighted average of the ims and mz # as the new values for the peak. bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) - # highest_intensity = v_intensities.argmax() - # bundled["ims"][i] = ims_values[v[highest_intensity]] - # bundled["mz"][i] = mz_values[v[highest_intensity]] - bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity diff --git a/diadem/deisotoping.py b/diadem/deisotoping.py index 74e8483..f7f64fb 100644 --- a/diadem/deisotoping.py +++ b/diadem/deisotoping.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np from numpy.typing import NDArray @@ -25,14 +27,14 @@ def ppm_to_delta_mass(obs: float, ppm: float) -> float: # @profile def _deisotope_with_ims_arrays( - mzs, - intensities, - imss=None, - max_charge=5, - ims_tolerance=0.01, - mz_tolerance=0.01, - track_indices=False, -): + mzs: NDArray[np.float32], + intensities: NDArray[np.float32], + imss: NDArray[np.float32] | None = None, + max_charge: int = 5, + ims_tolerance: float = 0.01, + mz_tolerance: float = 0.01, + track_indices: bool = False, +) -> dict[str, NDArray[np.float32]]: """Deisotope a spectrum with IMS data. The current implementation allows only absolute values for the ims and mz @@ -135,7 +137,7 @@ def _deisotope_with_ims_arrays( # intensity now bleongs to another peak). f_peaks = _filter_peaks(peaks, extract=extract_values) - f_peaks = {k: v for k, v in zip(extract_values, f_peaks)} + f_peaks = dict(zip(extract_values, f_peaks)) return f_peaks @@ -176,7 +178,7 @@ def deisotope( (array([800.9 , 803.408]), array([1. , 2.4])) >>> deisotope(my_mzs, my_intens, max_charge=2, diff=5.0, unit="ppm", track_indices=True) (array([800.9 , 803.408]), array([1. , 2.4]), ([0], [1, 2, 3])) - """ + """ # noqa: if unit.lower() == "da": mz_tolerance = diff @@ -251,7 +253,7 @@ def deisotope_with_ims( >>> deisotope_with_ims(my_mzs, my_intens, my_imss, max_charge=2, ... mz_diff=5.0, mz_unit="ppm", ims_diff=0.01, ims_unit="abs", track_indices=True) (array([800.9 , 803.408, 803.409]), array([1. , 2.1, 1.4]), array([0.7, 0.7, 0.8]), ([0], [1, 3, 5], [2, 4])) - """ + """ # noqa E501 if mz_unit.lower() == "da": mz_tolerance = mz_diff diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 8e83cfa..6e2f405 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -751,7 +751,7 @@ def score_arrays( dms = np.abs(dms - fragment_mz) candidates = [x for x, y in zip(candidates, dms <= ms2_tol) if y] - for (seq, frag, series), dm in zip(candidates, dms): + for (seq, _frag, series), dm in zip(candidates, dms): # Should tolerances be checked here? # IN THEORY, they should have been filtered in the past. peaks.append( diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index bebf12f..dba05db 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -21,7 +21,7 @@ # @profile -def search_group( +def search_group( # noqa C901 `search_group` is too complex (18) group: ScanGroup | TimsScanGroup, db: IndexedDb, config: DiademConfig, @@ -168,6 +168,10 @@ def search_group( top_n=100, ) + # TODO: implement here a + # partial ms2-score and then a follow up + # ms1 score + # if scores is not None: # rt = group.retention_times[new_stack.ref_index] # prec_intensity, prec_dms = group.get_precursor_evidence( @@ -177,7 +181,9 @@ def search_group( # mz_tolerance_unit=MS1_TOLERANCE_UNIT, # ) # scores["PrecursorIntensity"] = prec_intensity - # scores.drop(scores[scores.PrecursorIntensity < 100].index, inplace = True) + # scores.drop( + # scores[scores.PrecursorIntensity < 100].index, + # inplace = True) # scores.reset_index(drop=True, inplace=True) # scores["rank"] = scores["Score"].rank(ascending=False, method="min") # if len(scores) == 0: diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index 45f22e2..f9d912c 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -262,8 +262,8 @@ def find_neighbors_sorted( ) -> IndexBipartite: """Finds neighbors between to sorted arrays. - Arguments: - --------- + Parameters + ---------- x, NDArray: First array to use to find neighbors y, NDArray: @@ -280,8 +280,19 @@ def find_neighbors_sorted( low_dist: Highest value allowable as a distance for two elements to be considered neighbors + high_dist: + Highest value allowable as a distance for two elements. + allowed_neighbors: + A dictionary that contains the allowed neighbors for each + element in `x`. The keys of the dictionary are the indices + of the elements in `x` and the values are sets that contain + the indices of the elements in `y` that are allowed to be + neighbors of the element in `x`. If this is None then all + elements in `y` are allowed to be neighbors of the elements + in `x`. - Examples: + + Examples -------- >>> x = np.array([1.,2.,3.,4.,5.,15.,25.]) >>> y = np.array([1.1, 2.3, 3.1, 4., 25., 25.1]) @@ -293,7 +304,7 @@ def find_neighbors_sorted( right_neighbors={0: [0], 2: [2], 3: [3], 4: [6], 5: [6]}) >>> find_neighbors_sorted(x,y,dist_fun,low_dist, high_dist, allowed_neighbors={6: {5, 4}}) IndexBipartite(left_neighbors={6: [4, 5]}, right_neighbors={4: [6], 5: [6]}) - """ + """ # noqa: E501 assert is_sorted(x) assert is_sorted(y) assert low_dist < high_dist @@ -365,15 +376,17 @@ def find_neighbors_multi_vectorized( In addition it (in theory) does some of the merging operations in a vectorized manner, which should be faster due to the overhead of cpu-cache moving overhead. - Arguments: - --------- - x, y list[NDArray]: + Parameters + ---------- + x, list[NDArray]: List of arrays to use to find neighbors. the two lists need to be the same length and the length of each element in each of the lists needs to be the same length. For example: if x is a list of 5 arrays, each of length 200; then y needs to be a list of 5 arrays, but the length of each sub-array can be any length (as long as they are all the same...). + y, list[NDArray]: + See the description of x. dist_funs, list[callable]: List of functions to calculate the distance between an element in `x` and an element in `y`. @@ -390,7 +403,7 @@ def find_neighbors_multi_vectorized( List of highest value allowable as a distance for two elements to be considered neighbors. - Returns: + Returns ------- IndexBipartite: The neighbors found between the two arrays. @@ -432,8 +445,8 @@ def find_neighbors_vectorized( distances between elements, BUT due to vectorization, cpu caching and not going through the iteration in python. - Arguments: - --------- + Parameters + ---------- x, NDArray: First array to use to find neighbors y, NDArray: @@ -447,11 +460,11 @@ def find_neighbors_vectorized( low_dist: Lowest value allowable as a distance for two elements to be considered neighbors - low_dist: + high_dist: Highest value allowable as a distance for two elements to be considered neighbors - Examples: + Examples -------- >>> x = np.array([1.,2.,3.,4.,5.,15.,25.]) >>> y = np.array([1.1, 2.3, 3.1, 4., 25., 25.1]) @@ -520,9 +533,11 @@ def multidim_neighbor_search( ) -> IndexBipartite: """Searches for neighbors in multiple dimensions. - Arguments: - --------- - elems1 and elems2, dict[str,NDArray]: + Parameters + ---------- + elems1, dict[str,NDArray]: + Seel elems2 + elems2, dict[str,NDArray] | None: A dictionary of arrays. All arrays within one of those elements need to have the same length. @@ -534,7 +549,7 @@ def multidim_neighbor_search( dimension_order, optional str: Optional tuple of strings denoting what dimensions to use. - Examples: + Examples -------- >>> x1 = {"d1": np.array([1000., 1000., 2001., 3000.]), ... "d2": np.array([1000., 1000.3, 2000., 3000.01])} @@ -545,8 +560,8 @@ def multidim_neighbor_search( >>> multidim_neighbor_search( ... x1, x2, d_ranges, d_funs ... ) - IndexBipartite(left_neighbors={0: [0, 1]}, right_neighbors={0: [0], 1: [0]}) - """ + IndexBipartite(left_neighbors={0: {0, 1}, 2: {2}}, right_neighbors={0: {0}, 1: {0}, 2: {2}}) + """ # noqa: E501 if dimension_order is None: dimension_order = list(elems1.keys()) @@ -594,7 +609,16 @@ def _multidim_neighbor_search( dist_ranges: dict[str, tuple[float, float]], dist_funs: dict[str, callable], dimension_order: tuple[str], -): +) -> IndexBipartite: + """Searches for neighbors in multiple dimensions. + + This internal function is used by `multidim_neighbor_search` and + is not intended to be used directly. Since it does not have the + safety guarantees that the public function has. + + For the parameters deltails please check the documentation of the + public function. + """ neighbors = IndexBipartite() # ii = 0 assert is_sorted(elems_1[dimension_order[0]]) diff --git a/diadem/utilities/utils.py b/diadem/utilities/utils.py index 782b2fa..a1dcb4c 100644 --- a/diadem/utilities/utils.py +++ b/diadem/utilities/utils.py @@ -91,6 +91,7 @@ def make_decoy(pep: Peptide) -> Peptide: return pep +# @profile def get_slice_inds(arr: NDArray, minval: float, maxval: float) -> slice: """Gets the slide indices that include a range. @@ -125,7 +126,7 @@ def get_slice_inds(arr: NDArray, minval: float, maxval: float) -> slice: # slice_max = np.searchsorted(arr[slice_min:], maxval, side="right") # slice_max = slice_min + slice_max i = 0 - for i, val in enumerate(arr[slice_min:]): + for _i, val in enumerate(arr[slice_min:]): if val > maxval: break diff --git a/pyproject.toml b/pyproject.toml index f39c1eb..687366b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,9 +20,9 @@ classifiers = [ "Topic :: Scientific/Engineering :: Bio-Informatics", ] dependencies = [ - "pandas >= 1.5.2", - "numpy >= 1.23.5", - "ms2ml >= 0.0.33", + "pandas >= 2.0.0", + "numpy == 1.23.5", # Pinned because of numba + "ms2ml >= 0.0.35", "tqdm >= 4.64.1", "loguru >= 0.6.0", "rich-click >= 1.6.0", @@ -56,11 +56,14 @@ test = [ profiling = [ "line_profiler", ] +plot = [ + "matplotlib" +] dev = [ - "ruff", - "black", - "isort", - "pylance", + "ruff >= 0.0.253", + "black >= 23.1.0", + "isort >= 5.12.0", + "pylance >= 0.3.9", ] @@ -78,7 +81,8 @@ testpaths = [ [tool.ruff] line-length = 88 -select = ["E", "F", "W", "C", "I", "D", "UP", "N", "ANN", "T20", "COM"] +select = ["E", "F", "B","W", "C", "I", "D", "UP", "N", "ANN", "T20", "COM"] +target-version = "py39" # ANN101 Missing type annotation for `self` in method # D213 Multi-line docstring summary should start at the second lin From 2018ea078f930e585118efe9d3da1fb33dbfbbc3 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 25 Apr 2023 17:02:33 -0700 Subject: [PATCH 13/41] (wip) ruff compliance --- benchmarking_tests/test_deisotoping.py | 45 +++++++++++++++++++++++--- diadem/data_io/timstof.py | 23 +++++++++++++ diadem/utilities/neighborhood.py | 10 +++--- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/benchmarking_tests/test_deisotoping.py b/benchmarking_tests/test_deisotoping.py index c671548..afd6881 100644 --- a/benchmarking_tests/test_deisotoping.py +++ b/benchmarking_tests/test_deisotoping.py @@ -451,7 +451,7 @@ def extend_ims(seed_mzs, intensities, ims_start=0.7, ims_end=1.2, std_ims=0.01): ) -def simulate_ims_isotopes( +def simulate_ims_isotopes( # noqa: D103 min_mz, max_mz, num_peaks, @@ -462,7 +462,7 @@ def simulate_ims_isotopes( min_ims, max_ims, ): - pass + raise NotImplementedError def add_noise(values, snr): @@ -502,7 +502,16 @@ def get_noise_peaks(mz_min, mz_max, ints, quantile, pct): # clean simple spectrum -def clean_simple_spectrum(): +def clean_simple_spectrum() -> tuple[np.ndarray, np.ndarray]: + """Generate a clean simple spectrum. + + Returns + ------- + mzs : np.ndarray + The m/z values of the peaks. + intensities : np.ndarray + The intensities of the peaks. + """ mzs, ints = simulate_isotopes(1000, 2000, 100, 1, 5, 1000, 10000) ints = add_noise(ints, 100) return mzs, ints @@ -510,6 +519,15 @@ def clean_simple_spectrum(): # clean complicated spectrum def clean_complicated_spectrum(): + """Generate a clean simple spectrum. + + Returns + ------- + mzs : np.ndarray + The m/z values of the peaks. + intensities : np.ndarray + The intensities of the peaks. + """ mzs, ints = simulate_isotopes(1000, 2000, 100, 1, 5, 1000, 10000) ints = add_noise(ints, 100) mzs = jitter_values(mzs, 0.01) @@ -521,11 +539,29 @@ def clean_complicated_spectrum(): # noisy simple spectrum def noisy_simple_spectrum(): - pass + """Generate a noisy simple spectrum. + + Returns + ------- + mzs : np.ndarray + The m/z values of the peaks. + intensities : np.ndarray + The intensities of the peaks. + """ + raise NotImplementedError # noisy complicated spectrum def noisy_complicated_spectrum(): + """Generate a noisy complicated spectrum. + + Returns + ------- + mzs : np.ndarray + The m/z values of the peaks. + intensities : np.ndarray + The intensities of the peaks. + """ npeaks = 5_000 mzs, ints = simulate_isotopes( @@ -537,6 +573,7 @@ def noisy_complicated_spectrum(): min_intensity=1_000, max_intensity=100_000, ) + raise NotImplementedError if __name__ == "__main__": diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 45717a1..df4f6a2 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -280,6 +280,8 @@ def _bin_spectrum_intensities( @dataclass class TimsScanGroup(ScanGroup): + """Represent all 'spectra' that share an isolation window.""" + imss: list[NDArray] precursor_imss: list[NDArray] @@ -318,6 +320,15 @@ def get_highest_window( min_correlation: float, max_peaks: int, ) -> TimsStackedChromatograms: + """Gets the highest intensity window of the chromatogram. + + Briefly ... + 1. Gets the highes peak accross all spectra in the chromatogram range. + 2. Finds what peaks are in that same spectrum. + 3. Looks for spectra around that spectrum. + 4. extracts the chromatogram for all mzs in the "parent spectrum" + + """ top_index = np.argmax(self.base_peak_int) window = TimsStackedChromatograms.from_group( self, @@ -365,11 +376,23 @@ def _scale_spectrum_at( self.base_peak_mz[i] = -1 def __len__(self) -> int: + """Returns the number of spectra in the group.""" return len(self.imss) class TimsSpectrumStacker(SpectrumStacker): + """Helper class that stacks the spectra of TimsTof file into chromatograms.""" + def __init__(self, filepath: PathLike, config: DiademConfig) -> None: + """Initializes the class. + + Parameters + ---------- + filepath : PathLike + Path to the TimsTof file + config : DiademConfig + Configuration object + """ self.filepath = filepath self.config = config with self.lazy_datafile() as datafile: diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index f9d912c..9dbf2a3 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -124,7 +124,7 @@ def from_arrays(cls, x: NDArray, y: NDArray) -> IndexBipartite: that match between them. For example element i in the array y is a neighbor of element i in array x. - Examples: + Examples -------- >>> a = np.array([1,1,1,51]) >>> b = np.array([2,3,4,1]) @@ -149,13 +149,13 @@ def intersect(self, other: IndexBipartite) -> IndexBipartite: other: IndexBipartite The other bipartite graph to intersect with. - Returns: + Returns ------- IndexBipartite A new bipartite graph that contains the intersection of both graphs. - Examples: + Examples -------- >>> bg1 = IndexBipartite() >>> bg1.add_connection(1,1) @@ -502,13 +502,13 @@ def _apply_vectorized(x: NDArray, y: NDArray, fun: callable) -> NDArray: fun: callable Function to apply to all combinations of elements in x and y - Returns: + Returns ------- NDArray Array of shape (len(x), len(y)) with the results of the function applied to all combinations of elements in x and y - Examples: + Examples -------- >>> x = np.array([1.,5.,15.,25.]) >>> y = np.array([1.1, 25.1]) From 12c57b828065893af9e2f2435e395c24d80fd67f Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 25 Apr 2023 17:12:24 -0700 Subject: [PATCH 14/41] (wip) prettyfied deisotoping testing --- benchmarking_tests/test_deisotoping.py | 260 ++----------------------- 1 file changed, 13 insertions(+), 247 deletions(-) diff --git a/benchmarking_tests/test_deisotoping.py b/benchmarking_tests/test_deisotoping.py index afd6881..92de094 100644 --- a/benchmarking_tests/test_deisotoping.py +++ b/benchmarking_tests/test_deisotoping.py @@ -12,258 +12,24 @@ 800: array([1.000, 0.436, 0.116, 0.023, 0.004, 0.000, 0.000, 0.000]), 1000: array([1.000, 0.536, 0.167, 0.038, 0.007, 0.001, 0.000, 0.000, 0.000]), 1200: array([1.000, 0.645, 0.238, 0.064, 0.014, 0.003, 0.000, 0.000, 0.000]), - 1400: array( - [ - 1.000, - 0.757, - 0.367, - 0.133, - 0.039, - 0.009, - 0.002, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 1600: array( - [ - 1.000, - 0.868, - 0.461, - 0.181, - 0.057, - 0.015, - 0.003, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 1800: array( - [ - 1.000, - 0.976, - 0.565, - 0.242, - 0.083, - 0.024, - 0.006, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 2000: array( - [ - 0.923, - 1.000, - 0.629, - 0.290, - 0.107, - 0.033, - 0.009, - 0.002, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 2200: array( - [ - 0.837, - 1.000, - 0.680, - 0.336, - 0.133, - 0.044, - 0.013, - 0.003, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 2400: array( - [ - 0.768, - 1.000, - 0.731, - 0.386, - 0.163, - 0.058, - 0.018, - 0.005, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 2600: array( - [ - 0.708, - 1.000, - 0.784, - 0.442, - 0.198, - 0.074, - 0.024, - 0.007, - 0.002, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 2800: array( - [ - 0.662, - 1.000, - 0.831, - 0.494, - 0.233, - 0.092, - 0.031, - 0.009, - 0.003, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 3000: array( - [ - 0.617, - 1.000, - 0.884, - 0.557, - 0.277, - 0.115, - 0.041, - 0.013, - 0.004, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), - 3200: array( - [ - 0.578, - 1.000, - 0.936, - 0.622, - 0.327, - 0.143, - 0.054, - 0.018, - 0.005, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], - ), + 1400: array([1.000, 0.757, 0.367, 0.133, 0.039, 0.009, 0.002]), + 1600: array([1.000, 0.868, 0.461, 0.181, 0.057, 0.015, 0.003, 0.001]), + 1800: array([1.000, 0.976, 0.565, 0.242, 0.083, 0.024, 0.006, 0.001]), + 2000: array([0.923, 1.000, 0.629, 0.290, 0.107, 0.033, 0.009, 0.002]), + 2200: array([0.837, 1.000, 0.680, 0.336, 0.133, 0.044, 0.013, 0.003, 0.001]), + 2400: array([0.768, 1.000, 0.731, 0.386, 0.163, 0.058, 0.018, 0.005, 0.001]), + 2600: array([0.708, 1.000, 0.784, 0.442, 0.198, 0.074, 0.024, 0.007, 0.002]), + 2800: array([0.662, 1.000, 0.831, 0.494, 0.233, 0.092, 0.031, 0.009, 0.003, 0.001]), + 3000: array([0.617, 1.000, 0.884, 0.557, 0.277, 0.115, 0.041, 0.013, 0.004, 0.001]), + 3200: array([0.578, 1.000, 0.936, 0.622, 0.327, 0.143, 0.054, 0.018, 0.005, 0.001]), 3400: array( - [ - 0.543, - 1.000, - 0.990, - 0.692, - 0.381, - 0.175, - 0.069, - 0.024, - 0.008, - 0.002, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], + [0.543, 1.000, 0.990, 0.692, 0.381, 0.175, 0.069, 0.024, 0.008, 0.002, 0.001] ), 3600: array( - [ - 0.493, - 0.959, - 1.000, - 0.735, - 0.424, - 0.203, - 0.084, - 0.031, - 0.010, - 0.003, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], + [0.493, 0.959, 1.000, 0.735, 0.424, 0.203, 0.084, 0.031, 0.010, 0.003, 0.001] ), 3800: array( - [ - 0.444, - 0.913, - 1.000, - 0.770, - 0.464, - 0.233, - 0.101, - 0.038, - 0.013, - 0.004, - 0.001, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - 0.000, - ], + [0.444, 0.913, 1.000, 0.770, 0.464, 0.233, 0.101, 0.038, 0.013, 0.004, 0.001] ), } From ffc1ccbeca8bb8d011a4ac964306aab9bd583056 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 26 Apr 2023 14:56:34 -0700 Subject: [PATCH 15/41] added ims and rt info to the output file --- diadem/data_io/timstof.py | 2 +- diadem/search/diadem.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index df4f6a2..7e11a97 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -180,7 +180,7 @@ def from_group( u_mzs, u_intensities, inv = _bin_spectrum_intensities( m, inten, - bin_width=0.02, + bin_width=0.001, bin_offset=0, ) diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index dba05db..74d1385 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -195,6 +195,9 @@ def search_group( # noqa C901 `search_group` is too complex (18) if scores is not None: scores["id"] = match_id + scores["RetentionTime"] = group.retention_times[new_stack.ref_index] + if hasattr(group, "imss"): + scores["IonMobility"] = new_stack.ref_ims if scores["decoy"].iloc[0]: num_decoys += 1 else: From f5c1b8d01b76b1dbd8ee8a42565613d4503b61ee Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 26 Apr 2023 15:01:28 -0700 Subject: [PATCH 16/41] removed some dead code --- diadem/utilities/neighborhood.py | 185 ++----------------------------- 1 file changed, 7 insertions(+), 178 deletions(-) diff --git a/diadem/utilities/neighborhood.py b/diadem/utilities/neighborhood.py index 9dbf2a3..05863b0 100644 --- a/diadem/utilities/neighborhood.py +++ b/diadem/utilities/neighborhood.py @@ -117,9 +117,11 @@ def get_matching_indices(self) -> tuple[NDArray, NDArray]: def from_arrays(cls, x: NDArray, y: NDArray) -> IndexBipartite: """Builds a bipartite index from two arrays. - Arguments: - --------- - x, y: NDArray + Parameters + ---------- + x: NDArray + See the documentation of y. + y: NDArray Two arrays of the same length that contain the indices that match between them. For example element i in the array y is a neighbor of element i in array x. @@ -144,8 +146,8 @@ def from_arrays(cls, x: NDArray, y: NDArray) -> IndexBipartite: def intersect(self, other: IndexBipartite) -> IndexBipartite: """Returns the intersection of two bipartite graphs. - Arguments: - --------- + Parameters + ---------- other: IndexBipartite The other bipartite graph to intersect with. @@ -322,8 +324,6 @@ def find_neighbors_sorted( for i in iter_x: x_val = x[i] - # if (abs(x_val - 0.8112) < 1e-4): - # breakpoint() last_diff = None if allowed_neighbors is None: @@ -334,11 +334,6 @@ def find_neighbors_sorted( for j in iter_y: y_val = y[j] - # if (abs(x_val - 401.7911) < 1e-4) & (abs(y_val - 401.8036) < 1e-4): - # breakpoint() - - # if (abs(x_val - 0.8112) < 1e-4) & (abs(y_val - 0.8085) < 1e-4): - # breakpoint() diff = dist_fun(x_val, y_val) # TODO disable this for performance ... @@ -357,172 +352,6 @@ def find_neighbors_sorted( return neighbors -def find_neighbors_multi_vectorized( - x: list[NDArray], - y: list[NDArray], - dist_funs: list[callable], - low_dists: list[float], - high_dists: list[float], -) -> IndexBipartite: - """Finds Neighbors in multiple dimensions. - - This is the generalized version of `find_neighbors_vectorized`. - This function allows for multiple dimensions to be used to find neighbors. - - It is more efficient than calling the `find_neighbors_vectorized` function - multiple times because it does not require many intermediate representations - of the data. - - In addition it (in theory) does some of the merging operations in a vectorized - manner, which should be faster due to the overhead of cpu-cache moving overhead. - - Parameters - ---------- - x, list[NDArray]: - List of arrays to use to find neighbors. - the two lists need to be the same length and the length of each element in - each of the lists needs to be the same length. - For example: if x is a list of 5 arrays, each of length 200; then y needs to - be a list of 5 arrays, but the length of each sub-array can be any length - (as long as they are all the same...). - y, list[NDArray]: - See the description of x. - dist_funs, list[callable]: - List of functions to calculate the distance between an element - in `x` and an element in `y`. - The list should be the same length as `x` and `y`. - Note that this asumes directionality and should increase in value. - - In other words ... - dist_fun(x[0], y[0]) < dist_fun(x[0], y[1]); assuming that y[1] > y[0] - low_dists, list[float]: - List of lowest value allowable as a distance for two elements - to be considered neighbors. - The list should be the same length as `x` and `y`. - high_dists, list[float]: - List of highest value allowable as a distance for two elements - to be considered neighbors. - - Returns - ------- - IndexBipartite: - The neighbors found between the two arrays. - """ - neighbors = IndexBipartite() - final_in_range = None - - my_iter = zip( - x, - y, - dist_funs, - low_dists, - high_dists, - ) - for xi, yi, dist_fun, low_dist, high_dist in my_iter: - diffs = _apply_vectorized(xi, yi, dist_fun) - in_range = (diffs > low_dist) & (diffs < high_dist) - if final_in_range is None: - final_in_range = in_range - else: - final_in_range = final_in_range & in_range - - inds = np.where(final_in_range) - neighbors = IndexBipartite.from_arrays(*inds) - return neighbors - - -def find_neighbors_vectorized( - x: NDArray, - y: NDArray, - dist_fun: callable, - low_dist: float, - high_dist: float, -) -> IndexBipartite: - """Finds neighbors between to sorted arrays. - - This version uses a vectorized version of the calculation. - In theory it should take longer, since it calculates all - distances between elements, BUT due to vectorization, cpu caching - and not going through the iteration in python. - - Parameters - ---------- - x, NDArray: - First array to use to find neighbors - y, NDArray: - Second array to use to find neighbors - dist_fun: - Function to calculate the distance between an element - in `x` and an element in `y`. - Note that this asumes directionality and should increase in value. - In other words ... - dist_fun(x[0], y[0]) < dist_fun(x[0], y[1]); assuming that y[1] > y[0] - low_dist: - Lowest value allowable as a distance for two elements - to be considered neighbors - high_dist: - Highest value allowable as a distance for two elements - to be considered neighbors - - Examples - -------- - >>> x = np.array([1.,2.,3.,4.,5.,15.,25.]) - >>> y = np.array([1.1, 2.3, 3.1, 4., 25., 25.1]) - >>> dist_fun = lambda x,y: y - x - >>> low_dist = -0.11 - >>> high_dist = 0.11 - >>> find_neighbors_vectorized(x,y,dist_fun,low_dist, high_dist) - IndexBipartite(left_neighbors={0: [0], 2: [2], 3: [3], 6: [4, 5]}, - right_neighbors={0: [0], 2: [2], 3: [3], 4: [6], 5: [6]}) - >>> low_dist = -1.11 - >>> high_dist = -0.98 - >>> out = find_neighbors_vectorized(x,y,dist_fun,low_dist, high_dist) - >>> out - IndexBipartite(left_neighbors={4: [3]}, right_neighbors={3: [4]}) - >>> [[f"{x[k]} matches {y[w]}" for w in v] for k, v in out.left_neighbors.items()] - [['5.0 matches 4.0']] - """ - assert low_dist < high_dist - neighbors = IndexBipartite() - - diffs = _apply_vectorized(x, y, dist_fun) - in_range = (diffs > low_dist) & (diffs < high_dist) - inds = np.where(in_range) - neighbors = IndexBipartite.from_arrays(*inds) - return neighbors - - -def _apply_vectorized(x: NDArray, y: NDArray, fun: callable) -> NDArray: - """Applies a function to all combinations of elements in x and y. - - Arguments: - --------- - x, y: NDArray - One dimensional Arrays to apply the function to - fun: callable - Function to apply to all combinations of elements in x and y - - Returns - ------- - NDArray - Array of shape (len(x), len(y)) with the results of the function - applied to all combinations of elements in x and y - - Examples - -------- - >>> x = np.array([1.,5.,15.,25.]) - >>> y = np.array([1.1, 25.1]) - >>> fun = lambda x,y: y - x - >>> _apply_vectorized(x,y,fun) - array([[ 0.1, 24.1], - [ -3.9, 20.1], - [-13.9, 10.1], - [-23.9, 0.1]]) - """ - outs = fun(np.tile(np.expand_dims(x, axis=-1), len(y)), y) - return outs - - # @profile def multidim_neighbor_search( elems1: dict[str, NDArray], From fb20fec8b98fc2812fab57e4c71b00f897c35f03 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Wed, 26 Apr 2023 15:08:23 -0700 Subject: [PATCH 17/41] Added run-level mokapot and updated rt --- .../{rt_alignment => aggregate}/__init__.py | 0 .../{rt_alignment => aggregate}/rt_model.py | 208 +++++++++--------- diadem/index/indexed_db.py | 2 +- diadem/index/protein_index.py | 2 +- diadem/search/diadem.py | 9 +- diadem/search/mokapot.py | 155 +++++++++++++ diadem/search/search_utils.py | 57 ----- pyproject.toml | 5 +- tests/unit_tests/test_mokapot.py | 76 +++++++ 9 files changed, 345 insertions(+), 169 deletions(-) rename diadem/{rt_alignment => aggregate}/__init__.py (100%) rename diadem/{rt_alignment => aggregate}/rt_model.py (52%) create mode 100644 diadem/search/mokapot.py delete mode 100644 diadem/search/search_utils.py create mode 100644 tests/unit_tests/test_mokapot.py diff --git a/diadem/rt_alignment/__init__.py b/diadem/aggregate/__init__.py similarity index 100% rename from diadem/rt_alignment/__init__.py rename to diadem/aggregate/__init__.py diff --git a/diadem/rt_alignment/rt_model.py b/diadem/aggregate/rt_model.py similarity index 52% rename from diadem/rt_alignment/rt_model.py rename to diadem/aggregate/rt_model.py index e7fd51f..6ee5e7c 100644 --- a/diadem/rt_alignment/rt_model.py +++ b/diadem/aggregate/rt_model.py @@ -1,16 +1,20 @@ -"""This module contains a matrix factorization model.""" +"""The retention time matrix factorization model.""" +from __future__ import annotations + import logging import numpy as np import pandas as pd import torch import torch.nn as nn +from sklearn.base import BaseEstimator +from sklearn.exceptions import NotFittedError from tqdm import trange LOGGER = logging.getLogger(__name__) -class MatrixModel(nn.Module): +class MatrixFactorizationModel(nn.Module): """The PyTorch matrix factorization model. Parameters @@ -21,53 +25,45 @@ class MatrixModel(nn.Module): The number of runs. n_factors: int The number of latent factors. + rng : int | numpy.random.Generator + The random number generator. """ - def __init__(self, n_peptides: int, n_runs: int, n_factors: int) -> None: + + def __init__( + self, + n_peptides: int, + n_runs: int, + n_factors: int, + rng: int | np.random.Generator, + ) -> None: """Initialize an ImputerModel.""" super().__init__() self.n_peptides = n_peptides self.n_runs = n_runs self.n_factors = n_factors - - torch.manual_seed(0) + self.rng = np.random.default_rng(rng) + + torch.manual_seed(self.rng.integers(1, 100000)) # The model: - self.peptide_factors = torch.randn(n_peptides, n_factors, requires_grad=True) - self.run_factors = torch.randn(n_runs, n_factors, requires_grad=True) + self.peptide_factors = nn.Parameter(torch.randn(n_peptides, n_factors)) + self.run_factors = nn.Parameter(torch.randn(n_factors, n_runs)) - def forward(self, X, non_zero_mask) -> torch.Tensor: - """Reconstruct the matrix and determine prediction error. - - Parameters - ---------- - X : torch.Tensor of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as -1. - non_zero_mask: torch.Tensor of shape (n_peptides, n_runs) - Tensor of boolean values where missing values are False. + def forward(self) -> torch.Tensor: + """Reconstruct the matrix. Returns ------- - torch.Tensor of shape (1, 1) - Difference between the expected and predicted RTs. + torch.Tensor of shape (n_peptide, n_runs) + The reconstructed matrix. """ - - predicted = torch.mm(self.peptide_factors, self.run_factors.T) - - diff = (X - predicted)**2 - prediction_error = torch.sum(diff*non_zero_mask) - - return prediction_error - - def get_params(self): - """Return the peptide and run factors.""" - return self.peptide_factors, self.run_factors - - - -class RTImputer: + return torch.mm(self.peptide_factors, self.run_factors) + + +class RTImputer(BaseEstimator): """A retention time prediction model. - This is a PyTorch model wrapped in a sklearn-like API. + RTImputer is a PyTorch model wrapped in a sklearn API. Parameters ---------- @@ -82,26 +78,36 @@ class RTImputer: n_iter_no_change : int, optional The number of iterations to wait before triggering early stopping. - device : str or torch.Device + lr: float, optional + The learning rate. + device : str or torch.Device, optional A valid PyTorch device on which to perform the optimization. - + silent : bool, optional + Disable logging. + rng : int | np.random.Generator | None, optional + The random number generator. """ def __init__( - self, - n_factors: int, - max_iter: int = 1000, - tol: float = 1e-4, - n_iter_no_change: int = 20, - device: str | torch.device = "cpu", - ): - """Initialize the RTImputer""" + self, + n_factors: int, + max_iter: int = 1000, + tol: float = 1e-4, + n_iter_no_change: int = 20, + lr: float = 0.1, + device: str | torch.device = "cpu", + silent: bool = False, + rng: int | np.random.Generator | None = None, + ) -> None: + """Initialize the RTImputer.""" # Parameters: self.n_factors = n_factors self.max_iter = max_iter self.tol = tol self.n_iter_no_change = n_iter_no_change self.device = device + self.lr = lr + self.silent = silent # Set during fit: self._model = None @@ -111,8 +117,11 @@ def __init__( self._means = None @property - def model_(self) -> MatrixModel: + def model_(self) -> MatrixFactorizationModel: """The underlying PyTorch model.""" + if self._model is None: + raise NotFittedError("This model has not been fit yet.") + return self._model @property @@ -122,74 +131,50 @@ def history_(self) -> pd.DataFrame: self._history, columns=["iteration", "train_loss"], ) - - def predict(self, only_missing: bool, non_zero_mask: torch.Tensor, X: torch.Tensor=None) -> torch.Tensor: - """Reconstruct the matrix. + + def transform(self, X: np.array | torch.Tensor) -> np.array: # noqa: N803 + """Impute missing retention times. Parameters ---------- - only_missing : bool - True if predicted model should only predict missing values. - False if predicted model should return predicted values for all peptides/runs. - non_zero_mask: torch.Tensor of shape (n_peptides, n_runs) - Tensor of boolean values where missing values are False. X : torch.Tensor of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as -1. - + The retention time array. Missing peptides should be denoted as np.nan. + Returns ------- - torch.Tensor of shape (n_peptides, n_runs) + np.array of shape (n_peptides, n_runs) The predicted retention time matrix. """ - - peptide_factors, run_factors = self._model.get_params() - if only_missing: - pred = torch.mm(peptide_factors, run_factors.T).detach().numpy() - try: - X = X.copy() - except AttributeError: - res = X.clone().numpy() - res[~non_zero_mask] = pred[~non_zero_mask] - return res - - return torch.mm(self.peptide_factors, self.run_factors.T).detach().numpy() - - - def fit(self, X: np.ndarray, only_missing: bool=True) -> torch.Tensor: - """Fit the RTImputer. + # Prepare the input and initialize model + X = to_tensor(X) + mask = torch.isnan(X) + X_hat = self.model().to("cpu").detach() + X[mask] = X_hat[mask] + return X.numpy() + + def fit(self, X: np.ndarray | torch.Tensor) -> RTImputer: + """Fit the model. Parameters ---------- - X : numpy.ndarray of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as -1. - only_missing : bool - True if predicted model should only predict missing values. - False if predicted model should return predicted values for all peptides/runs. + X : array of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as np.nan. Returns ------- - torch.Tensor of shape (n_peptides, n_runs) - The predicted retention time matrix. + self """ - LOGGER.info("Training retention time predictor...") - - # Set seed: - torch.manual_seed(0) # Prepare the input and initialize model X = to_tensor(X) + mask = ~torch.isnan(X) X = X.to(self.device, torch.float32) - X[torch.isnan(X)] = -1 - + self._shape = X.shape - self._model = MatrixModel(*X.shape, self.n_factors).to(self.device) + self._model = MatrixFactorizationModel(*X.shape, self.n_factors).to(self.device) self._history = [] - - # Get actual values to compare model against - non_zero_mask = (X != -1) - - optimizer = torch.optim.RMSprop(self._model.get_params(), lr=.07) + optimizer = torch.optim.RMSprop(self._model.parameters(), lr=self.lr) log_interval = max(1, self.max_iter // 20) LOGGER.info("Logging every %i iterations...", log_interval) @@ -199,17 +184,18 @@ def fit(self, X: np.ndarray, only_missing: bool=True) -> torch.Tensor: LOGGER.info("Iteration | Train Loss") LOGGER.info("----------------------") bar = trange(self.max_iter) - for epoch in bar: + for iteration in bar: optimizer.zero_grad() - loss = self._model(X, non_zero_mask) + X_hat = self._model(X) + loss = ((X - X_hat)[mask] ** 2).mean() loss.backward() optimizer.step() - self._history.append((epoch, loss.item())) - - if not epoch % log_interval: - LOGGER.info("%9i | %10.4f", epoch, loss.item()) - - bar.set_postfix(loss=f'{loss:,.3f}') + self._history.append((iteration, loss.item())) + + if not iteration % log_interval: + LOGGER.info("%9i | %10.4f", iteration, loss.item()) + + bar.set_postfix(loss=f"{loss:,.3f}") if self.tol is not None: if loss < best_loss: best_loss = loss.item() @@ -219,12 +205,26 @@ def fit(self, X: np.ndarray, only_missing: bool=True) -> torch.Tensor: if early_stopping_counter >= self.n_iter_no_change: LOGGER.info("Stopping...") break - + LOGGER.info("DONE!") + return self + + def fit_transform(self, X: np.array | torch.Tensor) -> np.ndarray: + """Fit and impute missing retention times. + + Parameters + ---------- + X : torch.Tensor of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as np.nan. + + Returns + ------- + np.array of shape (n_peptides, n_runs) + The predicted retention time matrix. + """ + return self.fit(X).transform(X) + - return self.predict(only_missing, non_zero_mask, X) - - def to_tensor(array: np.ndarray | torch.Tensor) -> torch.Tensor: """Transform an array into a PyTorch Tensor, copying the data. @@ -241,4 +241,4 @@ def to_tensor(array: np.ndarray | torch.Tensor) -> torch.Tensor: if isinstance(array, torch.Tensor): return array.to("cpu").clone().detach() - return torch.tensor(X) \ No newline at end of file + return torch.tensor(array) diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index fd05903..1ce09fd 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -774,7 +774,7 @@ def hyperscore( indices_seqs_local = np.searchsorted(self.seq_ids, scores["id"].values) assert np.allclose(self.seq_ids[indices_seqs_local], scores["id"].values) - scores["Peptide"] = self.seqs[indices_seqs_local] + scores["peptide"] = self.seqs[indices_seqs_local] scores["decoy"] = [s not in self.target_proforma for s in scores["Peptide"]] return scores diff --git a/diadem/index/protein_index.py b/diadem/index/protein_index.py index cc31cd0..6999df7 100644 --- a/diadem/index/protein_index.py +++ b/diadem/index/protein_index.py @@ -84,7 +84,7 @@ def from_fasta( inv_alias = {} for i, entry in tqdm( - enumerate(FASTA(fasta_file)), + enumerate(FASTA(str(fasta_file))), disable=not progress, desc="Building peptide n-gram index", ): diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 9cdfc95..6ed8c7b 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -15,7 +15,7 @@ from diadem.config import DiademConfig from diadem.index.indexed_db import IndexedDb, db_from_fasta from diadem.mzml import ScanGroup, SpectrumStacker, StackedChromatograms -from diadem.search.search_utils import make_pin +from diadem.search.mokapot import brew_run def plot_to_log(*args, **kwargs) -> None: # noqa @@ -132,7 +132,7 @@ def search_group( scores = None if scores is not None: - scores["id"] = match_id + scores["peak_id"] = match_id ref_peak_mz = new_stack.mzs[new_stack.ref_index] mzs = itertools.chain( @@ -283,13 +283,12 @@ def diadem_main( logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) results.to_parquet(prefix + ".parquet", index=False, engine="pyarrow") - make_pin( + mokapot_results = brew_run( results, fasta_path=fasta_path, mzml_path=mzml_path, - pin_path=prefix + ".tsv.pin", ) - + mokapot_results.to_parquet(prefix + ".peptides.parquet") end_time = time.time() elapsed_time = end_time - start_time logger.info(f"Elapsed time: {elapsed_time}") diff --git a/diadem/search/mokapot.py b/diadem/search/mokapot.py new file mode 100644 index 0000000..07068e5 --- /dev/null +++ b/diadem/search/mokapot.py @@ -0,0 +1,155 @@ +"""Run-level mokapot analyses.""" +import re +from os import PathLike +from pathlib import Path +from typing import Iterable + +import mokapot +import pandas as pd + +from diadem.index.protein_index import ProteinNGram + + +def brew_run( + results: pd.DataFrame, + fasta_path: PathLike, + ms_data_path: PathLike, +) -> pd.DataFrame: + """Prepare the result DataFrame for mokapot. + + Parameters + ---------- + results: pd.DataFrame + The diadem search results. + fasta_path : PathLike + The FASTA file that was used for the search. + ms_data_path : PathLike + The mass spectrometry data file that was searched. + + Returns + ------- + pd.DataFrame + The run-level peptide scores and confidence estimates. + """ + input_df = (_prepare_df(results, fasta_path, ms_data_path),) + nonfeat = [ + "peptide", + "proteins", + "filename", + "target_pair", + "peak_id", + "is_target", + ] + peptides = mokapot.LinearPsmDataset( + psms=input_df, + target_column="is_target", + spectrum_columns="target_pair", + peptide_column="peptide", + protein_column="proteins", + feature_columns=[c for c in input_df.columns if c not in nonfeat], + filename_column="filename", + copy_data=False, + ) + results = mokapot.brew(peptides) + return results.peptides + + +def _prepare_df( + results: pd.DataFrame, + fasta_path: PathLike, + ms_data_path: PathLike, +) -> pd.DataFrame: + """Prepare the result DataFrame for mokapot. + + Parameters + ---------- + results: pd.DataFrame + The diadem search results. + fasta_path : PathLike + The FASTA file that was used for the search. + ms_data_path : PathLike + The mass spectrometry data file that was searched. + + Returns + ------- + pd.DataFrame + The input DataFrame for mokapot. + """ + # Keep only rank 1 peptides + results = results.loc[results["rank"] == 1, :] + + # Remove all list columns + non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] + results = results.loc[:, non_list_cols].drop(columns="rank") + + results["filename"] = Path(ms_data_path).stem + results["target_pair"] = results["peptide"] + results.loc[results["decoy"], "target_pair"] = results.loc[ + results["decoy"], "peptide" + ].apply(_decoy_to_target) + results["decoy"] = ~results["decoy"] + stripped_peptides = results["target_pair"].str.replace("\\[.*?\\]", "", regex=True) + results["peptide_length"] = stripped_peptides.str.len() + + # Add the pct-features + npeak_cols = [x for x in results.columns if "npeaks" in x] + for x in npeak_cols: + results[f"{x}_pct"] = 100 * results[x] / results["peptide_length"] + + # Get proteins, although not enirely necessary: + ## For decoys, this is the corresponding target protein. + results["proteins"] = stripped_peptides.apply( + _get_proteins, + ngram=ProteinNGram.from_fasta(fasta_path, progress=False), + ) + + # Rename columns to the expected values + expected_names = {"decoy": "is_target"} + results.rename(columns=expected_names, inplace=True) + return results + + +def _decoy_to_target(seq: str, permutation: Iterable[int] | None = None) -> str: + """Get the target sequence for a decoy peptide. + + Parameters + ---------- + seq : str + The decoy peptide sequence with Proforma-style modifications. + permutation : Sequence[int] | None + The permuation that was used to generate the decoy from the target sequence. + If ``None,`` it is assumed to be reversed between the termini. + + Returns + ------- + str + The target sequence that generated the decoy sequence. + """ + seq = re.split(r"(?=[A-Z])", seq)[1:] + if permutation is None: + inverted = list(range(len(seq))) + inverted[1:-1] = reversed(inverted[1:-1]) + else: + inverted = [None] * len(seq) + for decoy_idx, target_idx in enumerate(permutation): + inverted[target_idx] = decoy_idx + + return "".join([seq[i] for i in inverted]) + + +def _get_proteins(peptide: str, ngram: ProteinNGram) -> str: + """Get the protein(s) that may have generated a peptide. + + Parameters + ---------- + peptide : str + The stripped peptide sequence. + ngram : ProteinNGram + The n-gram object to look-up sequences. + + Returns + ------- + str + The protein or proteins delimited by semi-colons. + """ + return ";".join(ngram.search_ngram(peptide)) diff --git a/diadem/search/search_utils.py b/diadem/search/search_utils.py deleted file mode 100644 index 6fdd459..0000000 --- a/diadem/search/search_utils.py +++ /dev/null @@ -1,57 +0,0 @@ -from os import PathLike -from pathlib import Path - -from pandas import DataFrame -from pyteomics.proforma import parse - -from diadem.index.protein_index import ProteinNGram - - -def make_pin( - results: DataFrame, fasta_path: PathLike, mzml_path: PathLike, pin_path: PathLike -) -> None: - """Makes a '.pin' file from a dataframe of results. - - It writes the .pin file to disk (`pin_path` argument). - """ - # Postprocessing of the results dataframe for percolator - ## 1. keep only rank 1 peptides - results = results[results["rank"] == 1] - - ## Remove all list columns - non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] - results = results[non_list_cols] - - ## Add protein names - ngram = ProteinNGram.from_fasta(str(fasta_path)) - stripped_peptides = [ - "".join([x[0] for x in parse(y)[0]]) for y in results["Peptide"] - ] - proteins = [";".join(ngram.search_ngram(x)) for x in stripped_peptides] - results["Proteins"] = proteins - - ## Add the pct-features - results["PeptideLength"] = [len(x) for x in stripped_peptides] - npeak_cols = [x for x in results.columns if "npeaks" in x] - for x in npeak_cols: - results[f"{x}_pct"] = 100 * results[x] / results["PeptideLength"] - - ## Convert the decoys column to the right format - results["decoy"] = [-1 if d else 1 for d in results["decoy"]] - - ## Add a scan number column .... - # TODO add to the diadem logic to include the representative scan ID - results["ScanNr"] = list(range(len(results))) - results["Filename"] = Path(mzml_path).stem - - ## Rename columns to the expected values - ## Some columns in mokapot/percolator require a specific name - expected_names = { - "id": "SpecID", - "ScanNr": "ScanNr", - "Peptide": "Peptide", - "Proteins": "Proteins", - "decoy": "Label", - } - results.rename(columns=expected_names, inplace=True) - results.to_csv(pin_path, index=False, sep="\t") diff --git a/pyproject.toml b/pyproject.toml index b077b1e..f1fd7d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,8 @@ build-backend = "setuptools.build_meta" name = "diadem" authors = [ {name = "Sebastian Paez", email = "spaez@talus.bio"}, - {name = "William E. Fondrie", email = "wfrondie@talus.bio"}, {name = "Carollyn Allen", email = "callen@talus.bio"}, + {name = "William E. Fondrie", email = "wfrondie@talus.bio"}, ] description = "A modular, feature-centric toolkit for DIA proteomics" requires-python = ">=3.9,<=3.12" @@ -35,6 +35,7 @@ dependencies = [ "brain-isotopic-distribution >= 1.5.11", "ms-peak-picker >= 0.1.40", "ms-deisotope >= 0.0.46", + "mokapot >= 0.9.1", ] dynamic = ["version"] @@ -86,6 +87,8 @@ fix = true "*tests/*.py" = ["ANN"] # D104 is missing docstring in public package "**__init__.py" = ["D104"] +# Implements a sklearn interface with X and X_hat variables/params. +"diadem/aggregate/rt_model.py" = ["N803", "N806"] # ANN001 Missing type annotation for function argument # Ignoring in the cli since it is redundant with the click options diff --git a/tests/unit_tests/test_mokapot.py b/tests/unit_tests/test_mokapot.py new file mode 100644 index 0000000..a7e647c --- /dev/null +++ b/tests/unit_tests/test_mokapot.py @@ -0,0 +1,76 @@ +"""Unit tests for mokapot interactions.""" +import pandas as pd +import pytest + +from diadem.index.protein_index import ProteinNGram +from diadem.search.mokapot import _decoy_to_target, _get_proteins, _prepare_df + + +@pytest.fixture +def kid_fasta(tmp_path): + """A tiny fasta.""" + fasta = """ + > sp|KID1|KID1_HUMAN + LESLIEKAAAAAR + > sp|KID3|KID3_HUMAN + EDITHKAAAAAR + """ + + fasta_file = tmp_path / "test.fasta" + with fasta_file.open("w+") as fout: + fout.write(fasta) + + return fasta_file + + +def test_prepare_df(kid_fasta): + """Test that our dataframe is prepared correctly.""" + in_df = pd.DataFrame( + { + "rank": [1, 2, 1, 1, 1], + "peptide": ["EDITH", "EDITH", "LES[+79.9]LIE", "LILS[+79.9]EE", "AAAR"], + "list_col": [[1, 2]] * 5, + "cool_npeaks": [5, 5, 6, 6, 4], + "decoy": [False, False, False, True, False], + } + ) + + expected = pd.DataFrame( + { + "peptide": ["EDITH", "LES[+79.9]LIE", "LILS[+79.9]EE", "AAAR"], + "cool_npeaks": [5, 6, 6, 4], + "is_target": [True, True, False, True], + "filename": "test", + "target_pair": ["EDITH", "LES[+79.9]LIE", "LES[+79.9]LIE", "AAAR"], + "peptide_length": [5, 6, 6, 4], + "cool_npeaks_pct": [100.0, 100.0, 100.0, 100.0], + "proteins": ["KID3", "KID1", "KID1", "KID1;KID3"], + }, + index=[0, 2, 3, 4], + ) + + out_df = _prepare_df(in_df, kid_fasta, "test.mzML") + pd.testing.assert_frame_equal(out_df, expected) + + +def test_decoy_to_target(): + """Test that our decoy to target function works correctly.""" + target = "LES[+79.9]LIEK" + + # Test reversal + decoy = "LEILS[+79.9]EK" + assert target == _decoy_to_target(decoy) + + # Test another permutation: + perm = [2, 0, 1, 3, 4, 5, 6] + decoy = "S[+79.9]LELIEK" + + assert target == _decoy_to_target(decoy, perm) + + +def test_get_proteins(kid_fasta): + """Test that _get_proteins works corectly.""" + ngram = ProteinNGram.from_fasta(kid_fasta) + assert _get_proteins("LESLIE", ngram) == "KID1" + assert _get_proteins("EDITH", ngram) == "KID3" + assert _get_proteins("AAAR", ngram) == "KID1;KID3" From 54684f9c67422d009997e86bf3e21efe8249d617 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Wed, 26 Apr 2023 15:28:09 -0700 Subject: [PATCH 18/41] better top rank handling --- diadem/search/diadem.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 74d1385..c2ea949 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -165,7 +165,8 @@ def search_group( # noqa C901 `search_group` is too complex (18) precursor_mz=group.precursor_range, spec_int=scoring_intensities, spec_mz=new_stack.mzs, - top_n=100, + top_n=1, + # top_n=100, use 100 when you add precursor information ) # TODO: implement here a @@ -203,6 +204,8 @@ def search_group( # noqa C901 `search_group` is too complex (18) else: num_targets += 1 + scores = scores.sort_values(by="Score", ascending=False).iloc[:1] + del scores["rank"] match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) From 53ebe2de643bd73e6835554c98d4cd34674512bb Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Wed, 26 Apr 2023 16:33:29 -0700 Subject: [PATCH 19/41] remove search_utils.py --- diadem/aggregate/rt_model.py | 4 +- diadem/search/search_utils.py | 60 ------------------- tests/unit_tests/{ => search}/test_mokapot.py | 2 +- 3 files changed, 3 insertions(+), 63 deletions(-) delete mode 100644 diadem/search/search_utils.py rename tests/unit_tests/{ => search}/test_mokapot.py (99%) diff --git a/diadem/aggregate/rt_model.py b/diadem/aggregate/rt_model.py index 6ee5e7c..c11a9ae 100644 --- a/diadem/aggregate/rt_model.py +++ b/diadem/aggregate/rt_model.py @@ -25,7 +25,7 @@ class MatrixFactorizationModel(nn.Module): The number of runs. n_factors: int The number of latent factors. - rng : int | numpy.random.Generator + rng : int | numpy.random.Generator | None The random number generator. """ @@ -34,7 +34,7 @@ def __init__( n_peptides: int, n_runs: int, n_factors: int, - rng: int | np.random.Generator, + rng: int | np.random.Generator | None = None, ) -> None: """Initialize an ImputerModel.""" super().__init__() diff --git a/diadem/search/search_utils.py b/diadem/search/search_utils.py deleted file mode 100644 index feb4c8e..0000000 --- a/diadem/search/search_utils.py +++ /dev/null @@ -1,60 +0,0 @@ -from os import PathLike -from pathlib import Path - -from pandas import DataFrame -from pyteomics.proforma import parse - -from diadem.index.protein_index import ProteinNGram - - -def make_pin( - results: DataFrame, - fasta_path: PathLike, - mzml_path: PathLike, - pin_path: PathLike, -) -> None: - """Makes a '.pin' file from a dataframe of results. - - It writes the .pin file to disk (`pin_path` argument). - """ - # Postprocessing of the results dataframe for percolator - ## 1. keep only rank 1 peptides - results = results[results["rank"] == 1] - - ## Remove all list columns - non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] - results = results[non_list_cols] - - ## Add protein names - ngram = ProteinNGram.from_fasta(str(fasta_path)) - stripped_peptides = [ - "".join([x[0] for x in parse(y)[0]]) for y in results["Peptide"] - ] - proteins = [";".join(ngram.search_ngram(x)) for x in stripped_peptides] - results["Proteins"] = proteins - - ## Add the pct-features - results["PeptideLength"] = [len(x) for x in stripped_peptides] - npeak_cols = [x for x in results.columns if "npeaks" in x] - for x in npeak_cols: - results[f"{x}_pct"] = 100 * results[x] / results["PeptideLength"] - - ## Convert the decoys column to the right format - results["decoy"] = [-1 if d else 1 for d in results["decoy"]] - - ## Add a scan number column .... - # TODO add to the diadem logic to include the representative scan ID - results["ScanNr"] = list(range(len(results))) - results["Filename"] = Path(mzml_path).stem - - ## Rename columns to the expected values - ## Some columns in mokapot/percolator require a specific name - expected_names = { - "id": "SpecID", - "ScanNr": "ScanNr", - "Peptide": "Peptide", - "Proteins": "Proteins", - "decoy": "Label", - } - results.rename(columns=expected_names, inplace=True) - results.to_csv(pin_path, index=False, sep="\t") diff --git a/tests/unit_tests/test_mokapot.py b/tests/unit_tests/search/test_mokapot.py similarity index 99% rename from tests/unit_tests/test_mokapot.py rename to tests/unit_tests/search/test_mokapot.py index a7e647c..c7e4847 100644 --- a/tests/unit_tests/test_mokapot.py +++ b/tests/unit_tests/search/test_mokapot.py @@ -32,7 +32,7 @@ def test_prepare_df(kid_fasta): "list_col": [[1, 2]] * 5, "cool_npeaks": [5, 5, 6, 6, 4], "decoy": [False, False, False, True, False], - } + }, ) expected = pd.DataFrame( From 07537e1da58bdf3b186b3846d0accc5c05ab7d3d Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Wed, 26 Apr 2023 16:35:11 -0700 Subject: [PATCH 20/41] removed legacy make pin function --- diadem/search/dda.py | 19 +++++++---- diadem/search/search_utils.py | 60 ----------------------------------- 2 files changed, 12 insertions(+), 67 deletions(-) delete mode 100644 diadem/search/search_utils.py diff --git a/diadem/search/dda.py b/diadem/search/dda.py index 920f365..500a589 100644 --- a/diadem/search/dda.py +++ b/diadem/search/dda.py @@ -13,7 +13,7 @@ from diadem.config import DiademConfig from diadem.index.indexed_db import IndexedDb, db_from_fasta -from diadem.search.search_utils import make_pin +from diadem.search.mokapot import brew_run def score(db: IndexedDb, spec: Spectrum, mzml_stem: str) -> DataFrame | None: @@ -92,12 +92,17 @@ def out_hook(spec: Spectrum) -> Spectrum | None: logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) results.to_parquet(prefix + ".parquet", index=False) - make_pin( - results, - fasta_path=fasta_path, - mzml_path=mzml_path, - pin_path=prefix + ".tsv.pin", - ) + try: + # Right now I am bypassing the mokapot results, because they break a test + # meant to check that no decoys are detected (which is true in that case). + mokapot_results = brew_run( + results, + fasta_path=fasta_path, + ms_data_path=mzml_path, + ) + mokapot_results.to_parquet(prefix + ".peptides.parquet") + except ValueError as e: + logger.error(f"Could not run mokapot: {e}") end_time = time.time() elapsed_time = end_time - start_time diff --git a/diadem/search/search_utils.py b/diadem/search/search_utils.py deleted file mode 100644 index feb4c8e..0000000 --- a/diadem/search/search_utils.py +++ /dev/null @@ -1,60 +0,0 @@ -from os import PathLike -from pathlib import Path - -from pandas import DataFrame -from pyteomics.proforma import parse - -from diadem.index.protein_index import ProteinNGram - - -def make_pin( - results: DataFrame, - fasta_path: PathLike, - mzml_path: PathLike, - pin_path: PathLike, -) -> None: - """Makes a '.pin' file from a dataframe of results. - - It writes the .pin file to disk (`pin_path` argument). - """ - # Postprocessing of the results dataframe for percolator - ## 1. keep only rank 1 peptides - results = results[results["rank"] == 1] - - ## Remove all list columns - non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] - results = results[non_list_cols] - - ## Add protein names - ngram = ProteinNGram.from_fasta(str(fasta_path)) - stripped_peptides = [ - "".join([x[0] for x in parse(y)[0]]) for y in results["Peptide"] - ] - proteins = [";".join(ngram.search_ngram(x)) for x in stripped_peptides] - results["Proteins"] = proteins - - ## Add the pct-features - results["PeptideLength"] = [len(x) for x in stripped_peptides] - npeak_cols = [x for x in results.columns if "npeaks" in x] - for x in npeak_cols: - results[f"{x}_pct"] = 100 * results[x] / results["PeptideLength"] - - ## Convert the decoys column to the right format - results["decoy"] = [-1 if d else 1 for d in results["decoy"]] - - ## Add a scan number column .... - # TODO add to the diadem logic to include the representative scan ID - results["ScanNr"] = list(range(len(results))) - results["Filename"] = Path(mzml_path).stem - - ## Rename columns to the expected values - ## Some columns in mokapot/percolator require a specific name - expected_names = { - "id": "SpecID", - "ScanNr": "ScanNr", - "Peptide": "Peptide", - "Proteins": "Proteins", - "decoy": "Label", - } - results.rename(columns=expected_names, inplace=True) - results.to_csv(pin_path, index=False, sep="\t") From 20de9104d3a01e794d4589c7bc3675d733514348 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Wed, 26 Apr 2023 16:35:22 -0700 Subject: [PATCH 21/41] Add sklearn --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d608ac1..aeec562 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "hdf5plugin", "polars >= 0.16.9", "torch >= 2.0.0", + "scikit-learn >= 1.2.2" ] dynamic = ["version"] @@ -63,7 +64,7 @@ plot = [ ] dev = [ "ruff >= 0.0.253", - "black >= 23.1.0", + "black >= 23.1.0", "isort >= 5.12.0", "pylance >= 0.3.9", ] From 8807b12169b155847230893d519925bb62046e59 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Wed, 26 Apr 2023 16:38:31 -0700 Subject: [PATCH 22/41] Aggregate into bruker branch (#17) * initial rt model * Added run-level mokapot and updated rt * better top rank handling * remove search_utils.py * removed legacy make pin function * Add sklearn --------- Co-authored-by: Carolyn Allen Co-authored-by: William Fondrie --- diadem/aggregate/__init__.py | 0 diadem/aggregate/rt_model.py | 244 ++++++++++++++++++++++++ diadem/index/indexed_db.py | 8 +- diadem/index/protein_index.py | 2 +- diadem/search/dda.py | 19 +- diadem/search/diadem.py | 31 ++- diadem/search/mokapot.py | 156 +++++++++++++++ diadem/search/search_utils.py | 60 ------ diadem/utilities/utils.py | 3 +- pyproject.toml | 9 +- tests/test_database_build.py | 4 +- tests/test_database_parquet_cache.py | 4 +- tests/test_database_scoring.py | 4 +- tests/test_dia_search.py | 2 +- tests/unit_tests/search/test_mokapot.py | 76 ++++++++ 15 files changed, 531 insertions(+), 91 deletions(-) create mode 100644 diadem/aggregate/__init__.py create mode 100644 diadem/aggregate/rt_model.py create mode 100644 diadem/search/mokapot.py delete mode 100644 diadem/search/search_utils.py create mode 100644 tests/unit_tests/search/test_mokapot.py diff --git a/diadem/aggregate/__init__.py b/diadem/aggregate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diadem/aggregate/rt_model.py b/diadem/aggregate/rt_model.py new file mode 100644 index 0000000..c11a9ae --- /dev/null +++ b/diadem/aggregate/rt_model.py @@ -0,0 +1,244 @@ +"""The retention time matrix factorization model.""" +from __future__ import annotations + +import logging + +import numpy as np +import pandas as pd +import torch +import torch.nn as nn +from sklearn.base import BaseEstimator +from sklearn.exceptions import NotFittedError +from tqdm import trange + +LOGGER = logging.getLogger(__name__) + + +class MatrixFactorizationModel(nn.Module): + """The PyTorch matrix factorization model. + + Parameters + ---------- + n_peptides : int + The number of peptides. + n_runs : int + The number of runs. + n_factors: int + The number of latent factors. + rng : int | numpy.random.Generator | None + The random number generator. + """ + + def __init__( + self, + n_peptides: int, + n_runs: int, + n_factors: int, + rng: int | np.random.Generator | None = None, + ) -> None: + """Initialize an ImputerModel.""" + super().__init__() + self.n_peptides = n_peptides + self.n_runs = n_runs + self.n_factors = n_factors + self.rng = np.random.default_rng(rng) + + torch.manual_seed(self.rng.integers(1, 100000)) + + # The model: + self.peptide_factors = nn.Parameter(torch.randn(n_peptides, n_factors)) + self.run_factors = nn.Parameter(torch.randn(n_factors, n_runs)) + + def forward(self) -> torch.Tensor: + """Reconstruct the matrix. + + Returns + ------- + torch.Tensor of shape (n_peptide, n_runs) + The reconstructed matrix. + """ + return torch.mm(self.peptide_factors, self.run_factors) + + +class RTImputer(BaseEstimator): + """A retention time prediction model. + + RTImputer is a PyTorch model wrapped in a sklearn API. + + Parameters + ---------- + n_factors : int + The number of latent factors. + max_iter : int, optional + The maximum number of training iterations + tol : float, optional + The percent improvement over the previous loss required to + continue trianing. Used in conjuction with ``n_iter_no_change`` + to trigger early stopping. Set to ``None`` to disable. + n_iter_no_change : int, optional + The number of iterations to wait before triggering early + stopping. + lr: float, optional + The learning rate. + device : str or torch.Device, optional + A valid PyTorch device on which to perform the optimization. + silent : bool, optional + Disable logging. + rng : int | np.random.Generator | None, optional + The random number generator. + """ + + def __init__( + self, + n_factors: int, + max_iter: int = 1000, + tol: float = 1e-4, + n_iter_no_change: int = 20, + lr: float = 0.1, + device: str | torch.device = "cpu", + silent: bool = False, + rng: int | np.random.Generator | None = None, + ) -> None: + """Initialize the RTImputer.""" + # Parameters: + self.n_factors = n_factors + self.max_iter = max_iter + self.tol = tol + self.n_iter_no_change = n_iter_no_change + self.device = device + self.lr = lr + self.silent = silent + + # Set during fit: + self._model = None + self._history = None + self._shape = None + self._std = None + self._means = None + + @property + def model_(self) -> MatrixFactorizationModel: + """The underlying PyTorch model.""" + if self._model is None: + raise NotFittedError("This model has not been fit yet.") + + return self._model + + @property + def history_(self) -> pd.DataFrame: + """The training history.""" + return pd.DataFrame( + self._history, + columns=["iteration", "train_loss"], + ) + + def transform(self, X: np.array | torch.Tensor) -> np.array: # noqa: N803 + """Impute missing retention times. + + Parameters + ---------- + X : torch.Tensor of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as np.nan. + + Returns + ------- + np.array of shape (n_peptides, n_runs) + The predicted retention time matrix. + """ + # Prepare the input and initialize model + X = to_tensor(X) + mask = torch.isnan(X) + X_hat = self.model().to("cpu").detach() + X[mask] = X_hat[mask] + return X.numpy() + + def fit(self, X: np.ndarray | torch.Tensor) -> RTImputer: + """Fit the model. + + Parameters + ---------- + X : array of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as np.nan. + + Returns + ------- + self + """ + LOGGER.info("Training retention time predictor...") + + # Prepare the input and initialize model + X = to_tensor(X) + mask = ~torch.isnan(X) + X = X.to(self.device, torch.float32) + + self._shape = X.shape + self._model = MatrixFactorizationModel(*X.shape, self.n_factors).to(self.device) + self._history = [] + optimizer = torch.optim.RMSprop(self._model.parameters(), lr=self.lr) + log_interval = max(1, self.max_iter // 20) + LOGGER.info("Logging every %i iterations...", log_interval) + + # The main training loop: + best_loss = np.inf + early_stopping_counter = 0 + LOGGER.info("Iteration | Train Loss") + LOGGER.info("----------------------") + bar = trange(self.max_iter) + for iteration in bar: + optimizer.zero_grad() + X_hat = self._model(X) + loss = ((X - X_hat)[mask] ** 2).mean() + loss.backward() + optimizer.step() + self._history.append((iteration, loss.item())) + + if not iteration % log_interval: + LOGGER.info("%9i | %10.4f", iteration, loss.item()) + + bar.set_postfix(loss=f"{loss:,.3f}") + if self.tol is not None: + if loss < best_loss: + best_loss = loss.item() + early_stopping_counter = 0 + continue + early_stopping_counter += 1 + if early_stopping_counter >= self.n_iter_no_change: + LOGGER.info("Stopping...") + break + + LOGGER.info("DONE!") + return self + + def fit_transform(self, X: np.array | torch.Tensor) -> np.ndarray: + """Fit and impute missing retention times. + + Parameters + ---------- + X : torch.Tensor of shape (n_peptides, n_runs) + The retention time array. Missing peptides should be denoted as np.nan. + + Returns + ------- + np.array of shape (n_peptides, n_runs) + The predicted retention time matrix. + """ + return self.fit(X).transform(X) + + +def to_tensor(array: np.ndarray | torch.Tensor) -> torch.Tensor: + """Transform an array into a PyTorch Tensor, copying the data. + + Parameters + ---------- + array : numpy.ndarray or torch.Tensor + The array to transform + + Returns + ------- + torch.Tensor + The converted PyTorch tensor. + """ + if isinstance(array, torch.Tensor): + return array.to("cpu").clone().detach() + + return torch.tensor(array) diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 6e2f405..f675564 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -849,12 +849,12 @@ def hyperscore( indices_seqs_local = np.searchsorted(self.seq_ids, scores["id"].values) assert np.allclose(self.seq_ids[indices_seqs_local], scores["id"].values) - scores["Peptide"] = self.seqs[indices_seqs_local] + scores["peptide"] = self.seqs[indices_seqs_local] scores["PrecursorMZ"] = self.seq_prec_mzs[indices_seqs_local] - scores["decoy"] = [s not in self.target_proforma for s in scores["Peptide"]] + scores["decoy"] = [s not in self.target_proforma for s in scores["peptide"]] try: - assert len(np.unique(scores["Peptide"])) == len(scores), np.unique( - scores["Peptide"], + assert len(np.unique(scores["peptide"])) == len(scores), np.unique( + scores["peptide"], return_counts=True, ) except AssertionError: diff --git a/diadem/index/protein_index.py b/diadem/index/protein_index.py index d1cc6f3..285d445 100644 --- a/diadem/index/protein_index.py +++ b/diadem/index/protein_index.py @@ -86,7 +86,7 @@ def from_fasta( inv_alias = {} for i, entry in tqdm( - enumerate(FASTA(fasta_file)), + enumerate(FASTA(str(fasta_file))), disable=not progress, desc="Building peptide n-gram index", ): diff --git a/diadem/search/dda.py b/diadem/search/dda.py index 920f365..500a589 100644 --- a/diadem/search/dda.py +++ b/diadem/search/dda.py @@ -13,7 +13,7 @@ from diadem.config import DiademConfig from diadem.index.indexed_db import IndexedDb, db_from_fasta -from diadem.search.search_utils import make_pin +from diadem.search.mokapot import brew_run def score(db: IndexedDb, spec: Spectrum, mzml_stem: str) -> DataFrame | None: @@ -92,12 +92,17 @@ def out_hook(spec: Spectrum) -> Spectrum | None: logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) results.to_parquet(prefix + ".parquet", index=False) - make_pin( - results, - fasta_path=fasta_path, - mzml_path=mzml_path, - pin_path=prefix + ".tsv.pin", - ) + try: + # Right now I am bypassing the mokapot results, because they break a test + # meant to check that no decoys are detected (which is true in that case). + mokapot_results = brew_run( + results, + fasta_path=fasta_path, + ms_data_path=mzml_path, + ) + mokapot_results.to_parquet(prefix + ".peptides.parquet") + except ValueError as e: + logger.error(f"Could not run mokapot: {e}") end_time = time.time() elapsed_time = end_time - start_time diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 74d1385..72eace6 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -16,7 +16,7 @@ from diadem.data_io.mzml import ScanGroup, StackedChromatograms from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta -from diadem.search.search_utils import make_pin +from diadem.search.mokapot import brew_run from diadem.utilities.utils import plot_to_log @@ -165,7 +165,8 @@ def search_group( # noqa C901 `search_group` is too complex (18) precursor_mz=group.precursor_range, spec_int=scoring_intensities, spec_mz=new_stack.mzs, - top_n=100, + top_n=1, + # top_n=100, use 100 when you add precursor information ) # TODO: implement here a @@ -194,7 +195,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) scores = None if scores is not None: - scores["id"] = match_id + scores["peak_id"] = match_id scores["RetentionTime"] = group.retention_times[new_stack.ref_index] if hasattr(group, "imss"): scores["IonMobility"] = new_stack.ref_ims @@ -203,6 +204,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) else: num_targets += 1 + scores = scores.sort_values(by="Score", ascending=False).iloc[:1] match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) @@ -421,13 +423,24 @@ def setup_db_and_search( logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) + + # RTs are stored as f16, which need to be converted to f32 for parquet + f16_cols = list(results.select_dtypes("float16")) + if f16_cols: + for col in f16_cols: + results[col] = results[col].astype("float32") results.to_parquet(prefix + ".parquet", index=False, engine="pyarrow") - make_pin( - results, - fasta_path=fasta_path, - mzml_path=data_path, - pin_path=prefix + ".tsv.pin", - ) + try: + # Right now I am bypassing the mokapot results, because they break a test + # meant to check that no decoys are detected (which is true in that case). + mokapot_results = brew_run( + results, + fasta_path=fasta_path, + ms_data_path=data_path, + ) + mokapot_results.to_parquet(prefix + ".peptides.parquet") + except ValueError as e: + logger.error(f"Could not run mokapot: {e}") end_time = time.time() elapsed_time = end_time - start_time diff --git a/diadem/search/mokapot.py b/diadem/search/mokapot.py new file mode 100644 index 0000000..286ddff --- /dev/null +++ b/diadem/search/mokapot.py @@ -0,0 +1,156 @@ +"""Run-level mokapot analyses.""" +import re +from collections.abc import Iterable +from os import PathLike +from pathlib import Path + +import mokapot +import pandas as pd + +from diadem.index.protein_index import ProteinNGram + + +def brew_run( + results: pd.DataFrame, + fasta_path: PathLike, + ms_data_path: PathLike, +) -> pd.DataFrame: + """Prepare the result DataFrame for mokapot. + + Parameters + ---------- + results: pd.DataFrame + The diadem search results. + fasta_path : PathLike + The FASTA file that was used for the search. + ms_data_path : PathLike + The mass spectrometry data file that was searched. + + Returns + ------- + pd.DataFrame + The run-level peptide scores and confidence estimates. + """ + input_df = _prepare_df(results, fasta_path, ms_data_path) + nonfeat = [ + "peptide", + "proteins", + "filename", + "target_pair", + "peak_id", + "is_target", + ] + peptides = mokapot.LinearPsmDataset( + psms=input_df, + target_column="is_target", + spectrum_columns="target_pair", + peptide_column="peptide", + protein_column="proteins", + feature_columns=[c for c in input_df.columns if c not in nonfeat], + filename_column="filename", + copy_data=False, + ) + results = mokapot.brew(peptides) + return results.peptides + + +def _prepare_df( + results: pd.DataFrame, + fasta_path: PathLike, + ms_data_path: PathLike, +) -> pd.DataFrame: + """Prepare the result DataFrame for mokapot. + + Parameters + ---------- + results: pd.DataFrame + The diadem search results. + fasta_path : PathLike + The FASTA file that was used for the search. + ms_data_path : PathLike + The mass spectrometry data file that was searched. + + Returns + ------- + pd.DataFrame + The input DataFrame for mokapot. + """ + # Keep only rank 1 peptides + results = results.loc[results["rank"] == 1, :] + + # Remove all list columns + non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] + results = results.loc[:, non_list_cols].drop(columns="rank") + + results["filename"] = Path(ms_data_path).stem + results["target_pair"] = results["peptide"] + results.loc[results["decoy"], "target_pair"] = results.loc[ + results["decoy"], + "peptide", + ].apply(_decoy_to_target) + results["decoy"] = ~results["decoy"] + stripped_peptides = results["target_pair"].str.replace("\\[.*?\\]", "", regex=True) + results["peptide_length"] = stripped_peptides.str.len() + + # Add the pct-features + npeak_cols = [x for x in results.columns if "npeaks" in x] + for x in npeak_cols: + results[f"{x}_pct"] = 100 * results[x] / results["peptide_length"] + + # Get proteins, although not enirely necessary: + ## For decoys, this is the corresponding target protein. + results["proteins"] = stripped_peptides.apply( + _get_proteins, + ngram=ProteinNGram.from_fasta(fasta_path, progress=False), + ) + + # Rename columns to the expected values + expected_names = {"decoy": "is_target"} + results.rename(columns=expected_names, inplace=True) + return results + + +def _decoy_to_target(seq: str, permutation: Iterable[int] | None = None) -> str: + """Get the target sequence for a decoy peptide. + + Parameters + ---------- + seq : str + The decoy peptide sequence with Proforma-style modifications. + permutation : Sequence[int] | None + The permuation that was used to generate the decoy from the target sequence. + If ``None,`` it is assumed to be reversed between the termini. + + Returns + ------- + str + The target sequence that generated the decoy sequence. + """ + seq = re.split(r"(?=[A-Z])", seq)[1:] + if permutation is None: + inverted = list(range(len(seq))) + inverted[1:-1] = reversed(inverted[1:-1]) + else: + inverted = [None] * len(seq) + for decoy_idx, target_idx in enumerate(permutation): + inverted[target_idx] = decoy_idx + + return "".join([seq[i] for i in inverted]) + + +def _get_proteins(peptide: str, ngram: ProteinNGram) -> str: + """Get the protein(s) that may have generated a peptide. + + Parameters + ---------- + peptide : str + The stripped peptide sequence. + ngram : ProteinNGram + The n-gram object to look-up sequences. + + Returns + ------- + str + The protein or proteins delimited by semi-colons. + """ + return ";".join(ngram.search_ngram(peptide)) diff --git a/diadem/search/search_utils.py b/diadem/search/search_utils.py deleted file mode 100644 index feb4c8e..0000000 --- a/diadem/search/search_utils.py +++ /dev/null @@ -1,60 +0,0 @@ -from os import PathLike -from pathlib import Path - -from pandas import DataFrame -from pyteomics.proforma import parse - -from diadem.index.protein_index import ProteinNGram - - -def make_pin( - results: DataFrame, - fasta_path: PathLike, - mzml_path: PathLike, - pin_path: PathLike, -) -> None: - """Makes a '.pin' file from a dataframe of results. - - It writes the .pin file to disk (`pin_path` argument). - """ - # Postprocessing of the results dataframe for percolator - ## 1. keep only rank 1 peptides - results = results[results["rank"] == 1] - - ## Remove all list columns - non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] - results = results[non_list_cols] - - ## Add protein names - ngram = ProteinNGram.from_fasta(str(fasta_path)) - stripped_peptides = [ - "".join([x[0] for x in parse(y)[0]]) for y in results["Peptide"] - ] - proteins = [";".join(ngram.search_ngram(x)) for x in stripped_peptides] - results["Proteins"] = proteins - - ## Add the pct-features - results["PeptideLength"] = [len(x) for x in stripped_peptides] - npeak_cols = [x for x in results.columns if "npeaks" in x] - for x in npeak_cols: - results[f"{x}_pct"] = 100 * results[x] / results["PeptideLength"] - - ## Convert the decoys column to the right format - results["decoy"] = [-1 if d else 1 for d in results["decoy"]] - - ## Add a scan number column .... - # TODO add to the diadem logic to include the representative scan ID - results["ScanNr"] = list(range(len(results))) - results["Filename"] = Path(mzml_path).stem - - ## Rename columns to the expected values - ## Some columns in mokapot/percolator require a specific name - expected_names = { - "id": "SpecID", - "ScanNr": "ScanNr", - "Peptide": "Peptide", - "Proteins": "Proteins", - "decoy": "Label", - } - results.rename(columns=expected_names, inplace=True) - results.to_csv(pin_path, index=False, sep="\t") diff --git a/diadem/utilities/utils.py b/diadem/utilities/utils.py index a1dcb4c..ec7b8da 100644 --- a/diadem/utilities/utils.py +++ b/diadem/utilities/utils.py @@ -126,9 +126,10 @@ def get_slice_inds(arr: NDArray, minval: float, maxval: float) -> slice: # slice_max = np.searchsorted(arr[slice_min:], maxval, side="right") # slice_max = slice_min + slice_max i = 0 - for _i, val in enumerate(arr[slice_min:]): + for val in arr[slice_min:]: if val > maxval: break + i += 1 slice_max = slice_min + i return slice(slice_min, slice_max) diff --git a/pyproject.toml b/pyproject.toml index 687366b..aeec562 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,8 @@ build-backend = "setuptools.build_meta" name = "diadem" authors = [ {name = "Sebastian Paez", email = "spaez@talus.bio"}, - {name = "William E. Fondrie", email = "wfrondie@talus.bio"}, {name = "Carollyn Allen", email = "callen@talus.bio"}, + {name = "William E. Fondrie", email = "wfrondie@talus.bio"}, ] description = "A modular, feature-centric toolkit for DIA proteomics" requires-python = ">=3.9,<3.11" @@ -30,9 +30,12 @@ dependencies = [ "pyarrow >= 10.0.1", "platformdirs >= 2.6.0", "joblib >= 1.2.0", + "mokapot >= 0.9.1", "alphatims >= 1.0.6", "hdf5plugin", "polars >= 0.16.9", + "torch >= 2.0.0", + "scikit-learn >= 1.2.2" ] dynamic = ["version"] @@ -61,7 +64,7 @@ plot = [ ] dev = [ "ruff >= 0.0.253", - "black >= 23.1.0", + "black >= 23.1.0", "isort >= 5.12.0", "pylance >= 0.3.9", ] @@ -98,6 +101,8 @@ fix = true "*tests/*.py" = ["ANN"] # D104 is missing docstring in public package "**__init__.py" = ["D104"] +# Implements a sklearn interface with X and X_hat variables/params. +"diadem/aggregate/rt_model.py" = ["N803", "N806"] # ANN001 Missing type annotation for function argument # Ignoring in the cli since it is redundant with the click options diff --git a/tests/test_database_build.py b/tests/test_database_build.py index 22fed62..962c142 100644 --- a/tests/test_database_build.py +++ b/tests/test_database_build.py @@ -16,7 +16,7 @@ def test_peptide_scoring(sample_peaks, albumin_peptides): db.index_from_sequences() # breakpoint() scores = db.hyperscore(z2_mass, mzs, ints) - assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"]) + assert "VPQVSTPTLVEVSR/2" in set(scores["peptide"]) return db @@ -29,5 +29,5 @@ def test_database_from_fasta(shared_datadir, sample_peaks): mzs, ints, z2_mass = sample_peaks scores = db.hyperscore(z2_mass, mzs, ints) - assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"][np.invert(scores["decoy"])]) + assert "VPQVSTPTLVEVSR/2" in set(scores["peptide"][np.invert(scores["decoy"])]) return db diff --git a/tests/test_database_parquet_cache.py b/tests/test_database_parquet_cache.py index bd21f70..0c63795 100644 --- a/tests/test_database_parquet_cache.py +++ b/tests/test_database_parquet_cache.py @@ -27,7 +27,7 @@ def test_parquet_generation(shared_datadir, tmpdir, sample_peaks): db.index_from_parquet(tmpdir) mzs, ints, z2_mass = sample_peaks scores = db.hyperscore(z2_mass, mzs, ints) - assert "VPQVSTPTLVEVSR/2" in set(scores["Peptide"]) + assert "VPQVSTPTLVEVSR/2" in set(scores["peptide"]) - pep_scores = scores[scores["Peptide"] == "VPQVSTPTLVEVSR/2"] + pep_scores = scores[scores["peptide"] == "VPQVSTPTLVEVSR/2"] assert all(np.invert(pep_scores["decoy"])) diff --git a/tests/test_database_scoring.py b/tests/test_database_scoring.py index acc4c7c..5baca34 100644 --- a/tests/test_database_scoring.py +++ b/tests/test_database_scoring.py @@ -33,5 +33,5 @@ def test_fasta_shows_in_db(shared_datadir): score_df = db.hyperscore(ms1_range, spec_mz=mzs, spec_int=intens, top_n=2) score_df = score_df[np.invert(score_df["decoy"])] assert s.to_proforma() in list( - score_df["Peptide"], - ), f"Peptide i={i} {s.to_proforma()} not in db" + score_df["peptide"], + ), f"peptide i={i} {s.to_proforma()} not in db" diff --git a/tests/test_dia_search.py b/tests/test_dia_search.py index 8acf705..d7e83b9 100644 --- a/tests/test_dia_search.py +++ b/tests/test_dia_search.py @@ -23,7 +23,7 @@ def test_dia_search_works_mzml(tmpdir, shared_datadir, parallel): expected_csv = out + ".diadem.csv" df = pd.read_csv(expected_csv) df = df[np.invert(df["decoy"])] - peptides = {Peptide.from_sequence(x).stripped_sequence for x in df.Peptide.unique()} + peptides = {Peptide.from_sequence(x).stripped_sequence for x in df.peptide.unique()} theo_table = pd.read_csv( shared_datadir / "mzml/FGFR1_600_800_5min_peptide_table.tsv", diff --git a/tests/unit_tests/search/test_mokapot.py b/tests/unit_tests/search/test_mokapot.py new file mode 100644 index 0000000..c7e4847 --- /dev/null +++ b/tests/unit_tests/search/test_mokapot.py @@ -0,0 +1,76 @@ +"""Unit tests for mokapot interactions.""" +import pandas as pd +import pytest + +from diadem.index.protein_index import ProteinNGram +from diadem.search.mokapot import _decoy_to_target, _get_proteins, _prepare_df + + +@pytest.fixture +def kid_fasta(tmp_path): + """A tiny fasta.""" + fasta = """ + > sp|KID1|KID1_HUMAN + LESLIEKAAAAAR + > sp|KID3|KID3_HUMAN + EDITHKAAAAAR + """ + + fasta_file = tmp_path / "test.fasta" + with fasta_file.open("w+") as fout: + fout.write(fasta) + + return fasta_file + + +def test_prepare_df(kid_fasta): + """Test that our dataframe is prepared correctly.""" + in_df = pd.DataFrame( + { + "rank": [1, 2, 1, 1, 1], + "peptide": ["EDITH", "EDITH", "LES[+79.9]LIE", "LILS[+79.9]EE", "AAAR"], + "list_col": [[1, 2]] * 5, + "cool_npeaks": [5, 5, 6, 6, 4], + "decoy": [False, False, False, True, False], + }, + ) + + expected = pd.DataFrame( + { + "peptide": ["EDITH", "LES[+79.9]LIE", "LILS[+79.9]EE", "AAAR"], + "cool_npeaks": [5, 6, 6, 4], + "is_target": [True, True, False, True], + "filename": "test", + "target_pair": ["EDITH", "LES[+79.9]LIE", "LES[+79.9]LIE", "AAAR"], + "peptide_length": [5, 6, 6, 4], + "cool_npeaks_pct": [100.0, 100.0, 100.0, 100.0], + "proteins": ["KID3", "KID1", "KID1", "KID1;KID3"], + }, + index=[0, 2, 3, 4], + ) + + out_df = _prepare_df(in_df, kid_fasta, "test.mzML") + pd.testing.assert_frame_equal(out_df, expected) + + +def test_decoy_to_target(): + """Test that our decoy to target function works correctly.""" + target = "LES[+79.9]LIEK" + + # Test reversal + decoy = "LEILS[+79.9]EK" + assert target == _decoy_to_target(decoy) + + # Test another permutation: + perm = [2, 0, 1, 3, 4, 5, 6] + decoy = "S[+79.9]LELIEK" + + assert target == _decoy_to_target(decoy, perm) + + +def test_get_proteins(kid_fasta): + """Test that _get_proteins works corectly.""" + ngram = ProteinNGram.from_fasta(kid_fasta) + assert _get_proteins("LESLIE", ngram) == "KID1" + assert _get_proteins("EDITH", ngram) == "KID3" + assert _get_proteins("AAAR", ngram) == "KID1;KID3" From 552aefd5db02ee22d18154dc61d5c281ab2895ab Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Thu, 27 Apr 2023 13:30:27 -0700 Subject: [PATCH 23/41] Updated imputer models and added tests --- diadem/aggregate/{rt_model.py => imputers.py} | 151 ++++++++++++++---- pyproject.toml | 2 +- tests/unit_tests/aggregate/test_imputers.py | 51 ++++++ 3 files changed, 173 insertions(+), 31 deletions(-) rename diadem/aggregate/{rt_model.py => imputers.py} (57%) create mode 100644 tests/unit_tests/aggregate/test_imputers.py diff --git a/diadem/aggregate/rt_model.py b/diadem/aggregate/imputers.py similarity index 57% rename from diadem/aggregate/rt_model.py rename to diadem/aggregate/imputers.py index c11a9ae..6b7e573 100644 --- a/diadem/aggregate/rt_model.py +++ b/diadem/aggregate/imputers.py @@ -2,12 +2,12 @@ from __future__ import annotations import logging +from collections.abc import Iterable import numpy as np import pandas as pd import torch import torch.nn as nn -from sklearn.base import BaseEstimator from sklearn.exceptions import NotFittedError from tqdm import trange @@ -60,14 +60,14 @@ def forward(self) -> torch.Tensor: return torch.mm(self.peptide_factors, self.run_factors) -class RTImputer(BaseEstimator): - """A retention time prediction model. +class MFImputer: + """A matrix factorization imputation model. - RTImputer is a PyTorch model wrapped in a sklearn API. + The MFImputer is a PyTorch model wrapped in a sklearn API. Parameters ---------- - n_factors : int + n_factors : int | None, optional The number of latent factors. max_iter : int, optional The maximum number of training iterations @@ -82,22 +82,25 @@ class RTImputer(BaseEstimator): The learning rate. device : str or torch.Device, optional A valid PyTorch device on which to perform the optimization. - silent : bool, optional - Disable logging. rng : int | np.random.Generator | None, optional The random number generator. + task : str | None, optional + A sting specifying the task. This is only used for logging. + silent : bool, optional + Suppress logging messages. """ def __init__( self, - n_factors: int, - max_iter: int = 1000, + n_factors: int = None, + max_iter: int = 10000, tol: float = 1e-4, n_iter_no_change: int = 20, lr: float = 0.1, device: str | torch.device = "cpu", - silent: bool = False, rng: int | np.random.Generator | None = None, + task: str | None = None, + silent: bool = False, ) -> None: """Initialize the RTImputer.""" # Parameters: @@ -107,6 +110,8 @@ def __init__( self.n_iter_no_change = n_iter_no_change self.device = device self.lr = lr + self.rng = np.random.default_rng(rng) + self.task = task self.silent = silent # Set during fit: @@ -132,39 +137,68 @@ def history_(self) -> pd.DataFrame: columns=["iteration", "train_loss"], ) - def transform(self, X: np.array | torch.Tensor) -> np.array: # noqa: N803 + def _info(self, msg: str, *args: str | None) -> None: + """Log at the info level. + + Parameters + ---------- + msg : str + The message to be logged. + *args : str, optional + Values to be formatted into the message. + """ + if not self.silent: + LOGGER.info(msg, *args) + + def transform( + self, + X: np.array | torch.Tensor | None = None, + ) -> np.array: # noqa: N803 """Impute missing retention times. Parameters ---------- - X : torch.Tensor of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as np.nan. + X : array of shape (n_peptides, n_runs), optional + The value matrix. Missing peptides should be denoted as np.nan. + If ``none``, the full reconstruction will be returned. Returns ------- np.array of shape (n_peptides, n_runs) - The predicted retention time matrix. + The matrix with missing values imputed. """ + if X is None: + return self.model_().to("cpu").detach().numpy() + # Prepare the input and initialize model X = to_tensor(X) mask = torch.isnan(X) - X_hat = self.model().to("cpu").detach() + X_hat = self.model_().to("cpu").detach().type_as(X) X[mask] = X_hat[mask] return X.numpy() - def fit(self, X: np.ndarray | torch.Tensor) -> RTImputer: + def fit(self, X: np.ndarray | torch.Tensor) -> MFImputer: """Fit the model. Parameters ---------- X : array of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as np.nan. + The value matrix. Missing peptides should be denoted as np.nan. Returns ------- self """ - LOGGER.info("Training retention time predictor...") + if self.n_factors is None: + raise ValueError( + ( + "n_factors must be specified using search_factors() " + "to find the best value." + ), + ) + + if self.task: + self._info("Training %s model...", self.task) # Prepare the input and initialize model X = to_tensor(X) @@ -172,28 +206,30 @@ def fit(self, X: np.ndarray | torch.Tensor) -> RTImputer: X = X.to(self.device, torch.float32) self._shape = X.shape - self._model = MatrixFactorizationModel(*X.shape, self.n_factors).to(self.device) + self._model = MatrixFactorizationModel(*X.shape, self.n_factors, self.rng) + self._model.to(self.device) self._history = [] - optimizer = torch.optim.RMSprop(self._model.parameters(), lr=self.lr) + optimizer = torch.optim.Adam(self._model.parameters(), lr=self.lr) log_interval = max(1, self.max_iter // 20) - LOGGER.info("Logging every %i iterations...", log_interval) + self._info("Logging every %i iterations...", log_interval) # The main training loop: best_loss = np.inf early_stopping_counter = 0 - LOGGER.info("Iteration | Train Loss") - LOGGER.info("----------------------") - bar = trange(self.max_iter) + self._info("+-----------------------+") + self._info("| Iteration | Train MSE |") + self._info("+-----------------------+") + bar = trange(self.max_iter, disable=self.silent) for iteration in bar: optimizer.zero_grad() - X_hat = self._model(X) + X_hat = self._model() loss = ((X - X_hat)[mask] ** 2).mean() loss.backward() optimizer.step() self._history.append((iteration, loss.item())) if not iteration % log_interval: - LOGGER.info("%9i | %10.4f", iteration, loss.item()) + self._info("| %9i | %9.4f |", iteration, loss.item()) bar.set_postfix(loss=f"{loss:,.3f}") if self.tol is not None: @@ -203,10 +239,11 @@ def fit(self, X: np.ndarray | torch.Tensor) -> RTImputer: continue early_stopping_counter += 1 if early_stopping_counter >= self.n_iter_no_change: - LOGGER.info("Stopping...") break - LOGGER.info("DONE!") + self._info("+-----------------------+") + self._model.to("cpu") + self._info("DONE!") return self def fit_transform(self, X: np.array | torch.Tensor) -> np.ndarray: @@ -214,8 +251,8 @@ def fit_transform(self, X: np.array | torch.Tensor) -> np.ndarray: Parameters ---------- - X : torch.Tensor of shape (n_peptides, n_runs) - The retention time array. Missing peptides should be denoted as np.nan. + X : array of shape (n_peptides, n_runs) + The value matrix. Missing values should be denoted as np.nan. Returns ------- @@ -224,6 +261,60 @@ def fit_transform(self, X: np.array | torch.Tensor) -> np.ndarray: """ return self.fit(X).transform(X) + def search_factors( + self, + X: np.array | torch.Tensor, + n_factors: Iterable[int], + folds: int = 3, + ) -> MFImputer: + """Perform line search for the number of latent factors. + + Parameters + ---------- + X : array of shape (n_peptides, n_runs) + The value matrix. Missing values should be denoted as np.nan. + n_factors : Iterable[int] + The numbers of latent factors to try. + folds: int, optional + The number of cross-validation folds. + + Returns + ------- + self + """ + X = to_tensor(X) + prev_silent = self.silent + + # Create CV splits + indices = np.dstack(np.meshgrid(range(X.shape[0]), range(X.shape[1]))) + indices = indices.reshape(-1, 2) + + self.rng.shuffle(indices, axis=0) + splits = torch.split(torch.tensor(indices), indices.shape[0] // folds) + + # Do the line search + self._info("Searching for the best number of latent factors...") + self.silent = True + scores = [] + # This could be parallelized, but I don't think its worth it yet. + for num in n_factors: + self.n_factors = num + split_scores = [] + for split in splits: + split = tuple(split.T) + train = X.clone() + train[split] = np.nan + pred = to_tensor(self.fit_transform(train)) + split_scores.append(((X[split] - pred[split]) ** 2).mean()) + + scores.append(sum(split_scores).item()) + + self.n_factors = n_factors[np.argmin(scores)] + self.silent = prev_silent + self._model = None + self._info(" -> Chose %s factors", self.n_factors) + return self + def to_tensor(array: np.ndarray | torch.Tensor) -> torch.Tensor: """Transform an array into a PyTorch Tensor, copying the data. diff --git a/pyproject.toml b/pyproject.toml index aeec562..f0bf3f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ fix = true # D104 is missing docstring in public package "**__init__.py" = ["D104"] # Implements a sklearn interface with X and X_hat variables/params. -"diadem/aggregate/rt_model.py" = ["N803", "N806"] +"diadem/aggregate/imputers.py" = ["N803", "N806"] # ANN001 Missing type annotation for function argument # Ignoring in the cli since it is redundant with the click options diff --git a/tests/unit_tests/aggregate/test_imputers.py b/tests/unit_tests/aggregate/test_imputers.py new file mode 100644 index 0000000..e68e87d --- /dev/null +++ b/tests/unit_tests/aggregate/test_imputers.py @@ -0,0 +1,51 @@ +"""Test our RT alignment module.""" +import logging + +import numpy as np + +from diadem.aggregate.imputers import MatrixFactorizationModel, MFImputer + + +def test_model(): + """Test the base pytorch model.""" + model = MatrixFactorizationModel(n_peptides=10, n_runs=5, n_factors=3, rng=1) + assert model.peptide_factors.shape == (10, 3) + assert model.run_factors.shape == (3, 5) + assert model().shape == (10, 5) + + +def test_imputer(caplog): + """Test the imputer.""" + caplog.set_level(logging.INFO) + rng = np.random.default_rng(42) + model = MFImputer(n_factors=3, task="retention time", rng=1) + peptide_factors = rng.random((100, 3)) + run_factors = rng.random((3, 20)) + mat = peptide_factors @ run_factors + assert mat.shape == (100, 20) + + # Without NaNs: + pred = model.fit(mat).transform() + np.testing.assert_allclose(pred, mat, rtol=1e-5) + + # With NaNs: + mask = rng.binomial(1, 0.1, size=mat.shape).astype(bool) + missing = mat.copy() + missing[mask] = np.nan + pred = model.fit_transform(missing) + np.testing.assert_allclose(pred, mat, rtol=1e-5) + + +def test_search_factors(caplog): + """Test searching for the best number of factors.""" + caplog.set_level(logging.INFO) + rng = np.random.default_rng(42) + model = MFImputer(n_factors=None, task="retention time", rng=1) + peptide_factors = rng.random((101, 3)) + run_factors = rng.random((3, 20)) + mat = peptide_factors @ run_factors + assert mat.shape == (101, 20) + assert model.n_factors is None + + model = model.search_factors(mat, (2, 3, 4)) + assert model.n_factors == 3 From e2d40b402b77b741d6ca0cba2553581c560c70e6 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Fri, 28 Apr 2023 11:16:47 -0700 Subject: [PATCH 24/41] partial migration to polars and partial implementation of caching --- diadem/data_io/mzml.py | 114 +++++++++++++++++++++++------- diadem/data_io/timstof.py | 43 +++++++---- diadem/index/indexed_db.py | 4 +- diadem/search/diadem.py | 21 ++++-- diadem/search/mokapot.py | 2 +- profiling/lineprofile_timstof.zsh | 7 +- profiling/run_profile_tims.py | 4 +- 7 files changed, 142 insertions(+), 53 deletions(-) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 2b1d53b..4f045e9 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -7,13 +7,12 @@ from typing import Literal import numpy as np -import pandas as pd +import polars as pl from joblib import Parallel, delayed from loguru import logger from ms2ml import Spectrum from ms2ml.data.adapters import MZMLAdapter from numpy.typing import NDArray -from pandas import DataFrame from tqdm.auto import tqdm from diadem.config import DiademConfig, MassError @@ -42,7 +41,7 @@ class ScanGroup: precursor_mzs: list[NDArray] precursor_intensities: list[NDArray] - precursor_rts: NDArray + precursor_retention_times: NDArray def __post_init__(self) -> None: """Check that all the arrays have the same length.""" @@ -72,7 +71,48 @@ def __post_init__(self) -> None: title=f"Base peak chromatogram for the Group in {self.iso_window_name}", ) - def as_dataframe(self) -> pd.DataFrame: + def to_cache(self, dir: Path) -> None: + """Saves the group to a cache file.""" + Path(dir).mkdir(parents=True, exist_ok=True) + fragment_df = self.as_dataframe() + precursor_df = self.precursor_dataframe() + + stem = Path(self.iso_window_name) + precursor_df.write_parquet(dir / f"{stem}_precursors.parquet") + fragment_df.write_parquet(dir / f"{stem}_fragments.parquet") + + @classmethod + def from_cache(cls, dir: Path, name: str) -> ScanGroup: + """Loads a group from a cache file.""" + fragment_data = pl.read_parquet(dir / f"{name}_fragments.parquet").to_dict() + precursor_range = ( + fragment_data.pop("precursor_start")[0], + fragment_data.pop("precursor_end")[0], + ) + ind_max_int = [np.argmax(x) for x in fragment_data["intensities"]] + base_peak_mz = np.array( + [x[i] for x, i in zip(fragment_data["mzs"], ind_max_int)], + ) + base_peak_int = np.array( + [x[i] for x, i in zip(fragment_data["intensities"], ind_max_int)], + ) + + precursor_data = pl.read_parquet(dir / f"{name}_fragments.parquet").to_dict() + return cls( + precursor_range=precursor_range, + mzs=fragment_data["mzs"], + intensities=fragment_data["intensities"], + base_peak_mz=base_peak_mz, + base_peak_int=base_peak_int, + retention_times=fragment_data["retention_times"], + scan_ids=fragment_data["scan_ids"], + iso_window_name=name, + precursor_mzs=precursor_data["precursor_mzs"], + precursor_intensities=precursor_data["precursor_intensities"], + precursor_retention_times=precursor_data["precursor_retention_times"], + ) + + def as_dataframe(self) -> pl.DataFrame: """Returns a dataframe with the data in the group. The dataframe has the following columns: @@ -82,17 +122,37 @@ def as_dataframe(self) -> pd.DataFrame: - precursor_start: start of the precursor range - precursor_end: end of the precursor range """ - out = pd.DataFrame( + out = pl.DataFrame( { "mzs": self.mzs, "intensities": self.intensities, - "rts": self.retention_times, + "retention_times": self.retention_times, + "scan_ids": self.scan_ids, }, ) out["precursor_start"] = min(self.precursor_range) out["precursor_end"] = max(self.precursor_range) return out + def precursor_dataframe(self) -> pl.DataFrame: + """Returns a dataframe with the metadata for the group. + + The dataframe has the following columns: + - precursor_mzs: list of precursor mzs for each spectrum + - precursor_intensities: list of precursor intensities for each spectrum + - precursor_retention_times: precursor retention times for each spectrum + - precursor_start: start of the precursor range + - precursor_end: end of the precursor range + """ + out = pl.DataFrame( + { + "precursor_mzs": self.precursor_mzs, + "precursor_intensities": self.precursor_intensities, + "precursor_retention_times": self.precursor_retention_times, + }, + ) + return out + def get_highest_window( self, window: int, @@ -232,7 +292,7 @@ def get_precursor_evidence( each of which is the for the values integrated for the intensity values. """ - index = np.searchsorted(self.precursor_rts, rt) + index = np.searchsorted(self.precursor_retention_times, rt) slc, center_index = slice_from_center( index, window=11, @@ -517,23 +577,29 @@ def __init__(self, mzml_file: Path | str, config: DiademConfig) -> None: # TODO check if directly reading the xml is faster ... # also evaluate if that is needed - scaninfo = self.adapter.get_scan_info() - ms1_scaninfo = scaninfo[scaninfo.ms_level <= 1] + scaninfo = pl.DataFrame(self.adapter.get_scan_info()).fill_null(0.0) + ms1_scaninfo = scaninfo.filter(pl.col("ms_level") <= 1) + self.config = config if "DEBUG_DIADEM" in os.environ: logger.error("RUNNING DIADEM IN DEBUG MODE (only 700-710 mz iso windows)") - scaninfo = scaninfo[scaninfo.ms_level > 1] - scaninfo = scaninfo[ - [x[0] > 700 and x[0] < 705 for x in scaninfo.iso_window] - ] + scaninfo = scaninfo.filter(pl.col("ms_level") > 1) + window_filter = [x[0] > 700 and x[0] < 705 for x in scaninfo["iso_window"]] + scaninfo = scaninfo.filter(window_filter) + + ms1_scaninfo = ms1_scaninfo.drop("collision_energy").with_columns( + iso_window=0.0, + ) self.ms1info = ms1_scaninfo - self.ms2info = scaninfo[scaninfo.ms_level > 1].copy().reset_index() - self.unique_iso_windows = set(np.array(self.ms2info.iso_window)) + self.ms2info = scaninfo.filter(pl.col("ms_level") > 1) + self.unique_iso_windows = { + tuple(x) for x in self.ms2info["iso_window"].to_numpy() + } def _preprocess_scangroup( self, name: str, - df_chunk: DataFrame, + df_chunk: pl.DataFrame, mz_range: None | tuple[float, float] = None, progress: bool = True, ) -> tuple[NDArray, ...]: @@ -550,11 +616,11 @@ def _preprocess_scangroup( npeaks_deisotope = [] for row in tqdm( - df_chunk.itertuples(), + df_chunk.rows(named=True), desc=f"Preprocessing spectra for {name}", disable=not progress, ): - spec_id = row.spec_id + spec_id = row["spec_id"] curr_spec: Spectrum = self.adapter[spec_id] # NOTE instrument seems to have a wrong value ... # Also activation seems to not be recorded ... @@ -609,7 +675,7 @@ def _preprocess_scangroup( window_bp_mz = np.array(window_bp_mz).astype(np.float32) window_bp_int = np.array(window_bp_int).astype(np.float32) - window_rtinsecs = np.array(window_rtinsecs).astype(np.float16) + window_rtinsecs = np.array(window_rtinsecs).astype(np.float32) check_sorted(window_rtinsecs) window_scanids = np.array(window_scanids, dtype="object") @@ -638,8 +704,8 @@ def _get_iso_window_group( self, iso_window_name: str, iso_window: tuple[float, float], - ms2_chunk: DataFrame, - ms1_chunk: DataFrame, + ms2_chunk: pl.DataFrame, + ms1_chunk: pl.DataFrame, ) -> ScanGroup: """Gets all spectra that share the same window. @@ -696,7 +762,7 @@ def _get_iso_window_group( scan_ids=window_scanids, precursor_intensities=prec_ints, precursor_mzs=prec_mzs, - precursor_rts=prec_rtinsecs, + precursor_retention_times=prec_rtinsecs, ) return group @@ -705,7 +771,7 @@ def get_iso_window_groups( workerpool: None | Parallel = None, ) -> list[ScanGroup]: """Returns a list of all ScanGroups in an mzML file.""" - grouped = self.ms2info.sort_values("RTinSeconds").groupby("iso_window") + grouped = self.ms2info.sort("RTinSeconds").groupby("iso_window") iso_windows, chunks = zip(*list(grouped)) precursor_info = self.ms1info @@ -738,7 +804,7 @@ def get_iso_window_groups( def yield_iso_window_groups(self, progress: bool = False) -> Iterator[ScanGroup]: """Yield scan groups for each unique isolation window.""" - grouped = self.ms2info.sort_values("RTinSeconds").groupby("iso_window") + grouped = self.ms2info.sort("RTinSeconds").groupby("iso_window") precursor_info = self.ms1info for iso_window, chunk in tqdm( diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 7e11a97..75af5b5 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -9,7 +9,7 @@ from typing import Literal import numpy as np -import pandas as pd +import polars as pl from alphatims.bruker import TimsTOF from joblib import Parallel, delayed from loguru import logger @@ -294,7 +294,7 @@ def __post_init__(self) -> None: if len(self.imss) != len(self.mzs): raise ValueError("IMS values do not have the same lenth as the MZ values") - def as_dataframe(self) -> pd.DataFrame: + def as_dataframe(self) -> pl.DataFrame: """Returns a dataframe with the data in the group. The dataframe has the following columns: @@ -416,6 +416,7 @@ def lazy_datafile(self) -> TimsTOF: yield _datafile del _datafile + # @profile def _precursor_iso_window_groups( self, precursor_index: int, @@ -452,12 +453,13 @@ def _precursor_iso_window_groups( out[k] = TimsScanGroup( precursor_mzs=precursor_mzs, precursor_intensities=precursor_intensities, - precursor_rts=precursor_rts, + precursor_retention_times=precursor_rts, precursor_imss=precursor_imss, **v, ) return out + # @profile def _precursor_iso_window_elements( self, precursor_index: int, @@ -726,7 +728,7 @@ def _get_precursor_index_windows( # Since we want groups of peaks that share rt-quad values, regardless # of the IMS value. - df_peak_data = pd.DataFrame(contig_peak_data) + df_peak_data = pl.DataFrame(contig_peak_data) grouping_vals = [ "quad_low_mz_values", "quad_high_mz_values", @@ -736,23 +738,40 @@ def _get_precursor_index_windows( peak_data_vals = ["mz_values", "corrected_intensity_values", "mobility_values"] g_inds, g_vals = get_break_indices( - df_peak_data["quad_indices"].array, + df_peak_data["quad_indices"].to_numpy(zero_copy_only=True), min_diff=0, ) for si, ei in zip(g_inds[:-1], g_inds[1:]): - curr_peak_data = df_peak_data.iloc[si:ei] + curr_peak_data = df_peak_data[si:ei, :] + + # Sanity check to make sure all peaks in the selected chunk share + # the same retention times and quad isolation windows assert all( np.abs(np.max(curr_peak_data[x]) - np.min(curr_peak_data[x])) < 1e-3 for x in grouping_vals ) - current_chunk_data = {k: curr_peak_data[k].values[0] for k in grouping_vals} + current_chunk_data = {k: curr_peak_data[k][0] for k in grouping_vals} + quad_name = ( + f"{current_chunk_data['quad_low_mz_values']:.6f}," + f" {current_chunk_data['quad_high_mz_values']:.6f}" + ) + if ( + "DEBUG_DIADEM" in os.environ + and quad_splits + and quad_name not in quad_splits + ): + # This makes it so that when enabling the debug diadem + # mode will only use one quad iso window instead of all + # quad iso windows that share the same precursor incex. + continue current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] - peak_data = dict( - zip(peak_data_vals, curr_peak_data[peak_data_vals].T.values), - ) + peak_data = { + k: v.to_numpy(zero_copy_only=True) + for k, v in curr_peak_data[peak_data_vals].to_dict().items() + } start_len = len(peak_data["mz_values"]) @@ -783,10 +802,6 @@ def _get_precursor_index_windows( f_min = int(np.min(d_out["intensity"])) f_max = int(np.max(d_out["intensity"])) current_chunk_data.update(d_out) - quad_name = ( - f"{current_chunk_data['quad_low_mz_values']:.6f}," - f" {current_chunk_data['quad_high_mz_values']:.6f}" - ) quad_splits.setdefault(quad_name, []).append(current_chunk_data) postfix_dict = { diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index f675564..054c509 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -830,7 +830,9 @@ def hyperscore( return None scores["Score"] = ( - scores["log_factorial_peak_sum"] + scores["log_intensity_sums"] + scores["log_factorial_peak_sum"] + + scores["log_intensity_sums"] + # scores["log_intensity_sums"] ) # Calculate requirements for the z score among all other proposed scores! score_mean = scores["Score"].mean() diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 72eace6..596bc06 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -196,7 +196,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) if scores is not None: scores["peak_id"] = match_id - scores["RetentionTime"] = group.retention_times[new_stack.ref_index] + scores["RetentionTime"] = group.retention_times[new_stack.parent_index] if hasattr(group, "imss"): scores["IonMobility"] = new_stack.ref_ims if scores["decoy"].iloc[0]: @@ -384,6 +384,7 @@ def diadem_main( for group in ss.yield_iso_window_groups(progress=True): group_db = db.index_prefiltered_from_parquet(cache, *group.precursor_range) group_results = search_group(group=group, db=group_db, config=config) + group_results.to_parquet("latestresults.parquet") results.append(group_results) else: @@ -440,8 +441,16 @@ def setup_db_and_search( ) mokapot_results.to_parquet(prefix + ".peptides.parquet") except ValueError as e: - logger.error(f"Could not run mokapot: {e}") - - end_time = time.time() - elapsed_time = end_time - start_time - logger.info(f"Elapsed time: {elapsed_time}") + if "decoy PSMs were detected" in str(e): + logger.warning(f"Could not run mokapot: {e}") + else: + logger.error(f"Could not run mokapot: {e}") + raise e + except RuntimeError as e: + logger.warning(f"Could not run mokapot: {e}") + logger.error(results) + raise e + finally: + end_time = time.time() + elapsed_time = end_time - start_time + logger.info(f"Elapsed time: {elapsed_time}") diff --git a/diadem/search/mokapot.py b/diadem/search/mokapot.py index 286ddff..ea48b8e 100644 --- a/diadem/search/mokapot.py +++ b/diadem/search/mokapot.py @@ -50,7 +50,7 @@ def brew_run( filename_column="filename", copy_data=False, ) - results = mokapot.brew(peptides) + results, _models = mokapot.brew(peptides) return results.peptides diff --git a/profiling/lineprofile_timstof.zsh b/profiling/lineprofile_timstof.zsh index 239e85a..926459e 100644 --- a/profiling/lineprofile_timstof.zsh +++ b/profiling/lineprofile_timstof.zsh @@ -19,8 +19,5 @@ python -m line_profiler run_profile_tims.py.lprof > "line_profile_tims_latest.tx python -m pip install -e "../.[test,profiling,dev]" -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/raw_td_plot.png", plot = g)' -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_tims/iter_score_plot.png", plot = g)' - -mokapot lineprofile_results_tims/results.diadem.tsv.pin --test_fdr 0.05 --keep_decoys -d lineprofile_results_tims -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_tims/mokapot.peptides.txt") ; foo2 = readr::read_tsv("lineprofile_results_tims/mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/td_plot.png", plot = g)' +R -e 'library(tidyverse) ; foo = readr::read_csv("lineprofile_results_tims/results.diadem.csv") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(decoy))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/raw_td_plot.png", plot = g)' +R -e 'library(tidyverse) ; foo = readr::read_csv("lineprofile_results_tims/results.diadem.csv") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(decoy))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_tims/iter_score_plot.png", plot = g)' diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 1533848..896d7c6 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -4,7 +4,7 @@ cli.setup_logger() cli.diadem_main( fasta_path="./profiling_data/UP000005640_9606.fasta", - # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", + # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", # noqa: E501 data_path="./profiling_data/Hela_25ng_22min_6x3_short_S2-A4_1_118.hdf", config=DiademConfig( run_parallelism=1, # Needs to use 1 core for profiling @@ -17,7 +17,7 @@ run_min_correlation_score=0.5, run_min_intensity_ratio=0.01, peptide_mz_range=(400, 2000), - run_scalin_limits=(0, 0.8), + run_scalin_limits=(0.1, 0.99), ), out_prefix="lineprofile_results_tims/results", ) From d4ec9e6b5d0e7f5c118bc6aafcb63a85c3e5620e Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Fri, 28 Apr 2023 16:00:19 -0700 Subject: [PATCH 25/41] further additions to spectrum caching --- diadem/data_io/mzml.py | 56 ++++++++++++++++++++++++++++----------- diadem/data_io/timstof.py | 23 ++++++++++++++++ diadem/search/diadem.py | 3 ++- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 4f045e9..72ba0d8 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -71,20 +71,25 @@ def __post_init__(self) -> None: title=f"Base peak chromatogram for the Group in {self.iso_window_name}", ) + @property + def cache_file_stem(self) -> str: + stem = "".join(x if x.isalnum() else "_" for x in self.iso_window_name) + return stem + def to_cache(self, dir: Path) -> None: """Saves the group to a cache file.""" Path(dir).mkdir(parents=True, exist_ok=True) fragment_df = self.as_dataframe() precursor_df = self.precursor_dataframe() - stem = Path(self.iso_window_name) + stem = self.cache_file_stem + precursor_df.write_parquet(dir / f"{stem}_precursors.parquet") fragment_df.write_parquet(dir / f"{stem}_fragments.parquet") @classmethod - def from_cache(cls, dir: Path, name: str) -> ScanGroup: - """Loads a group from a cache file.""" - fragment_data = pl.read_parquet(dir / f"{name}_fragments.parquet").to_dict() + def _elems_from_fragment_cache(cls, file): + fragment_data = pl.read_parquet(file).to_dict() precursor_range = ( fragment_data.pop("precursor_start")[0], fragment_data.pop("precursor_end")[0], @@ -97,19 +102,40 @@ def from_cache(cls, dir: Path, name: str) -> ScanGroup: [x[i] for x, i in zip(fragment_data["intensities"], ind_max_int)], ) - precursor_data = pl.read_parquet(dir / f"{name}_fragments.parquet").to_dict() + out = { + "precursor_range": precursor_range, + "mzs": fragment_data["mzs"], + "intensities": fragment_data["intensities"], + "base_peak_mz": base_peak_mz, + "base_peak_int": base_peak_int, + "retention_times": fragment_data["retention_times"], + "scan_ids": fragment_data["scan_ids"], + } + + return out, fragment_data + + def _precursor_elems_from_cache(self, file): + precursor_data = pl.read_parquet(file).to_dict() + out = { + "precursor_mzs": precursor_data["precursor_mzs"], + "precursor_intensities": precursor_data["precursor_intensities"], + "precursor_retention_times": precursor_data["precursor_retention_times"], + } + return out, precursor_data + + @classmethod + def from_cache(cls, dir: Path, name: str) -> ScanGroup: + """Loads a group from a cache file.""" + fragment_elems, _fragment_data = cls._elems_from_fragment_cache( + dir / f"{name}_fragments.parquet", + ) + precursor_elems, _fragment_data = cls._precursor_elems_from_cache( + dir / f"{name}_precursors.parquet", + ) return cls( - precursor_range=precursor_range, - mzs=fragment_data["mzs"], - intensities=fragment_data["intensities"], - base_peak_mz=base_peak_mz, - base_peak_int=base_peak_int, - retention_times=fragment_data["retention_times"], - scan_ids=fragment_data["scan_ids"], iso_window_name=name, - precursor_mzs=precursor_data["precursor_mzs"], - precursor_intensities=precursor_data["precursor_intensities"], - precursor_retention_times=precursor_data["precursor_retention_times"], + **precursor_elems, + **fragment_elems, ) def as_dataframe(self) -> pl.DataFrame: diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 75af5b5..a45f261 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -294,6 +294,22 @@ def __post_init__(self) -> None: if len(self.imss) != len(self.mzs): raise ValueError("IMS values do not have the same lenth as the MZ values") + @classmethod + def _elems_from_fragment_cache(cls, file): + elems, data = super()._elems_from_fragment_cache(file) + elems["imss"] = data["imss"] + return elems, data + + @classmethod + def _precursor_elems_from_cache(cls, file): + elems, data = super()._precursor_elems_from_cache(file) + elems["imss"] = data["imss"] + return elems, data + + def to_cache(self, Path): + """Saves the group to a cache file.""" + super().to_cache(Path) + def as_dataframe(self) -> pl.DataFrame: """Returns a dataframe with the data in the group. @@ -309,6 +325,13 @@ def as_dataframe(self) -> pl.DataFrame: out["ims"] = self.imss return out + def precursor_dataframe(self) -> pl.DataFrame: + df = super().precursor_dataframe() + df = df.with_columns( + pl.Series(name="precursor_imss", values=self.precursor_imss), + ) + return df + def get_highest_window( self, window: int, diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 596bc06..89601ab 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -384,7 +384,8 @@ def diadem_main( for group in ss.yield_iso_window_groups(progress=True): group_db = db.index_prefiltered_from_parquet(cache, *group.precursor_range) group_results = search_group(group=group, db=group_db, config=config) - group_results.to_parquet("latestresults.parquet") + if group_results is not None: + group_results.to_parquet("latestresults.parquet") results.append(group_results) else: From dd253334f138a2f8deb6794718706fe40cd4dcdc Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Mon, 1 May 2023 13:36:04 -0700 Subject: [PATCH 26/41] Add decoys --- diadem/search/mokapot.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/diadem/search/mokapot.py b/diadem/search/mokapot.py index 286ddff..e85b44e 100644 --- a/diadem/search/mokapot.py +++ b/diadem/search/mokapot.py @@ -7,6 +7,7 @@ import mokapot import pandas as pd +from diadem.config import DiademConfig from diadem.index.protein_index import ProteinNGram @@ -14,6 +15,7 @@ def brew_run( results: pd.DataFrame, fasta_path: PathLike, ms_data_path: PathLike, + config: DiademConfig, ) -> pd.DataFrame: """Prepare the result DataFrame for mokapot. @@ -25,6 +27,8 @@ def brew_run( The FASTA file that was used for the search. ms_data_path : PathLike The mass spectrometry data file that was searched. + config : DiademConfig + The configuration setting. Returns ------- @@ -50,8 +54,14 @@ def brew_run( filename_column="filename", copy_data=False, ) - results = mokapot.brew(peptides) - return results.peptides + + mokapot.PercolatorModel(train_fdr=config.train_fdr) + results = mokapot.brew(peptides, test_fdr=config.eval_fdr) + targets = results.confidence_estimates["peptides"] + decoys = results.decoy_confidence_estiamtes["peptides"] + targets["is_target"] = True + decoys["is_target"] = False + return pd.concat([targets, decoys], axis=1) def _prepare_df( From 2d03ef8edf9fb6d92ffc28c1e463439f11a0e39e Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Mon, 1 May 2023 15:18:34 -0700 Subject: [PATCH 27/41] (wip,bugfix,feature) initial addition of dvc workflows for benchmarking and logging sinking for mokapot --- benchmarking_tests/test_deisotoping.py | 6 +-- diadem/config.py | 2 +- diadem/data_io/mzml.py | 9 ++-- diadem/data_io/timstof.py | 56 ++++++++++++----------- diadem/search/diadem.py | 34 ++++++++++++++ diadem/search/mokapot.py | 9 ++++ diadem/utilities/logging.py | 28 ++++++++++++ profiling/dvc.yaml | 31 +++++++++++++ profiling/plot_results.py | 54 ++++++++++++++++++++++ profiling/run_profile_multithread.zsh | 1 - profiling/run_profile_tims.py | 5 +- profiling/run_profile_tims_multithread.py | 24 ++++++++++ 12 files changed, 222 insertions(+), 37 deletions(-) create mode 100644 diadem/utilities/logging.py create mode 100644 profiling/dvc.yaml create mode 100644 profiling/plot_results.py create mode 100644 profiling/run_profile_tims_multithread.py diff --git a/benchmarking_tests/test_deisotoping.py b/benchmarking_tests/test_deisotoping.py index 92de094..d0422d2 100644 --- a/benchmarking_tests/test_deisotoping.py +++ b/benchmarking_tests/test_deisotoping.py @@ -23,13 +23,13 @@ 3000: array([0.617, 1.000, 0.884, 0.557, 0.277, 0.115, 0.041, 0.013, 0.004, 0.001]), 3200: array([0.578, 1.000, 0.936, 0.622, 0.327, 0.143, 0.054, 0.018, 0.005, 0.001]), 3400: array( - [0.543, 1.000, 0.990, 0.692, 0.381, 0.175, 0.069, 0.024, 0.008, 0.002, 0.001] + [0.543, 1.000, 0.990, 0.692, 0.381, 0.175, 0.069, 0.024, 0.008, 0.002, 0.001], ), 3600: array( - [0.493, 0.959, 1.000, 0.735, 0.424, 0.203, 0.084, 0.031, 0.010, 0.003, 0.001] + [0.493, 0.959, 1.000, 0.735, 0.424, 0.203, 0.084, 0.031, 0.010, 0.003, 0.001], ), 3800: array( - [0.444, 0.913, 1.000, 0.770, 0.464, 0.233, 0.101, 0.038, 0.013, 0.004, 0.001] + [0.444, 0.913, 1.000, 0.770, 0.464, 0.233, 0.101, 0.038, 0.013, 0.004, 0.001], ), } diff --git a/diadem/config.py b/diadem/config.py index a79094f..3516636 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -180,7 +180,7 @@ class DiademConfig(DiademIndexConfig): # noqa run_min_intensity_ratio: float = 0.01 run_min_correlation_score: float = 0.2 - run_scaling_ratio = 0.001 + run_scaling_ratio: float = 0.001 run_scalin_limits: tuple[float, float] = (0.001, 0.999) @property diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 72ba0d8..849676a 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -452,13 +452,14 @@ def ref_fwhm(self) -> int: above_hm = rt >= (rt.max() / 2) return above_hm.astype(int).sum() - def plot(self, plt) -> None: # noqa + def plot(self, plt, matches=None) -> None: # noqa """Plots the stacked chromatogram as lines.""" # TODO reconsider this implementation, maybe lazy import # of matplotlib. - plt.plot(self.array.T) - plt.plot(self.array[self.ref_index, ...].T, color="black") - plt.show() + plt.plot(self.array.T, color="gray", alpha=0.5, linewidth=0.5) + plt.plot(self.array[self.ref_index, ...].T, color="black", linewidth=2) + if matches is not None: + plt.plot(self.array[matches, ...].T, color="magenta") def trace_correlation(self) -> NDArray[np.float32]: """Calculate the correlation between the reference trace and all other traces. diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index a45f261..b551af5 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -771,7 +771,8 @@ def _get_precursor_index_windows( # Sanity check to make sure all peaks in the selected chunk share # the same retention times and quad isolation windows assert all( - np.abs(np.max(curr_peak_data[x]) - np.min(curr_peak_data[x])) < 1e-3 + np.abs(curr_peak_data[x].abs().max() - curr_peak_data[x].abs().max()) + < 1e-3 for x in grouping_vals ) current_chunk_data = {k: curr_peak_data[k][0] for k in grouping_vals} @@ -855,7 +856,7 @@ def _preprocess_ims( # TODO make all of these arguments # or make this callable class. PRELIM_N_PEAK_FILTER = 5_000 # noqa - FINAL_N_PEAK_FILTER = 1000 # noqa + FINAL_N_PEAK_FILTER = 1500 # noqa MIN_INTENSITY_KEEP = 500 # noqa MIN_NUM_SEEDS = 5_000 # noqa IMS_TOL = 0.01 # noqa @@ -948,30 +949,29 @@ def _preprocess_ims( & (ims > plot_ims_range[0]) & (ims < plot_ims_range[1]) ) - plot_ims_values = ims[top_plot_filter] - plot_mz_values = mz[top_plot_filter] - plot_intensity_values = intensity[top_plot_filter] - - plt.clf() - plt.scatter( - o_plot_mz_values, - o_plot_ims_values, - c="gray", - s=np.sqrt(o_plot_intensity_values), - ) - plt.scatter( - plot_mz_values, - plot_ims_values, - c=plot_intensity_values, - cmap="viridis", - s=np.sqrt(plot_intensity_values), - alpha=0.8, - ) - if np.sum(plot_intensity_values) > 1000: - if random.random() > 0.01: - plt.pause(0.1) - else: - plt.show() + if len(top_plot_filter) > 0: + plot_ims_values = ims[top_plot_filter] + plot_mz_values = mz[top_plot_filter] + plot_intensity_values = intensity[top_plot_filter] + + plt.clf() + plt.scatter( + o_plot_mz_values, + o_plot_ims_values, + c="gray", + s=np.sqrt(o_plot_intensity_values), + ) + plt.scatter( + plot_mz_values, + plot_ims_values, + c=plot_intensity_values, + cmap="viridis", + s=np.sqrt(plot_intensity_values), + alpha=0.8, + ) + if np.sum(plot_intensity_values) > 1000: + if random.random() > 0.01: + plt.pause(0.1) if len(intensity) > FINAL_N_PEAK_FILTER: partition_indices = np.argpartition( @@ -983,6 +983,10 @@ def _preprocess_ims( mz = mz[partition_indices] intensity = intensity[partition_indices] + # EXPERIMENTAL + # Does not seem to make it any better + # intensity = np.sqrt(intensity) + return mz, intensity, ims diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 89601ab..3db74a9 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -1,5 +1,7 @@ from __future__ import annotations +import logging +import os import time from os import PathLike from pathlib import Path @@ -17,8 +19,14 @@ from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta from diadem.search.mokapot import brew_run +from diadem.utilities.logging import InterceptHandler from diadem.utilities.utils import plot_to_log +logging.basicConfig(handlers=[InterceptHandler()], level=logging.INFO, force=True) + +if "PLOTDIADEM" in os.environ and os.environ["PLOTDIADEM"]: + import matplotlib.pyplot as plt # noqa: I001 + # @profile def search_group( # noqa C901 `search_group` is too complex (18) @@ -208,6 +216,30 @@ def search_group( # noqa C901 `search_group` is too complex (18) match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) + if "PLOTDIADEM" in os.environ and os.environ["PLOTDIADEM"]: + try: + ax1.cla() + ax2.cla() + except NameError: + fig, (ax1, ax2) = plt.subplots(1, 2) + + new_stack.plot(ax1, matches=match_indices) + ax2.plot(group.retention_times, group.base_peak_int) + ax2.vlines( + x=group.retention_times[new_stack.parent_index], + ymin=0, + ymax=new_stack.base_peak_intensity, + color="r", + ) + plt.title( + ( + f"Score: {scores['Score'].iloc[0]} \n" + f" Peptide: {scores['peptide'].iloc[0]} \n" + f"@ RT: {scores['RetentionTime'].iloc[0]}" + ), + ) + plt.pause(0.1) + scaling_window_indices = [ [x[y] for y in match_indices] for x in new_stack.stack_peak_indices ] @@ -435,11 +467,13 @@ def setup_db_and_search( try: # Right now I am bypassing the mokapot results, because they break a test # meant to check that no decoys are detected (which is true in that case). + logger.info("Running mokapot") mokapot_results = brew_run( results, fasta_path=fasta_path, ms_data_path=data_path, ) + logger.info(f"Writting mokapot results to {prefix}.peptides.parquet") mokapot_results.to_parquet(prefix + ".peptides.parquet") except ValueError as e: if "decoy PSMs were detected" in str(e): diff --git a/diadem/search/mokapot.py b/diadem/search/mokapot.py index ea48b8e..cf1649a 100644 --- a/diadem/search/mokapot.py +++ b/diadem/search/mokapot.py @@ -5,6 +5,7 @@ from pathlib import Path import mokapot +import numpy as np import pandas as pd from diadem.index.protein_index import ProteinNGram @@ -33,13 +34,18 @@ def brew_run( """ input_df = _prepare_df(results, fasta_path, ms_data_path) nonfeat = [ + "id", "peptide", "proteins", "filename", "target_pair", "peak_id", "is_target", + "PrecursorMZ", + "RetentionTime", ] + # Retention time could be used if we had a model that makes use of the + # combination of RT/MZ and IMS. Since an SVM doesn't, we'll drop it. peptides = mokapot.LinearPsmDataset( psms=input_df, target_column="is_target", @@ -80,6 +86,9 @@ def _prepare_df( # Remove all list columns non_list_cols = [c for c in results.columns if not isinstance(results[c][0], list)] + non_list_cols = [ + c for c in non_list_cols if not isinstance(results[c][0], np.ndarray) + ] results = results.loc[:, non_list_cols].drop(columns="rank") results["filename"] = Path(ms_data_path).stem diff --git a/diadem/utilities/logging.py b/diadem/utilities/logging.py new file mode 100644 index 0000000..050b178 --- /dev/null +++ b/diadem/utilities/logging.py @@ -0,0 +1,28 @@ +import logging +import sys +from logging import LogRecord + +from loguru import logger + + +class InterceptHandler(logging.Handler): + """Intercept a logging call and send it to Loguru.""" + + def emit(self, record: LogRecord) -> None: + """Intercept a logging call and send it to Loguru.""" + # Get corresponding Loguru level if it exists. + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + # Find caller from where originated the logged message. + frame, depth = sys._getframe(6), 6 + while frame and frame.f_code.co_filename == logging.__file__: + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log( + level, + record.getMessage(), + ) diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml new file mode 100644 index 0000000..28d3995 --- /dev/null +++ b/profiling/dvc.yaml @@ -0,0 +1,31 @@ + +stages: + get_data: + cmd: zsh get_data.zsh + deps: + - src/get_data.zsh + outs: + - profiling_data + + orbi_run: + deps: + - scripts/run.py + - params/orbi.toml + convert_tims_data: + cmd: docker run -it --rm -v ${PWD}/profiling_data/:/data alphatims_docker export hdf -t 3 /data/Hela_25ng_22min_9x4_short_S2-A8_1_122.d + + + tims_run: + - params/tims.toml + - scripts/run.py + + +plots: + - orbi_qvals.csv: + y: [n_peptides] + x: q_val + - orbi_td.png + - tims_qvals.csv: + y: [n_peptides] + x: q_val + - tims_td.png diff --git a/profiling/plot_results.py b/profiling/plot_results.py new file mode 100644 index 0000000..6aebc7e --- /dev/null +++ b/profiling/plot_results.py @@ -0,0 +1,54 @@ +import argparse +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import polars as pl + +parser = argparse.ArgumentParser() +parser.add_argument("base_dir", type=str) + + +def main(): + args = parser.parse_args() + base_dir = args.base_dir + + # Find the data + parquet_matches = list(Path(base_dir).glob("*.parquet")) + peptide_matches = [x for x in parquet_matches if "peptides" in x.name] + parquet_matches = [x for x in parquet_matches if "peptides" not in x.name] + + # Load the data + assert len(parquet_matches) == 1 + assert len(peptide_matches) == 1 + parquet_path = parquet_matches[0] + peptide_matches[0] + + # Plot the data + foo = pl.scan_parquet(parquet_path) + df = foo.select(pl.col(["Score", "decoy", "peptide"])).collect() + bins = np.histogram_bin_edges(df["Score"].to_numpy(), bins=100) + plt.hist(df.filter(pl.col("decoy"))["Score"], alpha=0.6, label="decoy", bins=bins) + plt.hist( + df.filter(pl.col("decoy").is_not())["Score"], + alpha=0.6, + label="target", + bins=bins, + ) + plt.legend() + plt.savefig(Path(base_dir) / "score_histogram_psm.png") + + df = df.groupby("peptide").max() + bins = np.histogram_bin_edges(df["Score"].to_numpy(), bins=100) + plt.hist(df.filter(pl.col("decoy"))["Score"], alpha=0.6, label="decoy", bins=bins) + plt.hist( + df.filter(pl.col("decoy").is_not())["Score"], + alpha=0.6, + label="target", + bins=bins, + ) + plt.legend() + plt.savefig(Path(base_dir) / "score_histogram_peptide.png") + + plt.yscale("log") + plt.savefig(Path(base_dir) / "log_score_histogram_peptide.png") diff --git a/profiling/run_profile_multithread.zsh b/profiling/run_profile_multithread.zsh index 60b6a58..3a072dd 100644 --- a/profiling/run_profile_multithread.zsh +++ b/profiling/run_profile_multithread.zsh @@ -3,7 +3,6 @@ python -m pip install ../. PYTHONOPTIMIZE=1 python run_profile_multithread.py mokapot lineprofile_results_multithread/results.diadem.tsv.pin --test_fdr 0.01 --keep_decoys -R -e 'library(tidyverse) ; foo = readr::read_tsv("mokapot.peptides.txt") ; foo2 = readr::read_tsv("mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_multithread/td_plot.png", plot = g)' R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_multithread/raw_td_plot.png", plot = g)' R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_multithread/iter_score_plot.png", plot = g)' diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py index 896d7c6..4a3ea18 100644 --- a/profiling/run_profile_tims.py +++ b/profiling/run_profile_tims.py @@ -14,10 +14,11 @@ g_tolerance_units=("da", "da"), g_ims_tolerance=0.02, g_ims_tolerance_unit="abs", - run_min_correlation_score=0.5, + run_min_correlation_score=0.2, run_min_intensity_ratio=0.01, peptide_mz_range=(400, 2000), - run_scalin_limits=(0.1, 0.99), + run_scaling_ratio=0.001, + run_scalin_limits=(0.001, 0.9), ), out_prefix="lineprofile_results_tims/results", ) diff --git a/profiling/run_profile_tims_multithread.py b/profiling/run_profile_tims_multithread.py new file mode 100644 index 0000000..cab0d95 --- /dev/null +++ b/profiling/run_profile_tims_multithread.py @@ -0,0 +1,24 @@ +from diadem import cli +from diadem.config import DiademConfig + +cli.setup_logger() +cli.diadem_main( + fasta_path="./profiling_data/UP000005640_9606.fasta", + # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", # noqa: E501 + data_path="./profiling_data/Hela_25ng_22min_6x3_short_S2-A4_1_118.hdf", + config=DiademConfig( + run_parallelism=4, # Needs to use 1 core for profiling + run_max_peaks=20000, + run_allowed_fails=5000, + g_tolerances=(0.02, 0.02), + g_tolerance_units=("da", "da"), + g_ims_tolerance=0.02, + g_ims_tolerance_unit="abs", + run_min_correlation_score=0.2, + run_min_intensity_ratio=0.01, + peptide_mz_range=(400, 2000), + run_scaling_ratio=0.001, + run_scalin_limits=(0.01, 0.99), + ), + out_prefix="lineprofile_results_tims_mt/results", +) From 797b14e89fb0f2f363f9d3dddfeabaf04f7c4e3b Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Wed, 3 May 2023 14:32:06 -0700 Subject: [PATCH 28/41] Drafted global FDR and alignment --- diadem/aggregate/__init__.py | 1 + diadem/aggregate/imputers.py | 2 +- diadem/aggregate/quants.py | 6 + diadem/aggregate/search.py | 201 ++++++++++++++++ diadem/config.py | 12 +- diadem/search/diadem.py | 296 +++++++----------------- tests/unit_tests/search/test_mokapot.py | 11 +- 7 files changed, 304 insertions(+), 225 deletions(-) create mode 100644 diadem/aggregate/quants.py create mode 100644 diadem/aggregate/search.py diff --git a/diadem/aggregate/__init__.py b/diadem/aggregate/__init__.py index e69de29..43b805b 100644 --- a/diadem/aggregate/__init__.py +++ b/diadem/aggregate/__init__.py @@ -0,0 +1 @@ +"""The aggregate module.""" diff --git a/diadem/aggregate/imputers.py b/diadem/aggregate/imputers.py index 6b7e573..562c66f 100644 --- a/diadem/aggregate/imputers.py +++ b/diadem/aggregate/imputers.py @@ -7,8 +7,8 @@ import numpy as np import pandas as pd import torch -import torch.nn as nn from sklearn.exceptions import NotFittedError +from torch import nn from tqdm import trange LOGGER = logging.getLogger(__name__) diff --git a/diadem/aggregate/quants.py b/diadem/aggregate/quants.py new file mode 100644 index 0000000..5ebdcff --- /dev/null +++ b/diadem/aggregate/quants.py @@ -0,0 +1,6 @@ +"""Aggregate quantification results.""" + + +def quants() -> None: + """Aggregate quantification results.""" + raise NotImplementedError("This hasn't been written yet...") diff --git a/diadem/aggregate/search.py b/diadem/aggregate/search.py new file mode 100644 index 0000000..f052a08 --- /dev/null +++ b/diadem/aggregate/search.py @@ -0,0 +1,201 @@ +"""Aggregate diadem search results.""" +from collections.abc import Iterable +from os import PathLike +from pathlib import Path + +import polars as pl +from mokapot import LinearPsmDataset + +from diadem.aggregate.imputers import MFImputer +from diadem.config import DiademConfig + + +def searches( + scores: Iterable[PathLike], + fasta_file: PathLike, + config: DiademConfig, +) -> tuple[Path]: + """Aggregate search results and align retention times. + + Parameters + ---------- + scores : Iterable[PathLike] + The run-level mokapot parquet files to read. + fasta_file : Pathlike + The FASTA file used for the database search. + config : DiademConfig + The configuration options. + + Returns + ------- + peptides : Path + The accepted peptides + proteins : Path + The accepted proteins + """ + agg = SearchAggregator(scores, fasta_file, config) + return agg.peptide_path, agg.protein_path + + +class SearchAggregator: + """Aggregate search results and align retention times. + + Parameters + ---------- + scores : Iterable[PathLike] + The run-level mokapot parquet files to read. + fasta_file : Pathlike + The FASTA file used for the database search. + config : DiademConfig + The configuration options. + """ + + def __init__( + self, + scores: Iterable[PathLike], + fasta_file: PathLike, + config: DiademConfig, + ) -> None: + """Initialize the search aggregator.""" + self.scores = scores + self.fasta_file = fasta_file + self.config = config + self.peptide_path = Path("diadem.search.peptides.parquet") + self.protein_path = Path("diadem.search.proteins.parquet") + + self._peptide = None + self._proteins = None + self._ret_time = None + self._ion_mobility = None + + if len(self.scores) < 2: + raise ValueError("At least two search results must be provided.") + + # 1. Compute global FDR + self.assign_confidence() + + # 2. Gather RT/IM for accepted peptides: + self.collect_imputer_data() + + # 3. Impute RT/IM for missing peptides in each run: + self.impute() + + # 4. Save the results. + self.save() + + def assign_confidence(self) -> None: + """Assign confidence across all runs.""" + keep_cols = ["peptide", "target_pair", "is_target", "mokapot score"] + score_df = pl.concat( + [pl.read_parquet(s, columns=keep_cols) for s in self.scores], + ) + + peptides = LinearPsmDataset( + psms=(score_df.with_columns(pl.lit("").alias("proteins")).to_pandas()), + target_column="is_target", + spectrum_columns="target_pair", + peptide_column="peptide", + protein_column="protein", + feature_columns="mokapot score", + copy_data=False, + ) + + peptides.add_proteins( + self.fasta_file, + enzyme=self.config.db_enzyme, + missed_cleavages=self.config.db_max_missed_cleavages, + min_length=self.config.peptide_length_range[0], + max_length=self.config.peptide_length_range[1], + ) + + # Global FDR: + results = peptides.assign_confidence( + "mokapot score", + eval_fdr=self.config.eval_fdr, + desc=True, + ) + + self._peptides = ( + pl.DataFrame(results.peptides) + .filter(pl.col("mokapot q-value") <= self.config.eval_fdr) + .drop("target_pair") + ) + + self._proteins = pl.DataFrame(results.proteins).filter( + pl.col("mokapot q-value") <= self.config.eval_fdr, + ) + + def collect_imputer_data(self) -> None: + """Filter run results for confident peptides.""" + keep_cols = ["peptide", "filename", "mokapot q-value"] + rt_df = [] + im_df = [] + for run_file in self.scores: + run_df = ( + pl.read_parquet(run_file, columns=keep_cols) + .filter(pl.col("mokapot q-value") <= self.config.eval_fdr) + .drop("mokapot q-value") + .merge(self._peptides, how="right") + ) + + fname = run_df["filename"][0] + + # Join with accepted peptides, maintaining order. + run_df = ( + self._peptides.lazy() + .join(run_df, how="left", on="peptide") + .drop( + [ + "mokapot q-value", + "mokapot PEP", + "mokapot score", + "filename", + "peptide", + ], + ) + .collect() + ) + + rt_df.append( + run_df.select(pl.col("RetentionTime").alias(f"retention_time_{fname}")), + ) + try: + im_df.append( + run_df.select(pl.col("IonMobility").alias(f"ion_mobility_{fname}")), + ) + except pl.exceptions.ColumnNotFoundError: + pass + + rt_df = pl.concat(rt_df, how="diagonal") + self._ret_time = rt_df + if im_df: + im_df = pl.concat(im_df, how="diagonal") + self._ion_mobility = im_df + + def impute(self) -> None: + """Imput missing retention times and ion mobility values.""" + rt_mat = ( + MFImputer(rng=self.config.seed, task="retention time") + .search_factors(self._ret_time.to_numpy(), [2, 4, 8, 16]) + .fit_transform(self._ret_time.to_numpy()) + ) + + self._ret_time = pl.DataFrame(rt_mat, schema=self._ret_time.columns) + + if self._ion_mobility is not None: + im_mat = ( + MFImputer(rng=self.config.seed, task="ion mobility") + .search_factors(self._ion_mobility.to_numpy(), [2, 4, 8, 16]) + .fit_transform(self._ion_mobility.to_numpy()) + ) + + self._ion_mobility = pl.DataFrame( + im_mat, + schema=self._ion_mobility.columns, + ) + + def save(self) -> tuple(Path): + """Save the aggregated results.""" + pep_dfs = [self._peptides, self._ret_time, self._ion_mobility] + pl.concat(pep_dfs, how="horizontally").write_parquet(self.peptide_path) + self._proteins.write_parquet(self.protein_path) diff --git a/diadem/config.py b/diadem/config.py index a79094f..3eb185f 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -53,7 +53,7 @@ class DiademIndexConfig: default=(250, 2000.0), ) - db_enzyme: str = field(default="trypsin") + db_enzyme: str = field(default="[KR]") # This needs to be a regex for mokapot. db_max_missed_cleavages: int = 2 db_bucket_size: int = 2**15 @@ -112,11 +112,11 @@ def from_args(cls, args: Namespace) -> DiademConfig: def hash(self) -> str: """Hashes the config in a reproducible manner. - Notes + Notes: ----- Python adds a seed to the hash, therefore the has will be different - Example + Example: ------- >>> DiademIndexConfig().hash() '1a23e68d04576bb73dbd5e0173679e64' @@ -180,9 +180,13 @@ class DiademConfig(DiademIndexConfig): # noqa run_min_intensity_ratio: float = 0.01 run_min_correlation_score: float = 0.2 - run_scaling_ratio = 0.001 + run_scaling_ratio: float = 0.001 run_scalin_limits: tuple[float, float] = (0.001, 0.999) + # Mokapot parameters + train_fdr: float = 0.01 + eval_fdr: float = 0.01 + @property def ms2ml_config(self) -> Config: """Returns the ms2ml config. diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 72eace6..880160f 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -1,28 +1,36 @@ from __future__ import annotations +import itertools import time -from os import PathLike from pathlib import Path import numpy as np import pandas as pd +import uniplot from joblib import Parallel, delayed from loguru import logger from pandas import DataFrame from tqdm.auto import tqdm from diadem.config import DiademConfig -from diadem.data_io import read_raw_data -from diadem.data_io.mzml import ScanGroup, StackedChromatograms -from diadem.data_io.timstof import TimsScanGroup, TimsStackedChromatograms from diadem.index.indexed_db import IndexedDb, db_from_fasta +from diadem.mzml import ScanGroup, SpectrumStacker, StackedChromatograms from diadem.search.mokapot import brew_run -from diadem.utilities.utils import plot_to_log + + +def plot_to_log(*args, **kwargs) -> None: # noqa + """Plot to log. + + Generates a plot of the passed data to the function. + All arguments are passed internally to uniplot.plot_to_string. + """ + for line in uniplot.plot_to_string(*args, **kwargs): + logger.debug(line) # @profile -def search_group( # noqa C901 `search_group` is too complex (18) - group: ScanGroup | TimsScanGroup, +def search_group( + group: ScanGroup, db: IndexedDb, config: DiademConfig, progress: bool = True, @@ -54,54 +62,19 @@ def search_group( # noqa C901 `search_group` is too complex (18) MS2_TOLERANCE = config.g_tolerances[1] # noqa MS2_TOLERANCE_UNIT = config.g_tolerance_units[1] # noqa - MS1_TOLERANCE = config.g_tolerances[0] # noqa - MS1_TOLERANCE_UNIT = config.g_tolerance_units[0] # noqa - - IMS_TOLERANCE = config.g_ims_tolerance # noqa - IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa - MAX_NUM_CONSECUTIVE_FAILS = 50 # noqa - - new_window_kwargs = { - "window": WINDOWSIZE, - "min_intensity_ratio": MIN_INTENSITY_RATIO, - "min_correlation": MIN_CORR_SCORE, - "mz_tolerance": MS2_TOLERANCE, - "mz_tolerance_unit": MS2_TOLERANCE_UNIT, - "max_peaks": WINDOW_MAX_PEAKS, - } - - if hasattr(group, "imss"): - logger.info("Detected a diaPASEF dataset") - new_window_kwargs.update( - { - "ims_tolerance": IMS_TOLERANCE, - "ims_tolerance_unit": IMS_TOLERANCE_UNIT, - }, - ) - stack_getter = TimsStackedChromatograms.from_group - - else: - logger.info("No IMS detected") - stack_getter = StackedChromatograms.from_group - # Results and stats related variables group_results = [] intensity_log = [] - score_log = [] index_log = [] fwhm_log = [] num_peaks = 0 - num_targets = 0 - num_decoys = 0 # Fail related variables num_fails = 0 - num_consecutive_fails = 0 curr_highest_peak_int = 2**30 last_id = None pbar = tqdm(desc=f"Slice: {group.iso_window_name}", disable=not progress) - st = time.time() while True: if not (curr_highest_peak_int >= MIN_PEAK_INTENSITY and num_peaks <= MAX_PEAKS): @@ -116,19 +89,14 @@ def search_group( # noqa C901 `search_group` is too complex (18) ) break - if num_consecutive_fails > MAX_NUM_CONSECUTIVE_FAILS: - logger.warning( - ( - "Exiting with early termination due " - f"to consecurtive fails {num_consecutive_fails}" - ), - ) - group_results = group_results[:-num_consecutive_fails] - break - - new_stack: StackedChromatograms | TimsStackedChromatograms - new_stack = group.get_highest_window(**new_window_kwargs) - + new_stack: StackedChromatograms = group.get_highest_window( + window=WINDOWSIZE, + min_intensity_ratio=MIN_INTENSITY_RATIO, + min_correlation=MIN_CORR_SCORE, + tolerance=MS2_TOLERANCE, + tolerance_unit=MS2_TOLERANCE_UNIT, + max_peaks=WINDOW_MAX_PEAKS, + ) if new_stack.base_peak_intensity < MIN_PEAK_INTENSITY: break @@ -166,54 +134,24 @@ def search_group( # noqa C901 `search_group` is too complex (18) spec_int=scoring_intensities, spec_mz=new_stack.mzs, top_n=1, - # top_n=100, use 100 when you add precursor information ) - - # TODO: implement here a - # partial ms2-score and then a follow up - # ms1 score - - # if scores is not None: - # rt = group.retention_times[new_stack.ref_index] - # prec_intensity, prec_dms = group.get_precursor_evidence( - # rt, - # mzs=scores["PrecursorMZ"].values, - # mz_tolerance=MS1_TOLERANCE, - # mz_tolerance_unit=MS1_TOLERANCE_UNIT, - # ) - # scores["PrecursorIntensity"] = prec_intensity - # scores.drop( - # scores[scores.PrecursorIntensity < 100].index, - # inplace = True) - # scores.reset_index(drop=True, inplace=True) - # scores["rank"] = scores["Score"].rank(ascending=False, method="min") - # if len(scores) == 0: - # logger.debug("All scores were removed due to precursor filtering") - # scores = None - else: scores = None if scores is not None: scores["peak_id"] = match_id - scores["RetentionTime"] = group.retention_times[new_stack.ref_index] - if hasattr(group, "imss"): - scores["IonMobility"] = new_stack.ref_ims - if scores["decoy"].iloc[0]: - num_decoys += 1 - else: - num_targets += 1 - - scores = scores.sort_values(by="Score", ascending=False).iloc[:1] - match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] - match_indices = np.sort(np.unique(np.array(match_indices))) - - scaling_window_indices = [ - [x[y] for y in match_indices] for x in new_stack.stack_peak_indices - ] + ref_peak_mz = new_stack.mzs[new_stack.ref_index] + + mzs = itertools.chain( + *[scores[x].iloc[0] for x in scores.columns if "_mzs" in x], + ) + best_match_mzs = np.sort( + np.array(tuple(itertools.chain(mzs, [ref_peak_mz]))), + ) # Scale based on the inverse of the reference chromatogram normalized_trace = new_stack.ref_trace / new_stack.ref_trace.max() + # scaling = SCALING_RATIO * (1-normalized_trace) scaling = 1 - normalized_trace # Since depending on the sampling rate the peak might be very assymetrical, # for now I am not mirroring the scaling @@ -224,105 +162,58 @@ def search_group( # noqa C901 `search_group` is too complex (18) group.scale_window_intensities( index=new_stack.parent_index, scaling=scaling, - window_indices=scaling_window_indices, + mzs=best_match_mzs, + window_indices=new_stack.stack_peak_indices, + window_mzs=new_stack.mzs, ) if (num_peaks % DEBUG_FREQUENCY) == 0: before = new_stack.ref_trace.copy() - try: - s = stack_getter( - group=group, - index=new_stack.parent_index, - **new_window_kwargs, - ) - plot_vals = [before, s.ref_trace] - title = ( + s = StackedChromatograms.from_group( + group=group, + index=new_stack.parent_index, + window=WINDOWSIZE, + tolerance=db.config.g_tolerances[1], + tolerance_unit=db.config.g_tolerance_units[1], + min_intensity_ratio=MIN_INTENSITY_RATIO, + min_correlation=MIN_CORR_SCORE, + max_peaks=MAX_PEAKS, + ) + plot_to_log( + [before, s.ref_trace], + title=( f"Window before ({new_stack.ref_mz}) " f"m/z and after ({s.ref_mz}) m/z" - ) - except ValueError: - # This happens when all peaks are removed - # ie: all in the spectrum matched a peptide - # and were removed - plot_vals = [before] - title = f"Window before ({new_stack.ref_mz}) m/z and after (None)" - - plot_to_log( - plot_vals, - title=title, + ), lines=True, ) plot_to_log([scaling], title="Scaling") group_results.append(scores.copy()) - score_log.append(scores["Score"].max()) - num_consecutive_fails = 0 else: - logger.debug(f"{match_id} did not match any peptides, scaling and skipping") - scaling = ( - SCALING_RATIO - * np.ones_like(new_stack.ref_trace) - * MIN_INTENSITY_SCALING - ) + logger.info(f"{match_id} did not match any peptides, scaling and skipping") + scaling = SCALING_RATIO * np.ones_like(new_stack.ref_trace) group.scale_window_intensities( index=new_stack.parent_index, scaling=scaling, + mzs=new_stack.mzs, window_indices=new_stack.stack_peak_indices, + window_mzs=new_stack.mzs, ) num_fails += 1 - num_consecutive_fails += 1 num_peaks += 1 - pbar.set_postfix( - { - "last_id": last_id, - "max_intensity": curr_highest_peak_int, - "num_fails": num_fails, - "num_scores": len(group_results), - "n_targets": num_targets, - "n_decoys": num_decoys, - }, - ) pbar.update(1) - - # TODO move this so it is disabled without the debug flag ... - if ((et := time.time()) - st) >= 2: - from ms2ml.utils.mz_utils import get_tolerance - - tot_candidates = 0 - for mz in new_stack.mzs: - ms2_tol = get_tolerance( - MS2_TOLERANCE, - theoretical=mz, - unit=MS2_TOLERANCE_UNIT, - ) - - candidates = db.bucketlist.yield_candidates( - ms1_range=group.precursor_range, - ms2_range=(mz - ms2_tol, mz + ms2_tol), - ) - for _ in candidates: - tot_candidates += 1 - - logger.error( - ( - f"Iteration took waaaay too long scores={scores} ;" - f" {tot_candidates} total candidates for precursor range " - f"{group.precursor_range} and m/z range " - f"and ms2 range {(mz - ms2_tol, mz + ms2_tol)}" - ), + if (num_peaks % DEBUG_FREQUENCY) == 0: + logger.debug( + f"peak {num_peaks}/{MAX_PEAKS} max ; Intensity {curr_highest_peak_int}", ) - logger.error(f"{new_stack.mzs.copy()}; len({len(new_stack.mzs)})") - st = et - else: - st = et pbar.close() plot_to_log( np.log1p(np.array(intensity_log)), title="Max (log) intensity over time", ) - plot_to_log(np.array(score_log), title="Score over time") plot_to_log(np.array(index_log), title="Requested index over time") plot_to_log(np.array(fwhm_log), title="FWHM across time") logger.info( @@ -333,17 +224,14 @@ def search_group( # noqa C901 `search_group` is too complex (18) f"on index {last_id}" ), ) - - if len(group_results) == 0: - logger.error("No results were accumulated in this group!") - group_results = pd.concat(group_results) if len(group_results) else None + group_results = pd.concat(group_results) return group_results # @profile def diadem_main( fasta_path: Path | str, - data_path: Path | str, + mzml_path: Path | str, config: DiademConfig, out_prefix: str = "", ) -> None: @@ -353,8 +241,8 @@ def diadem_main( ---------- fasta_path : Path | str Path to the fasta file - data_path : Path | str - Path to the mzml file or .d directory. + mzml_path : Path | str + Path to the mzml file config : DiademConfig Configuration object to use for the run. out_prefix : str, optional @@ -373,8 +261,8 @@ def diadem_main( ) # set up mzml file - ss = read_raw_data( - filepath=data_path, + ss = SpectrumStacker( + mzml_file=mzml_path, config=config, ) @@ -386,62 +274,34 @@ def diadem_main( group_results = search_group(group=group, db=group_db, config=config) results.append(group_results) else: - - @delayed - def setup_db_and_search( - precursor_range: tuple[float, float], - db: IndexedDb, - cache_location: PathLike, - config: DiademConfig, - group: ScanGroup, - ) -> DataFrame: - pfdb = db.index_prefiltered_from_parquet(cache_location, *precursor_range) - results = search_group(group=group, db=pfdb, config=config) - return results - with Parallel(n_jobs=config.run_parallelism) as workerpool: groups = ss.get_iso_window_groups(workerpool=workerpool) precursor_ranges = [group.precursor_range for group in groups] + dbs = workerpool( + delayed(db.index_prefiltered_from_parquet)(cache, *prange) + for prange in precursor_ranges + ) results = workerpool( - setup_db_and_search( - precursor_range=prange, - db=db, - cache_location=cache, - config=config, - group=group, - ) - for group, prange in zip(groups, precursor_ranges) + delayed(search_group)(group=group, db=pfdb, config=config) + for group, pfdb in zip(groups, dbs) ) - results: pd.DataFrame = pd.concat( - [x for x in results if x is not None], - ignore_index=True, - ) + results: pd.DataFrame = pd.concat(results, ignore_index=True) prefix = out_prefix + ".diadem" if out_prefix else "diadem" - Path(prefix).absolute().parent.mkdir(exist_ok=True) + prefix_dir = Path(prefix).absolute() + + prefix_dir.parent.mkdir(exist_ok=True) logger.info(f"Writting {prefix+'.csv'} and {prefix+'.parquet'}") results.to_csv(prefix + ".csv", index=False) - - # RTs are stored as f16, which need to be converted to f32 for parquet - f16_cols = list(results.select_dtypes("float16")) - if f16_cols: - for col in f16_cols: - results[col] = results[col].astype("float32") results.to_parquet(prefix + ".parquet", index=False, engine="pyarrow") - try: - # Right now I am bypassing the mokapot results, because they break a test - # meant to check that no decoys are detected (which is true in that case). - mokapot_results = brew_run( - results, - fasta_path=fasta_path, - ms_data_path=data_path, - ) - mokapot_results.to_parquet(prefix + ".peptides.parquet") - except ValueError as e: - logger.error(f"Could not run mokapot: {e}") - + mokapot_results = brew_run( + results, + fasta_path=fasta_path, + mzml_path=mzml_path, + ) + mokapot_results.to_parquet(prefix + ".peptides.parquet") end_time = time.time() elapsed_time = end_time - start_time logger.info(f"Elapsed time: {elapsed_time}") diff --git a/tests/unit_tests/search/test_mokapot.py b/tests/unit_tests/search/test_mokapot.py index c7e4847..1a6ae95 100644 --- a/tests/unit_tests/search/test_mokapot.py +++ b/tests/unit_tests/search/test_mokapot.py @@ -1,4 +1,5 @@ """Unit tests for mokapot interactions.""" +import numpy as np import pandas as pd import pytest @@ -28,8 +29,14 @@ def test_prepare_df(kid_fasta): in_df = pd.DataFrame( { "rank": [1, 2, 1, 1, 1], - "peptide": ["EDITH", "EDITH", "LES[+79.9]LIE", "LILS[+79.9]EE", "AAAR"], - "list_col": [[1, 2]] * 5, + "peptide": [ + "<[UNIMOD:4]@T>EDITH/2", + "<[UNIMOD:4]@T>EDITH/2", + "LES[+79.9]LIE/3", + "LILS[+79.9]EE/3", + "AAAR/2", + ], + "list_col": [np.array([1, 2])] * 5, "cool_npeaks": [5, 5, 6, 6, 4], "decoy": [False, False, False, True, False], }, From 2be68915a01fb1c24730421741e733843b66534b Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Wed, 3 May 2023 16:34:20 -0700 Subject: [PATCH 29/41] Update to work with dataframe inputs as well --- diadem/aggregate/search.py | 56 +++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/diadem/aggregate/search.py b/diadem/aggregate/search.py index f052a08..3c58607 100644 --- a/diadem/aggregate/search.py +++ b/diadem/aggregate/search.py @@ -11,8 +11,7 @@ def searches( - scores: Iterable[PathLike], - fasta_file: PathLike, + scores: Iterable[pl.DataFrame | pl.LazyFrame | PathLike], config: DiademConfig, ) -> tuple[Path]: """Aggregate search results and align retention times. @@ -33,7 +32,7 @@ def searches( proteins : Path The accepted proteins """ - agg = SearchAggregator(scores, fasta_file, config) + agg = SearchAggregator(scores, config) return agg.peptide_path, agg.protein_path @@ -53,17 +52,24 @@ class SearchAggregator: def __init__( self, scores: Iterable[PathLike], - fasta_file: PathLike, config: DiademConfig, ) -> None: """Initialize the search aggregator.""" self.scores = scores - self.fasta_file = fasta_file self.config = config - self.peptide_path = Path("diadem.search.peptides.parquet") - self.protein_path = Path("diadem.search.proteins.parquet") - self._peptide = None + base_path = Path(self.config.output_dir) / "aggregate" + self.peptide_path = base_path / "diadem.search.peptides.parquet" + self.protein_path = base_path / "diadem.search.proteins.parquet" + + if not self.config.overwrite: + base_msg = "%s already exists and overwrite is disabled." + if self.peptide_path.exists(): + raise RuntimeError(base_msg.format(str(self.peptide_path))) + if self.protein_path.exists(): + raise RuntimeError(base_msg.format(str(self.protein_path))) + + self._peptides = None self._proteins = None self._ret_time = None self._ion_mobility = None @@ -86,9 +92,18 @@ def __init__( def assign_confidence(self) -> None: """Assign confidence across all runs.""" keep_cols = ["peptide", "target_pair", "is_target", "mokapot score"] - score_df = pl.concat( - [pl.read_parquet(s, columns=keep_cols) for s in self.scores], - ) + try: + score_df = ( + pl.concat(self.scores, how="vertical") + .lazy() + .select(keep_cols) + .collect() + ) + except TypeError: + score_df = pl.concat( + [pl.read_parquet(s, columns=keep_cols) for s in self.scores], + how="vertical", + ) peptides = LinearPsmDataset( psms=(score_df.with_columns(pl.lit("").alias("proteins")).to_pandas()), @@ -101,7 +116,7 @@ def assign_confidence(self) -> None: ) peptides.add_proteins( - self.fasta_file, + self.config.fasta_file, enzyme=self.config.db_enzyme, missed_cleavages=self.config.db_max_missed_cleavages, min_length=self.config.peptide_length_range[0], @@ -130,13 +145,15 @@ def collect_imputer_data(self) -> None: keep_cols = ["peptide", "filename", "mokapot q-value"] rt_df = [] im_df = [] - for run_file in self.scores: - run_df = ( - pl.read_parquet(run_file, columns=keep_cols) - .filter(pl.col("mokapot q-value") <= self.config.eval_fdr) - .drop("mokapot q-value") - .merge(self._peptides, how="right") - ) + for run in self.scores: + try: + run_df = pl.read_parquet(run, columns=keep_cols).lazy() + except TypeError: + run_df = run.select(keep_cols).lazy() + + run_df = run_df.filter( + pl.col("mokapot q-value") <= self.config.eval_fdr, + ).drop("mokapot q-value") fname = run_df["filename"][0] @@ -196,6 +213,7 @@ def impute(self) -> None: def save(self) -> tuple(Path): """Save the aggregated results.""" + self.peptide_path.parent.mkdir(exist_ok=True) pep_dfs = [self._peptides, self._ret_time, self._ion_mobility] pl.concat(pep_dfs, how="horizontally").write_parquet(self.peptide_path) self._proteins.write_parquet(self.protein_path) From 8132ff369d8a3d95ccfec8dbedf75d0fe5b93973 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Mon, 8 May 2023 07:12:53 -0700 Subject: [PATCH 30/41] (wip) addition of fragment position information --- diadem/config.py | 4 +- diadem/index/indexed_db.py | 97 +++++++++++++++++++++-------- diadem/search/diadem.py | 4 ++ profiling/.dockerignore | 2 + profiling/.gitignore | 3 + profiling/Dockerfile | 7 +++ profiling/dvc.yaml | 20 +++--- profiling/get_data.bash | 21 ------- profiling/src/get_data.bash | 37 +++++++++++ profiling/{ => src}/plot_results.py | 0 profiling/src/run.py | 8 +++ tests/test_database_build.py | 1 - 12 files changed, 145 insertions(+), 59 deletions(-) create mode 100644 profiling/.dockerignore create mode 100644 profiling/Dockerfile delete mode 100644 profiling/get_data.bash create mode 100644 profiling/src/get_data.bash rename profiling/{ => src}/plot_results.py (100%) create mode 100644 profiling/src/run.py diff --git a/diadem/config.py b/diadem/config.py index 3516636..7f6bdb7 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -116,8 +116,8 @@ def hash(self) -> str: ----- Python adds a seed to the hash, therefore the has will be different - Example - ------- + Examples + -------- >>> DiademIndexConfig().hash() '1a23e68d04576bb73dbd5e0173679e64' >>> DiademIndexConfig(ion_series = "y").hash() diff --git a/diadem/index/indexed_db.py b/diadem/index/indexed_db.py index 054c509..4a8035f 100644 --- a/diadem/index/indexed_db.py +++ b/diadem/index/indexed_db.py @@ -64,7 +64,7 @@ def _make_score_dict(ions: str) -> dict[str, dict[str, float | list[float]]]: SeqProperties = namedtuple( "SeqProperties", - "fragments, ion_series, prec_mz, proforma_seq, num_frags", + "fragments, fragment_positions, ion_series, prec_mz, proforma_seq, num_frags", ) @@ -409,6 +409,7 @@ def index_from_parquet(self, dir: Path | str) -> None: frag_to_prec_ids=frags_df["seq_id"].values, prec_mzs=seqs_df["seq_mz"].values, prec_seqs=seqs_df["seq_proforma"].values, + frag_positions=frags_df["ion_position"].values, ) # @profile @@ -447,6 +448,7 @@ def _dump_peptides_parquet( frag_chunk = { "mz": [], "ion_series": [], + "ion_position": [], "seq_id": [], "precursor_mz": [], } @@ -454,23 +456,34 @@ def _dump_peptides_parquet( append = False if seq_file_path.exists(): append = True - for seq_id, (frag_mzs, ion_series, prec_mzs, prec_seqs, num_frags) in enumerate( + + my_iter = enumerate( (self.seq_properties(x) for x in iter_seqs), start=start_id, - ): + ) + for seq_id, ( + frag_mzs, + ion_positions, + ion_series, + prec_mzs, + prec_seqs, + num_frags, + ) in my_iter: seq_chunk["seq_id"].append(seq_id) seq_chunk["seq_mz"].append(prec_mzs) seq_chunk["seq_proforma"].append(prec_seqs) seq_chunk["decoy"].append(decoy) - for w, x, y, z in zip( + for w, x, x2, y, z in zip( [prec_mzs] * num_frags, frag_mzs, + ion_positions, ion_series, [seq_id] * num_frags, ): frag_chunk["precursor_mz"].append(float(w)) frag_chunk["mz"].append(float(x)) + frag_chunk["ion_position"].append(x2) frag_chunk["ion_series"].append(y) frag_chunk["seq_id"].append(z) @@ -528,7 +541,7 @@ def index_from_sequences(self) -> None: miniters=one_pct, ) - frag_mzs, frag_series, prec_mzs, prec_seqs, num_frags = zip( + frag_mzs, frag_position, frag_series, prec_mzs, prec_seqs, num_frags = zip( *(self.seq_properties(x) for x in iter_seqs), ) @@ -536,6 +549,7 @@ def index_from_sequences(self) -> None: prec_mzs = np.array(prec_mzs, dtype="float32") prec_seqs = np.array(prec_seqs, dtype="object") frag_mzs = np.concatenate(list(frag_mzs)).astype("float32") + frag_position = np.concatenate(list(frag_position)).astype("int8") frag_series = np.concatenate(list(frag_series)) seq_ids = np.empty_like(frag_mzs, dtype=int) @@ -556,12 +570,14 @@ def index_from_sequences(self) -> None: frag_to_prec_ids=seq_ids, prec_mzs=prec_mzs, prec_seqs=prec_seqs, + frag_positions=frag_position, ) def index_from_arrays( self, frag_mzs: NDArray[np.float32], frag_series: NDArray[np.str], + frag_positions: NDArray[np.int8], frag_to_prec_ids: NDArray[np.int64], prec_mzs: NDArray[np.float32], prec_seqs: NDArray[np.str], @@ -575,6 +591,9 @@ def index_from_arrays( An array of fragment m/z values. frag_series : NDArray[np.str] An array of fragment ion series. + frag_series : NDArray[np.int8] + An array of the positions of the fragment ions. + (for example 1 for b1, 2 for b2, 3 for b3, etc.) frag_to_prec_ids : NDArray[np.int64] An array of sequence ids. (unique identifier of a peptide sequence) prec_mzs : NDArray[np.float32] @@ -600,11 +619,15 @@ def index_from_arrays( " to be the same." ), ) - if not all(len(frag_mzs) == len(x) for x in [frag_series, frag_to_prec_ids]): + if not all( + len(frag_mzs) == len(x) + for x in [frag_series, frag_to_prec_ids, frag_positions] + ): raise ValueError( ( - "The length of the frag_mz, frag_series and frag_to_prec_ids need" - " to be the same" + f"The length of the frag_mz {len(frag_mzs)}, frag_series" + f" {len(frag_series)} and frag_to_prec_ids {len(frag_to_prec_ids)}," + f" frag_positions {len(frag_positions)} need to be the same" ), ) if not len(prec_seqs) == len(np.unique(prec_seqs)): @@ -616,27 +639,35 @@ def index_from_arrays( f"Sorting by ms2 mz. {frag_mzs.size} total fragments (if needed)", ) if not is_sorted(frag_mzs): - sorted_frags, sorted_frag_series, sorted_seq_ids = sort_all( - frag_mzs, - frag_series, - frag_to_prec_ids, + sorted_frags, sorted_frag_series, sorted_seq_ids, frag_positions = ( + sort_all( + frag_mzs, + frag_series, + frag_to_prec_ids, + frag_positions, + ) ) logger.debug("Done sorting (and GC), generating bucketlists") else: - logger.debug("Skippping sortinb because it is already sorted.") - sorted_frags, sorted_frag_series, sorted_seq_ids = ( + logger.debug("Skippping sorting because it is already sorted.") + sorted_frags, sorted_frag_series, sorted_seq_ids, frag_positions = ( frag_mzs, frag_series, frag_to_prec_ids, + frag_positions, ) del frag_mzs, frag_to_prec_ids, frag_series + # Temporary location for this, will be moved if it seems to give better results + # TODO + MIN_POSITION = 3 + self.bucketlist = FragmentBucketList.from_arrays( - fragment_mzs=sorted_frags, - fragment_series=sorted_frag_series, - precursor_ids=sorted_seq_ids, - precursor_mzs=prec_mzs[sorted_seq_ids], + fragment_mzs=sorted_frags[frag_positions >= MIN_POSITION], + fragment_series=sorted_frag_series[frag_positions >= MIN_POSITION], + precursor_ids=sorted_seq_ids[frag_positions >= MIN_POSITION], + precursor_mzs=prec_mzs[sorted_seq_ids[frag_positions >= MIN_POSITION]], chunksize=self.chunksize, sorting_level="ms2", been_sorted=True, @@ -652,22 +683,38 @@ def index_from_arrays( # @profile def seq_properties(self, x: Peptide) -> SeqProperties: """Internal method that extracts the peptide properties to build the index.""" - masses = { - k: np.concatenate( - [x.ion_series(ion_type=k, charge=c) for c in x.config.ion_charges], - ) - for k in x.config.ion_series - } + masses = {} + ion_positions = [] + for k in x.config.ion_series: + ions = [] + positions = [] + for c in x.config.ion_charges: + curr_ions = x.ion_series(ion_type=k, charge=c) + curr_pos = np.arange(len(curr_ions)) + 1 + ions.append(curr_ions) + positions.append(curr_pos) + + ion_positions.extend(positions) + masses[k] = np.concatenate(ions) ion_series = np.concatenate( [np.full_like(v, k, dtype=str) for k, v in masses.items()], ) masses = np.concatenate(list(masses.values())) + positions = np.concatenate(ion_positions) # TODO move this to the config ... mass_mask = (masses > 150) * (masses < 2000) masses = masses[mass_mask] ion_series = ion_series[mass_mask] - out = SeqProperties(masses, ion_series, x.mz, x.to_proforma(), len(masses)) + positions = positions[mass_mask] + out = SeqProperties( + masses, + positions, + ion_series, + x.mz, + x.to_proforma(), + len(masses), + ) return out # @profile diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 3db74a9..76a0e06 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -69,6 +69,8 @@ def search_group( # noqa C901 `search_group` is too complex (18) IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa MAX_NUM_CONSECUTIVE_FAILS = 50 # noqa + start_rts, start_bpc = group.retention_times.copy(), group.base_peak_int.copy() + new_window_kwargs = { "window": WINDOWSIZE, "min_intensity_ratio": MIN_INTENSITY_RATIO, @@ -224,6 +226,8 @@ def search_group( # noqa C901 `search_group` is too complex (18) fig, (ax1, ax2) = plt.subplots(1, 2) new_stack.plot(ax1, matches=match_indices) + + ax2.plot(start_rts, start_bpc, alpha=0.2, color="gray") ax2.plot(group.retention_times, group.base_peak_int) ax2.vlines( x=group.retention_times[new_stack.parent_index], diff --git a/profiling/.dockerignore b/profiling/.dockerignore new file mode 100644 index 0000000..235a3a8 --- /dev/null +++ b/profiling/.dockerignore @@ -0,0 +1,2 @@ + +* diff --git a/profiling/.gitignore b/profiling/.gitignore index 746ead5..1626de8 100644 --- a/profiling/.gitignore +++ b/profiling/.gitignore @@ -10,3 +10,6 @@ profile*.txt **mokapot*.txt *.png + +profiling_data_bkp +*/*.parquet diff --git a/profiling/Dockerfile b/profiling/Dockerfile new file mode 100644 index 0000000..cbe6b7f --- /dev/null +++ b/profiling/Dockerfile @@ -0,0 +1,7 @@ +# FROM python:3.9-slim +FROM --platform=linux/amd64 python:3.9-bullseye + +RUN apt-get update && apt-get install -y build-essential gcc python3-dev +RUN python3 -m pip install alphatims + +ENTRYPOINT [ "alphatims" ] diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index 28d3995..e9f9de5 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -1,24 +1,24 @@ stages: get_data: - cmd: zsh get_data.zsh + cmd: zsh src/get_data.zsh deps: - src/get_data.zsh + - Dockerfile outs: - - profiling_data + - profiling/profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + - profiling_data/UP000000625_83333_crap.fasta + - profiling_data/UP000005640_9606_crap.fasta orbi_run: deps: - - scripts/run.py - - params/orbi.toml - convert_tims_data: - cmd: docker run -it --rm -v ${PWD}/profiling_data/:/data alphatims_docker export hdf -t 3 /data/Hela_25ng_22min_9x4_short_S2-A8_1_122.d - + - src/run.py + - config/orbi.toml + cmd: python src/run.py -- tims_run: - - params/tims.toml - - scripts/run.py - + - src/run.py + - config/tims.toml plots: - orbi_qvals.csv: diff --git a/profiling/get_data.bash b/profiling/get_data.bash deleted file mode 100644 index 8f1f3cf..0000000 --- a/profiling/get_data.bash +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -mkdir -p profiling_data - -# TODO replace for brian's data -aws s3 cp --profile mfa s3://data-pipeline-mzml-bucket/221229_ChessFest_Plate3/Chessfest_Plate3_RH4_DMSO_DIA.mzML.gz ./profiling_data/. -aws s3 cp --profile mfa s3://data-pipeline-metadata-bucket/uniprot_human_sp_canonical_2021-11-19_crap.fasta ./profiling_data/. -curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_PASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_PASEF.d.zip -curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_diaPASEF.d.zip -curl https://ftp.pride.ebi.ac.uk/pride/data/archive/2021/07/PXD027359/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.d.zip --output ./profiling_data/hela_5min_diaPASEF.d.zip -# gunzip ./profiling_data/*.gz -for i in ./profiling_data/*.zip ; do unzip $i ; done -mv LFQ_timsTOFPro_* ./profiling_data/. -mv 20210510_TIMS03_EVO03* ./profiling_data/. - -alphatims export hdf profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.d - -curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Bacteria/UP000000625/UP000000625_83333.fasta.gz --output ./profiling_data/UP000000625_83333.fasta.gz -gunzip ./profiling_data/UP000000625_83333.fasta.gz -curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Eukaryota/UP000005640/UP000005640_9606.fasta.gz --output ./profiling_data/UP000005640_9606.fasta.gz -gunzip ./profiling_data/UP000005640_9606.fasta.gz diff --git a/profiling/src/get_data.bash b/profiling/src/get_data.bash new file mode 100644 index 0000000..2519d3e --- /dev/null +++ b/profiling/src/get_data.bash @@ -0,0 +1,37 @@ +#!/bin/bash + +mkdir -p profiling_data + +## Fasta Files +# Ecoli +curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Bacteria/UP000000625/UP000000625_83333.fasta.gz --output ./profiling_data/UP000000625_83333.fasta.gz +gunzip ./profiling_data/UP000000625_83333.fasta.gz + +# Human +curl https://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Eukaryota/UP000005640/UP000005640_9606.fasta.gz --output ./profiling_data/UP000005640_9606.fasta.gz +gunzip ./profiling_data/UP000005640_9606.fasta.gz +aws s3 cp --profile mfa s3://data-pipeline-metadata-bucket/contaminants.fasta ./profiling_data/. + +cat ./profiling_data/contaminants.fasta ./profiling_data/UP000000625_83333.fasta >> ./profiling_data/UP000000625_83333_crap.fasta +cat ./profiling_data/contaminants.fasta ./profiling_data/UP000005640_9606.fasta >> ./profiling_data/UP000005640_9606_crap.fasta + +# ./profiling_data/UP000000625_83333_crap.fasta +# ./profiling_data/UP000005640_9606_crap.fasta + +# Raw Data +# Ecoli +# TimsTof +curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d.zip --output ./profiling_data/ecoli_timsTOFPro_diaPASEF.d.zip + +# Human +# Orbi +aws s3 cp --profile mfa s3://tmp-jspp-diadem-assets/220119_hela_44m_1.mzML.gz + +# TimsTof + + +for i in ./profiling_data/*.zip ; do unzip $i -d profiling_data ; done + +# This is done in docker ... still waiting for the mann lab to check my PR +docker build -t alphatims_docker . +docker run -rm -it -v ${PWD}/profiling_data/:/data/ alphatims export hdf LFQ_timsTOFPro_diaPASEF_Ecoli_01 diff --git a/profiling/plot_results.py b/profiling/src/plot_results.py similarity index 100% rename from profiling/plot_results.py rename to profiling/src/plot_results.py diff --git a/profiling/src/run.py b/profiling/src/run.py new file mode 100644 index 0000000..324517d --- /dev/null +++ b/profiling/src/run.py @@ -0,0 +1,8 @@ +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--config", type=str, help="Path to the config file") +parser.add_argument("--fasta", type=str, help="Path to the FASTA file") +parser.add_argument("--ms_data", type=str, help="Path to the MS data file") +parser.add_argument("--output", type=str, help="Path to the output file path") +parser.add_argument("--threads", type=int, help="Number of threads to use") diff --git a/tests/test_database_build.py b/tests/test_database_build.py index 962c142..e8c522c 100644 --- a/tests/test_database_build.py +++ b/tests/test_database_build.py @@ -14,7 +14,6 @@ def test_peptide_scoring(sample_peaks, albumin_peptides): db = IndexedDb(config=diadem_config, chunksize=64) db.targets = albumin_peptides db.index_from_sequences() - # breakpoint() scores = db.hyperscore(z2_mass, mzs, ints) assert "VPQVSTPTLVEVSR/2" in set(scores["peptide"]) return db From 43bcc054d3ec6af95d318e964977cecfc4f112f1 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Mon, 8 May 2023 15:35:52 -0700 Subject: [PATCH 31/41] Added base interface --- diadem/interfaces.py | 151 ++++++++++++++++++++++++++++++++++ tests/unit_tests/interface.py | 66 +++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 diadem/interfaces.py create mode 100644 tests/unit_tests/interface.py diff --git a/diadem/interfaces.py b/diadem/interfaces.py new file mode 100644 index 0000000..d2be571 --- /dev/null +++ b/diadem/interfaces.py @@ -0,0 +1,151 @@ +"""Interfaces for results from Diadem modules. + +This module provides a base class for defining interfaces between +Diadem modules. Each child class should provides access to dataframes +representing results from a Diadem function/method, optionally backed +by a parquet file. The child class can then be used by other Diadem +modules to perform the next step of an algorithm. +""" +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections.abc import Generator, Iterable +from dataclasses import dataclass +from os import PathLike + +import polars as pl + +POLY_DTYPES = ( + pl.datatypes.FLOAT_DTYPES, + pl.datatypes.INTEGER_DTYPES, + pl.datatypes.UNSIGNED_INTEGER_DTYPES, +) + + +class BaseDiademInterface(ABC): + """A base class for interfaces in Diadem. + + Parameters + ---------- + data : DataFrame | LazyFrame + A polars or pandas DataFrame containing the data for the interface. + """ + + @classmethod + def from_parquet(cls, source: PathLike) -> None: + """Initialize the interface from a parquet file. + + Parameters + ---------- + source : PathLike + The parquet file. + """ + return cls(pl.scan_parquet(source)) + + def __init__(self, data: pl.DataFrame | pl.LazyFrame) -> None: + """Initialize the interface.""" + try: + self.data = data.lazy() + except AttributeError: + self.data = pl.from_pandas(data).lazy() + + self.validate_columns() + + def validate_columns(self) -> None: + """Verify that the required columns are present and the correct dtype.""" + req_dtypes = { + c.name: _check_for_poly_dtype(c.dtype) for c in self.required_columns + } + + dtype_errors = [] + missing_columns = set(req_dtypes.keys()) + for col, dtype in zip(self.data.columns, self.data.dtypes): + dtype = _check_for_poly_dtype(dtype) + if not dtype == req_dtypes[dtype]: + dtype_errors.append((col, dtype, req_dtypes[dtype])) + + try: + missing_columns.remove(col) + except KeyError: + pass + + if not dtype_errors and not missing_columns: + return + + dtype_msg = [ + f" - {n}: {d} (Expected {'or'.join(r)})" for n, d, r in dtype_errors + ] + missing_msg = [f" - {c}" for c in missing_columns] + + msg = [] + if dtype_errors: + msg.append("Some columns were of the wrong data type:") + msg += dtype_msg + + if missing_columns: + msg.append("Some columns were missing:") + msg += missing_msg + + raise ValueError("\n".join(msg)) + + @abstractmethod + @property + def required_columns(self) -> Iterable[RequiredColumn]: + """The required columns for the underlying DataFrame.""" + + +@dataclass +class RequiredColumn: + """Specify a required column. + + Parameters + ---------- + name : str + The column name. + dtype : pl.datatypes.Datatype + The polars data type for the column. + """ + + name: str + dtype: pl.datatypes.DataType + + @classmethod + def from_iter( + cls, + columns: Iterable[tuple[str, pl.DataType], ...], + ) -> Generator[RequiredColumn, ...]: + """Create required columns from an iterable. + + Parameters + ---------- + columns : Iterable[tuple[str, pl.DataType]] + 2-tuples of name-dtype pairs to be required. + + Yields + ------ + RequiredColumn + """ + for col, dtype in columns: + yield cls(col, dtype) + + +def _check_for_poly_dtype( + dtype: pl.datatypes.DataType, +) -> set[pl.datatypes.DataType, ...]: + """Check for poly-dtypes, like floats and ints. + + Parameters + ---------- + dtype : pl.datatypes.DataType + A polars datatype + + Returns + ------- + set[pl.datatypes.DataType] + """ + dtype = set(dtype) + for poly_dtype in POLY_DTYPES: + if dtype.issubset(poly_dtype): + return poly_dtype + + return dtype diff --git a/tests/unit_tests/interface.py b/tests/unit_tests/interface.py new file mode 100644 index 0000000..4b4b9a1 --- /dev/null +++ b/tests/unit_tests/interface.py @@ -0,0 +1,66 @@ +"""Verify that our interface base class works.""" +import polars as pl +import pytest + +from diadem.interfaces import BaseDiademInterface, RequiredColumn + + +def test_required_column(): + """Test the required column dataclass.""" + cols = [("foo", pl.datatypes.Float32), ("bar", pl.datatypes.Field)] + req = RequiredColumn.from_iter(cols) + + for col, result in zip(cols, req): + assert col[0] == result.name + assert col[1] == result.dtype + + +def test_base_interface_init(): + """Test our initialization.""" + + class RealInterface(BaseDiademInterface): + """A dummy interface.""" + + def __init__(self, data): + """Init the thing.""" + self.required_columns = [ + RequiredColumn("foo", pl.datatypes.Float32), + RequiredColumn("bar", pl.datatypes.Field), + ] + super().__init__(data) + + good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) + interface = RealInterface(good_df) + pl.testing.assert_frame_equal(interface.data.collect(), good_df) + + bad_df = pl.DataFrame({"foo": ["a", "b"], "bar": ["a", "b"]}) + with pytest.raises(ValueError) as err: + RealInterface(bad_df) + + assert "wrong data type" in str(err.value) + + bad_df = pl.DataFrame({"bar": ["a", "b"]}) + with pytest.raises(ValueError) as err: + RealInterface(bad_df) + + assert "missing" in str(err.value) + + +def test_base_interface_from_parquet(tmp_path): + """Test loading a parquet file.""" + + class RealInterface(BaseDiademInterface): + """A dummy interface.""" + + def __init__(self, data): + """Init the thing.""" + self.required_columns = [ + RequiredColumn("foo", pl.datatypes.Float32), + RequiredColumn("bar", pl.datatypes.Field), + ] + super().__init__(data) + + good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) + good_df.write_parquet(tmp_path / "test.parquet") + interface = RealInterface.from_parquet(tmp_path / "test.parquet") + pl.testing.assert_frame_equal(interface.data.collect(), good_df) From ca7a5eb4c76a7c65646b527da45525c12ddf2375 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Mon, 8 May 2023 15:51:54 -0700 Subject: [PATCH 32/41] Fixed interface tests --- diadem/interfaces.py | 18 +++---- tests/unit_tests/test_interface.py | 75 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 tests/unit_tests/test_interface.py diff --git a/diadem/interfaces.py b/diadem/interfaces.py index d2be571..4bc4e21 100644 --- a/diadem/interfaces.py +++ b/diadem/interfaces.py @@ -61,8 +61,8 @@ def validate_columns(self) -> None: missing_columns = set(req_dtypes.keys()) for col, dtype in zip(self.data.columns, self.data.dtypes): dtype = _check_for_poly_dtype(dtype) - if not dtype == req_dtypes[dtype]: - dtype_errors.append((col, dtype, req_dtypes[dtype])) + if not dtype == req_dtypes[col]: + dtype_errors.append((col, dtype, req_dtypes[col])) try: missing_columns.remove(col) @@ -72,24 +72,24 @@ def validate_columns(self) -> None: if not dtype_errors and not missing_columns: return - dtype_msg = [ - f" - {n}: {d} (Expected {'or'.join(r)})" for n, d, r in dtype_errors - ] - missing_msg = [f" - {c}" for c in missing_columns] - msg = [] if dtype_errors: + dtype_msg = [ + f" - {n}: {d} (Expected {','.join([str(i) for i in r])})" + for n, d, r in dtype_errors + ] msg.append("Some columns were of the wrong data type:") msg += dtype_msg if missing_columns: + missing_msg = [f" - {c[0]}" for c in missing_columns] msg.append("Some columns were missing:") msg += missing_msg raise ValueError("\n".join(msg)) - @abstractmethod @property + @abstractmethod def required_columns(self) -> Iterable[RequiredColumn]: """The required columns for the underlying DataFrame.""" @@ -143,7 +143,7 @@ def _check_for_poly_dtype( ------- set[pl.datatypes.DataType] """ - dtype = set(dtype) + dtype = {dtype} for poly_dtype in POLY_DTYPES: if dtype.issubset(poly_dtype): return poly_dtype diff --git a/tests/unit_tests/test_interface.py b/tests/unit_tests/test_interface.py new file mode 100644 index 0000000..7f7e4a6 --- /dev/null +++ b/tests/unit_tests/test_interface.py @@ -0,0 +1,75 @@ +"""Verify that our interface base class works.""" +import polars as pl +import pytest +from polars.testing import assert_frame_equal + +from diadem.interfaces import BaseDiademInterface, RequiredColumn + + +def test_required_column(): + """Test the required column dataclass.""" + cols = [("foo", pl.datatypes.Float32), ("bar", pl.datatypes.Field)] + req = RequiredColumn.from_iter(cols) + + for col, result in zip(cols, req): + assert col[0] == result.name + assert col[1] == result.dtype + + +def test_base_interface_init(): + """Test our initialization.""" + + class RealInterface(BaseDiademInterface): + """A dummy interface.""" + + def __init__(self, data): + """Init the thing.""" + super().__init__(data) + + @property + def required_columns(self): + """The required columns.""" + return [ + RequiredColumn("foo", pl.datatypes.Float32), + RequiredColumn("bar", pl.datatypes.Utf8), + ] + + good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) + interface = RealInterface(good_df) + assert_frame_equal(interface.data.collect(), good_df) + + bad_df = pl.DataFrame({"foo": ["a", "b"], "bar": ["a", "b"]}) + with pytest.raises(ValueError) as err: + RealInterface(bad_df) + + assert "wrong data type" in str(err.value) + + bad_df = pl.DataFrame({"bar": ["a", "b"]}) + with pytest.raises(ValueError) as err: + RealInterface(bad_df) + + assert "missing" in str(err.value) + + +def test_base_interface_from_parquet(tmp_path): + """Test loading a parquet file.""" + + class RealInterface(BaseDiademInterface): + """A dummy interface.""" + + def __init__(self, data): + """Init the thing.""" + super().__init__(data) + + @property + def required_columns(self): + """The required columns.""" + return [ + RequiredColumn("foo", pl.datatypes.Float32), + RequiredColumn("bar", pl.datatypes.Utf8), + ] + + good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) + good_df.write_parquet(tmp_path / "test.parquet") + interface = RealInterface.from_parquet(tmp_path / "test.parquet") + pl.testing.assert_frame_equal(interface.data.collect(), good_df) From 78a4f6828720b5da0c8be11a2a172ced16a0ce89 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Mon, 8 May 2023 15:52:12 -0700 Subject: [PATCH 33/41] moved test --- tests/unit_tests/interface.py | 66 ----------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 tests/unit_tests/interface.py diff --git a/tests/unit_tests/interface.py b/tests/unit_tests/interface.py deleted file mode 100644 index 4b4b9a1..0000000 --- a/tests/unit_tests/interface.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Verify that our interface base class works.""" -import polars as pl -import pytest - -from diadem.interfaces import BaseDiademInterface, RequiredColumn - - -def test_required_column(): - """Test the required column dataclass.""" - cols = [("foo", pl.datatypes.Float32), ("bar", pl.datatypes.Field)] - req = RequiredColumn.from_iter(cols) - - for col, result in zip(cols, req): - assert col[0] == result.name - assert col[1] == result.dtype - - -def test_base_interface_init(): - """Test our initialization.""" - - class RealInterface(BaseDiademInterface): - """A dummy interface.""" - - def __init__(self, data): - """Init the thing.""" - self.required_columns = [ - RequiredColumn("foo", pl.datatypes.Float32), - RequiredColumn("bar", pl.datatypes.Field), - ] - super().__init__(data) - - good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) - interface = RealInterface(good_df) - pl.testing.assert_frame_equal(interface.data.collect(), good_df) - - bad_df = pl.DataFrame({"foo": ["a", "b"], "bar": ["a", "b"]}) - with pytest.raises(ValueError) as err: - RealInterface(bad_df) - - assert "wrong data type" in str(err.value) - - bad_df = pl.DataFrame({"bar": ["a", "b"]}) - with pytest.raises(ValueError) as err: - RealInterface(bad_df) - - assert "missing" in str(err.value) - - -def test_base_interface_from_parquet(tmp_path): - """Test loading a parquet file.""" - - class RealInterface(BaseDiademInterface): - """A dummy interface.""" - - def __init__(self, data): - """Init the thing.""" - self.required_columns = [ - RequiredColumn("foo", pl.datatypes.Float32), - RequiredColumn("bar", pl.datatypes.Field), - ] - super().__init__(data) - - good_df = pl.DataFrame({"foo": [1.0, 2.0], "bar": ["a", "b"]}) - good_df.write_parquet(tmp_path / "test.parquet") - interface = RealInterface.from_parquet(tmp_path / "test.parquet") - pl.testing.assert_frame_equal(interface.data.collect(), good_df) From 977c9a30a74c876e249d1754fd4c5459adc46676 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Wed, 10 May 2023 18:07:26 -0700 Subject: [PATCH 34/41] added correlation filtering, parquet caching and improvement of data pipeline --- diadem/config.py | 2 +- diadem/data_io/mzml.py | 5 + diadem/data_io/timstof.py | 684 ++++++----------------------- diadem/search/diadem.py | 14 +- diadem/search/metrics.py | 11 + profiling/configs/tims.toml | 50 +++ profiling/dvc.yaml | 29 +- profiling/src/plot_results_base.py | 13 + profiling/src/run.py | 21 + 9 files changed, 263 insertions(+), 566 deletions(-) create mode 100644 profiling/configs/tims.toml create mode 100644 profiling/src/plot_results_base.py diff --git a/diadem/config.py b/diadem/config.py index 7f6bdb7..ebeffd6 100644 --- a/diadem/config.py +++ b/diadem/config.py @@ -181,7 +181,7 @@ class DiademConfig(DiademIndexConfig): # noqa run_min_correlation_score: float = 0.2 run_scaling_ratio: float = 0.001 - run_scalin_limits: tuple[float, float] = (0.001, 0.999) + run_scaling_limits: tuple[float, float] = (0.001, 0.999) @property def ms2ml_config(self) -> Config: diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index 849676a..f7ccbf1 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -400,6 +400,7 @@ class StackedChromatograms: base_peak_intensity: float stack_peak_indices: list[list[int]] | list[NDArray[np.int32]] center_intensities: NDArray[np.float32] + correlations: NDArray def __post_init__(self) -> None: """Checks that the dimensions of the arrays are correct. @@ -565,11 +566,14 @@ def from_group( max_peak_corr = np.sort(corrs)[-max_peaks] if len(corrs) > max_peaks else -1 keep = corrs >= max(min_correlation, max_peak_corr) + keep_corrs = corrs[keep] stacked_arr = stacked_arr[..., keep, ::1] center_mzs = center_mzs[keep] center_intensities = center_intensities[keep] indices = [[y for y, k in zip(x, keep) if k] for x in indices] + else: + keep_corrs = np.array([1.0]) ref_id = np.argmax(stacked_arr[..., center_index]) bp_int = stacked_arr[ref_id, center_index] @@ -582,6 +586,7 @@ def from_group( base_peak_intensity=bp_int, stack_peak_indices=indices, center_intensities=center_intensities, + correlations=keep_corrs, ) return out diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index b551af5..85475ca 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -4,8 +4,8 @@ from collections.abc import Iterator from contextlib import contextmanager from dataclasses import dataclass -from itertools import chain from os import PathLike +from pathlib import Path from typing import Literal import numpy as np @@ -14,8 +14,8 @@ from joblib import Parallel, delayed from loguru import logger from ms2ml.utils.mz_utils import get_tolerance +from msflattener.bruker import get_timstof_data from numpy.typing import NDArray -from tqdm.auto import tqdm from diadem.config import DiademConfig from diadem.data_io.mzml import ( @@ -25,15 +25,11 @@ StackedChromatograms, ) from diadem.data_io.utils import slice_from_center, xic -from diadem.deisotoping import deisotope_with_ims from diadem.search.metrics import get_ref_trace_corrs -from diadem.utilities.neighborhood import multidim_neighbor_search from diadem.utilities.utils import is_sorted if "PLOTDIADEM" in os.environ: - import random # noqa: I001 - - from matplotlib import pyplot as plt + pass IMSError = Literal["abs", "pct"] @@ -225,11 +221,14 @@ def from_group( max_peak_corr = np.sort(corrs)[-max_peaks] if len(corrs) > max_peaks else -1 keep = corrs >= max(min_correlation, max_peak_corr) + keep_corrs = corrs[keep] stacked_arr = stacked_arr[..., keep, ::1] u_center_mzs = u_center_mzs[keep] u_center_intensities = u_center_intensities[keep] indices = [[y for y, k in zip(x, keep) if k] for x in indices] + else: + keep_corrs = np.array([1.0]) ref_id = np.argmax(stacked_arr[..., center_index]) bp_int = stacked_arr[ref_id, center_index] @@ -244,6 +243,7 @@ def from_group( stack_peak_indices=indices, center_intensities=u_center_intensities, ref_ims=bp_ims, + correlations=keep_corrs, ) return out @@ -395,8 +395,8 @@ def _scale_spectrum_at( self.base_peak_int[i] = np.max(self.intensities[i]) self.base_peak_mz[i] = self.mzs[i][np.argmax(self.intensities[i])] else: - self.base_peak_int[i] = -1 - self.base_peak_mz[i] = -1 + self.base_peak_int[i] = 0 + self.base_peak_mz[i] = 0 def __len__(self) -> int: """Returns the number of spectra in the group.""" @@ -418,140 +418,139 @@ def __init__(self, filepath: PathLike, config: DiademConfig) -> None: """ self.filepath = filepath self.config = config - with self.lazy_datafile() as datafile: - unique_precursor_indices = np.unique(datafile.precursor_indices) - unique_precursor_indices = [x for x in unique_precursor_indices if x != 0] + self.cache_location = Path(filepath).with_suffix(".centroided.parquet") + if self.cache_location.exists(): + logger.info(f"Found cache file at {self.cache_location}") + else: + df = get_timstof_data(filepath, centroid=True) + df.write_parquet(self.cache_location) + del df + + unique_windows = ( + pl.scan_parquet(self.cache_location) + .select(pl.col(["quad_low_mz_values", "quad_high_mz_values"])) + .filter(pl.col("quad_low_mz_values") > 0) + .sort("quad_low_mz_values") + .unique() + .collect() + ) if "DEBUG_DIADEM" in os.environ: logger.error("RUNNING DIADEM IN DEBUG MODE (only the 4th precursor index)") - self.unique_precursor_indices = unique_precursor_indices[3:4] + self.unique_precursor_windows = unique_windows[3:4].rows(named=True) else: - self.unique_precursor_indices = unique_precursor_indices + self.unique_precursor_windows = unique_windows.rows(named=True) @contextmanager def lazy_datafile(self) -> TimsTOF: - """Reads the timstof data and yields it as context manager. - - This is right now a bandaid to prevent the full TimsTOF object - to be stored in memmory for the whole lifetime of this class. - """ - _datafile = TimsTOF(self.filepath) - yield _datafile - del _datafile + """Scans the cached version of the data and yields it as a context manager.""" + yield pl.scan_parquet(self.cache_location) # @profile def _precursor_iso_window_groups( self, - precursor_index: int, - progress: bool = True, + precursor_window: dict[str, float], ) -> dict[str:TimsScanGroup]: - elems = self._precursor_iso_window_elements(precursor_index, progress) - out = {} - for k, v in elems.items(): - # Note: precursor id of 0 means inactive quadrupole - # and collision cell. - prec_info = self._precursor_iso_window_elements( - 0, - progress=progress, - mz_range=v["precursor_range"], - ) - assert len(list(prec_info)) == 1 - prec_key = list(prec_info)[0] - prec_info = prec_info[prec_key] - inten_filter = prec_info["base_peak_int"] > 0 - - precursor_mzs = [ - k for i, k in enumerate(prec_info["mzs"]) if inten_filter[i] - ] - precursor_intensities = [ - k for i, k in enumerate(prec_info["intensities"]) if inten_filter[i] - ] - precursor_imss = [ - k for i, k in enumerate(prec_info["imss"]) if inten_filter[i] - ] - precursor_rts = prec_info["retention_times"][inten_filter] + elems = self._precursor_iso_window_elements(precursor_window) + prec_info = self._precursor_iso_window_elements( + {"quad_low_mz_values": -1, "quad_high_mz_values": -1}, + mz_range=list(precursor_window.values()), + ) - assert is_sorted(precursor_rts) + assert is_sorted(prec_info["retention_times"]) - out[k] = TimsScanGroup( - precursor_mzs=precursor_mzs, - precursor_intensities=precursor_intensities, - precursor_retention_times=precursor_rts, - precursor_imss=precursor_imss, - **v, - ) + out = TimsScanGroup( + precursor_mzs=prec_info["mzs"], + precursor_intensities=prec_info["intensities"], + precursor_retention_times=prec_info["retention_times"], + precursor_imss=prec_info["imss"], + **elems, + ) return out # @profile def _precursor_iso_window_elements( self, - precursor_index: int, - progress: bool = True, + precursor_window: dict[str, float], mz_range: None | tuple[float, float] = None, ) -> dict[str : dict[str:NDArray]]: with self.lazy_datafile() as datafile: - index_win_dicts = _get_precursor_index_windows( - datafile, - precursor_index=precursor_index, - progress=progress, - mz_range=mz_range, + datafile: pl.LazyFrame + promise = ( + pl.col("quad_low_mz_values") == precursor_window["quad_low_mz_values"] + ) & ( + pl.col("quad_high_mz_values") == precursor_window["quad_high_mz_values"] ) - out = {} - - for k, v in index_win_dicts.items(): - mzs = [] - ints = [] - imss = [] - - for vi in v: - if len(vi["mz"]) > 0: - new_order = np.argsort(vi["mz"]) - mzs.append(vi["mz"][new_order]) - ints.append(vi["intensity"][new_order]) - imss.append(vi["ims"][new_order]) - else: - mzs.append(vi["mz"]) - ints.append(vi["intensity"]) - imss.append(vi["ims"]) - - # TODO change this to a data structure that stores - # this only once. - quad_low = list({x["quad_low_mz_values"] for x in v}) - quad_high = list({x["quad_high_mz_values"] for x in v}) - assert len(quad_high) == 1 - assert len(quad_low) == 1 - - bp_indices = [np.argmax(x) if len(x) else None for x in ints] - bp_ints = [ - x1[x2] if x2 is not None else 0 for x1, x2 in zip(ints, bp_indices) + ms_data = datafile.filter(promise).sort("rt_values") + + if mz_range is not None: + nested_cols = [ + "mz_values", + "corrected_intensity_values", + "mobility_values", ] - bp_ints = np.array(bp_ints) - bp_mz = [ - x1[x2] if x2 is not None else -1 for x1, x2 in zip(mzs, bp_indices) + non_nested_cols = [ + x for x in ms_data.head().collect().columns if x not in nested_cols ] - bp_mz = np.array(bp_mz) - bp_indices = np.array(bp_indices) - # bp_ims = np.array([x1[x2] for x1, x2 in zip(imss, bp_indices)]) - rts = np.array([x["rt_values_min"] for x in v]) - assert is_sorted(rts) - - scan_indices = [str(x["scan_indices"]) for x in v] - - x = { - "precursor_range": (quad_low[0], quad_high[0]), - "mzs": mzs, - "intensities": ints, - "imss": imss, - "base_peak_int": bp_ints, - "base_peak_mz": bp_mz, - # 'base_peak_ims':bp_ims, - "iso_window_name": k, - "retention_times": rts, - "scan_ids": scan_indices, - } - out[k] = x + ms_data = ( + ms_data.explode(nested_cols) + .filter(pl.col("mz_values").is_between(mz_range[0], mz_range[1])) + .groupby(pl.col(non_nested_cols)) + .agg(nested_cols) + .sort("rt_values") + ) + + ms_data = ms_data.collect() + + bp_indices = [np.argmax(x) for x in ms_data["corrected_intensity_values"]] + bp_ints = [ + x1.to_numpy()[x2] + for x1, x2 in zip(ms_data["corrected_intensity_values"], bp_indices) + ] + bp_ints = np.array(bp_ints) + bp_mz = [ + x1.to_numpy()[x2] for x1, x2 in zip(ms_data["mz_values"], bp_indices) + ] + bp_mz = np.array(bp_mz) + bp_indices = np.array(bp_indices) + rts = ms_data["rt_values"].to_numpy(zero_copy_only=True) + assert is_sorted(rts) + + quad_high = ms_data["quad_high_mz_values"][0] + quad_low = ms_data["quad_low_mz_values"][0] + window_name = str(quad_low) + "_" + str(quad_high) + + template = window_name + "_{}" + scan_indices = [template.format(i) for i in range(len(rts))] + + x = { + "precursor_range": (quad_low, quad_high), + "base_peak_int": bp_ints, + "base_peak_mz": bp_mz, + # 'base_peak_ims':bp_ims, + "iso_window_name": window_name, + "retention_times": rts, + "scan_ids": scan_indices, + } + orders = [np.argsort(x.to_numpy()) for x in ms_data["mz_values"]] + + x.update( + { + "mzs": [ + x.to_numpy()[o] for x, o in zip(ms_data["mz_values"], orders) + ], + "intensities": [ + x.to_numpy()[o] + for x, o in zip(ms_data["corrected_intensity_values"], orders) + ], + "imss": [ + x.to_numpy()[o] + for x, o in zip(ms_data["mobility_values"], orders) + ], + }, + ) - return out + return x def get_iso_window_groups(self, workerpool: None | Parallel) -> list[TimsScanGroup]: """Get scan groups for each unique isolation window. @@ -570,33 +569,24 @@ def get_iso_window_groups(self, workerpool: None | Parallel) -> list[TimsScanGro Each of them corresponding to an unique isolation window from the quadrupole. """ - results = [] - if workerpool is None: - nested_results = [ + results = [ self._precursor_iso_window_groups(i) - for i in self.unique_precursor_indices + for i in self.unique_precursor_windows ] else: - nested_results = workerpool( + results = workerpool( delayed(self._precursor_iso_window_groups)(i) - for i in self.unique_precursor_indices + for i in self.unique_precursor_windows ) - for r in nested_results: - for rr in r.values(): - results.append(rr) - return results - def yield_iso_window_groups(self, progress: bool = True) -> Iterator[TimsScanGroup]: + def yield_iso_window_groups(self) -> Iterator[TimsScanGroup]: """Yield scan groups for each unique isolation window.""" - for i in self.unique_precursor_indices: - nested_results = self._precursor_iso_window_groups(i, progress=progress) - for r in nested_results.values(): - # TODO add here a the export of the cached windows - # maybe ... or just add a full interface to read this from cache... - yield r + for i in self.unique_precursor_windows: + results = self._precursor_iso_window_groups(i) + yield results # @profile @@ -707,429 +697,3 @@ def get_break_indices( breaks[-1] += 1 return breaks, break_indices - - -# @profile -def _get_precursor_index_windows( - dia_data: TimsTOF, - precursor_index: int, - progress: bool = True, - mz_range: None | tuple[float, float] = None, -) -> dict[dict[list]]: - inds = dia_data[{"precursor_indices": precursor_index}, "raw"] - - break_indices, break_values = get_break_indices(inds=inds) - - # This will generate chunks of contiguous indices - # Which are fast to query. - # They do not assure that there will be only one quad window in - # them - pbar = tqdm( - zip(break_indices[:-1], break_indices[1:]), - total=len(break_indices), - disable=not progress, - desc=f"Collecting spectra for precursor={precursor_index}", - bar_format="{l_bar}{bar:10}{r_bar}{bar:-10b}", - ) - quad_splits = {} - for startind, endind in pbar: - contig_peak_data = dia_data.convert_from_indices( - inds[startind:endind], - raw_indices_sorted=True, - return_mz_values=True, - return_corrected_intensity_values=True, - return_mobility_values=True, - return_rt_values_min=True, - # I think this is a better value than the actual RT - # since accounts for accumulation time. - return_quad_mz_values=True, - return_quad_indices=True, - # return_scan_indices=True, - ) - # Scan indices are the same for the same rt-ims-quad combination - # so not very useful in our case. - # Since we want groups of peaks that share rt-quad values, regardless - # of the IMS value. - - df_peak_data = pl.DataFrame(contig_peak_data) - grouping_vals = [ - "quad_low_mz_values", - "quad_high_mz_values", - "rt_values_min", - "quad_indices", - ] - peak_data_vals = ["mz_values", "corrected_intensity_values", "mobility_values"] - - g_inds, g_vals = get_break_indices( - df_peak_data["quad_indices"].to_numpy(zero_copy_only=True), - min_diff=0, - ) - - for si, ei in zip(g_inds[:-1], g_inds[1:]): - curr_peak_data = df_peak_data[si:ei, :] - - # Sanity check to make sure all peaks in the selected chunk share - # the same retention times and quad isolation windows - assert all( - np.abs(curr_peak_data[x].abs().max() - curr_peak_data[x].abs().max()) - < 1e-3 - for x in grouping_vals - ) - current_chunk_data = {k: curr_peak_data[k][0] for k in grouping_vals} - quad_name = ( - f"{current_chunk_data['quad_low_mz_values']:.6f}," - f" {current_chunk_data['quad_high_mz_values']:.6f}" - ) - if ( - "DEBUG_DIADEM" in os.environ - and quad_splits - and quad_name not in quad_splits - ): - # This makes it so that when enabling the debug diadem - # mode will only use one quad iso window instead of all - # quad iso windows that share the same precursor incex. - continue - - current_chunk_data["scan_indices"] = current_chunk_data["quad_indices"] - - peak_data = { - k: v.to_numpy(zero_copy_only=True) - for k, v in curr_peak_data[peak_data_vals].to_dict().items() - } - - start_len = len(peak_data["mz_values"]) - - if not len(peak_data["mz_values"]): - starting_min_intensity = 0 - starting_max_intensity = 0 - else: - starting_min_intensity = np.min(peak_data["corrected_intensity_values"]) - starting_max_intensity = np.max(peak_data["corrected_intensity_values"]) - - mz, intensity, ims = _preprocess_ims( - ims_values=peak_data["mobility_values"], - mz_values=peak_data["mz_values"], - intensity_values=peak_data["corrected_intensity_values"], - mz_range=mz_range, - ) - - d_out = {"mz": mz, "intensity": intensity, "ims": ims} - ## updating the progress bar - if not len(mz): - pct_compression = 0 - final_len = 0 - f_min = 0 - f_max = 0 - else: - pct_compression = round(100 * len(d_out["mz"]) / start_len) - final_len = len(d_out["mz"]) - f_min = int(np.min(d_out["intensity"])) - f_max = int(np.max(d_out["intensity"])) - current_chunk_data.update(d_out) - quad_splits.setdefault(quad_name, []).append(current_chunk_data) - - postfix_dict = { - "peaks": start_len, - "f_peaks": final_len, - "pct": pct_compression, - "min": starting_min_intensity, - "max": starting_max_intensity, - "f_min": f_max, - "f_max": f_min, - } - postfix_dict = {k: f"{v:.1e}" for k, v in postfix_dict.items()} - pbar.set_postfix( - postfix_dict, - refresh=True, - ) - - return quad_splits - - -# @profile -def _preprocess_ims( - ims_values: NDArray[np.float32], - mz_values: NDArray[np.float32], - intensity_values: NDArray[np.float32], - mz_range: None | tuple[float, float] = None, -) -> tuple[NDArray[np.float32], NDArray[np.float32], NDArray[np.float32]]: - # TODO make all of these arguments - # or make this callable class. - PRELIM_N_PEAK_FILTER = 5_000 # noqa - FINAL_N_PEAK_FILTER = 1500 # noqa - MIN_INTENSITY_KEEP = 500 # noqa - MIN_NUM_SEEDS = 5_000 # noqa - IMS_TOL = 0.01 # noqa - # these tolerances are set narrow on purpose, since they - # only delimit the definition of the neighborhood, which will iteratively - # expanded. - MZ_TOL = 0.02 # noqa - MAX_ISOTOPE_CHARGE = 3 # noqa - if len(intensity_values) > PRELIM_N_PEAK_FILTER: - partition_indices = np.argpartition( - intensity_values, - -PRELIM_N_PEAK_FILTER, - )[-PRELIM_N_PEAK_FILTER:] - - ims_values = ims_values[partition_indices] - mz_values = mz_values[partition_indices] - intensity_values = intensity_values[partition_indices] - - sort_idx = np.argsort(mz_values) - - ims_values = ims_values[sort_idx] - mz_values = mz_values[sort_idx] - intensity_values = intensity_values[sort_idx] - - # Find highest peak, plot 4 mz and 0.3 IMS around it - if "PLOTDIADEM" in os.environ: - mz_range_val = 3 - ims_range_val = 0.3 - top_intense = np.argmax(intensity_values) - top_mz = mz_values[top_intense] - top_ims = ims_values[top_intense] - plot_mz_range = (top_mz - mz_range_val, top_mz + mz_range_val) - plot_ims_range = (top_ims - ims_range_val, top_ims + ims_range_val) - - top_plot_filter = ( - (mz_values > plot_mz_range[0]) - & (mz_values < plot_mz_range[1]) - & (ims_values > plot_ims_range[0]) - & (ims_values < plot_ims_range[1]) - ) - o_plot_ims_values = ims_values[top_plot_filter] - o_plot_mz_values = mz_values[top_plot_filter] - o_plot_intensity_values = intensity_values[top_plot_filter] - - if mz_range is not None: - mz_filter = slice( - np.searchsorted(mz_values, mz_range[0]), - np.searchsorted(mz_values, mz_range[1], "right"), - ) - ims_values = ims_values[mz_filter] - mz_values = mz_values[mz_filter] - intensity_values = intensity_values[mz_filter] - - # This is the bottleneck of the whole issue... - f = collapse_ims( - ims_values=ims_values, - mz_values=mz_values, - intensity_values=intensity_values, - top_n=MIN_NUM_SEEDS, - ims_tol=IMS_TOL, - mz_tol=MZ_TOL, - ) - if len(f["mz"]) < 0: - new_order = np.argsort(f["mz"]) - f = {k: v[new_order] for k, v in f.items()} - - mz, intensity, ims = deisotope_with_ims( - ims_diff=IMS_TOL, - ims_unit="abs", - imss=f["ims"], - inten=f["intensity"], - mz=f["mz"], - mz_unit="da", - track_indices=False, - mz_diff=MZ_TOL, - max_charge=MAX_ISOTOPE_CHARGE, - ) - intensity = np.array(intensity) - - if len(mz) < 0: - filter = intensity > MIN_INTENSITY_KEEP - mz = mz[filter] - intensity = intensity[filter] - ims = ims[filter] - - if "PLOTDIADEM" in os.environ: - top_plot_filter = ( - (mz > plot_mz_range[0]) - & (mz < plot_mz_range[1]) - & (ims > plot_ims_range[0]) - & (ims < plot_ims_range[1]) - ) - if len(top_plot_filter) > 0: - plot_ims_values = ims[top_plot_filter] - plot_mz_values = mz[top_plot_filter] - plot_intensity_values = intensity[top_plot_filter] - - plt.clf() - plt.scatter( - o_plot_mz_values, - o_plot_ims_values, - c="gray", - s=np.sqrt(o_plot_intensity_values), - ) - plt.scatter( - plot_mz_values, - plot_ims_values, - c=plot_intensity_values, - cmap="viridis", - s=np.sqrt(plot_intensity_values), - alpha=0.8, - ) - if np.sum(plot_intensity_values) > 1000: - if random.random() > 0.01: - plt.pause(0.1) - - if len(intensity) > FINAL_N_PEAK_FILTER: - partition_indices = np.argpartition( - intensity, - -FINAL_N_PEAK_FILTER, - )[-FINAL_N_PEAK_FILTER:] - - ims = ims[partition_indices] - mz = mz[partition_indices] - intensity = intensity[partition_indices] - - # EXPERIMENTAL - # Does not seem to make it any better - # intensity = np.sqrt(intensity) - - return mz, intensity, ims - - -# @profile -def _collapse_seeds( - seeds: dict[int, list[int]], - intensity_values: NDArray[np.float32], -) -> tuple[dict[int, int], set[int]]: - seeds = seeds.copy() - seed_keys = list(seeds.keys()) - seed_order = np.argsort(-intensity_values[np.array(seed_keys)]) - taken = set() - out_seeds = {} - MAX_ITER = 5 # noqa - - for s in seed_order.tolist(): - sk = seed_keys[s] - if sk in taken: - continue - - out_seeds[sk] = set(seeds.pop(sk, {sk})) - curr_len = 1 - - for _ in range(MAX_ITER): - neigh_set = set(chain(*[seeds.pop(x, set()) for x in out_seeds[sk]])) - neigh_set = neigh_set.union(out_seeds[sk]) - untaken = neigh_set.difference(taken) - taken.update(untaken) - out_seeds[sk].update(untaken) - t_curr_len = len(out_seeds[sk]) - if curr_len == t_curr_len: - break - curr_len = t_curr_len - - out_seeds = {k: v for k, v in out_seeds.items() if len(v) > 0} - return out_seeds, taken - - -# @profile -def collapse_ims( - ims_values: NDArray[np.float32], - mz_values: NDArray[np.float32], - intensity_values: NDArray[np.float32], - top_n: int = 500, - top_n_pct: float = 0.2, - ims_tol: float = 0.01, - mz_tol: float = 0.01, -) -> dict[str, NDArray[np.float32]]: - """Collapses peaks with similar IMS and MZ. - - Sample output - ------------- - ``` - bundled = { - "ims": np.zeros(..., dtype=np.float32), - "mz": np.zeros(..., dtype=np.float32), - "intensity": np.zeros(..., dtype=np.float32), - } - ```. - """ - MIN_NEIGHBORS_SEED = 3 # noqa - assert is_sorted(mz_values) - - # TODO refactor using the neighborhood module in utils - - ## New implementation start - top_n = int(max(len(intensity_values) * top_n_pct, top_n)) - if len(intensity_values) > top_n: - top_indices = np.argpartition(intensity_values, -top_n)[-top_n:] - else: - top_indices = None - - elems1 = { - "ims": ims_values, - "mz": mz_values, - } - if top_indices is not None: - top_ims = ims_values[top_indices] - top_mz = mz_values[top_indices] - elems2 = { - "ims": top_ims, - "mz": top_mz, - } - else: - elems2 = None - - tmp = multidim_neighbor_search( - elems1=elems1, - elems2=elems2, - dist_ranges={"ims": [-ims_tol, ims_tol], "mz": [-mz_tol, mz_tol]}, - dimension_order=["mz", "ims"], - ) - - if top_indices is not None: - neighborhoods = {top_indices[k]: v for k, v in tmp.right_neighbors.items()} - for k in neighborhoods: - # TODO check if passing the set directly works - neighborhoods[k].add(k) - else: - neighborhoods = tmp.right_neighbors - - # unambiguous = {k for k, v in neighborhoods.items() if len(v) == 1} - ambiguous = {k: v for k, v in neighborhoods.items() if len(v) > 1} - seeds = {k: v for k, v in ambiguous.items() if len(v) >= MIN_NEIGHBORS_SEED} - - if seeds: - out_seeds, taken = _collapse_seeds( - seeds=seeds, - intensity_values=intensity_values, - ) - else: - out_seeds, taken = {}, set() - - ambiguous_untaken = list(set(ambiguous).difference(taken)) - bundled = { - "ims": np.zeros(len(out_seeds), dtype=np.float32), - "mz": np.zeros(len(out_seeds), dtype=np.float32), - "intensity": np.zeros(len(out_seeds), dtype=np.float32), - } - for i, v in enumerate(out_seeds.values()): - v = list(v) - v_intensities = intensity_values[v] - # This generates the weighted average of the ims and mz - # as the new values for the peak. - bundled["intensity"][i] = (tot_intensity := v_intensities.sum()) - bundled["ims"][i] = (ims_values[v] * v_intensities).sum() / tot_intensity - bundled["mz"][i] = (mz_values[v] * v_intensities).sum() / tot_intensity - - # # # TODO consider whether I really want to keep ambiduous matches - # # # In theory I could keep only the expanded seeds. - # unambiguous_out = { - # "ims": ims_values[list(chain(unambiguous, ambiguous_untaken))], - # "mz": mz_values[list(chain(unambiguous, ambiguous_untaken))], - # "intensity": intensity_values[list(chain(unambiguous, ambiguous_untaken))], - # } - - unambiguous_out = { - "ims": ims_values[list(ambiguous_untaken)], - "mz": mz_values[list(ambiguous_untaken)], - "intensity": intensity_values[list(ambiguous_untaken)], - } - final = { - k: np.concatenate([unambiguous_out[k], bundled[k]]) for k in unambiguous_out - } - - return final diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 76a0e06..45e90f5 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -50,7 +50,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) WINDOWSIZE = config.run_window_size # noqa WINDOW_MAX_PEAKS = config.run_max_peaks_per_window # noqa - MIN_INTENSITY_SCALING, MAX_INTENSITY_SCALING = config.run_scalin_limits # noqa + MIN_INTENSITY_SCALING, MAX_INTENSITY_SCALING = config.run_scaling_limits # noqa SCALING_RATIO = config.run_scaling_ratio # noqa # Min intensity required on a peak in the base @@ -218,6 +218,14 @@ def search_group( # noqa C901 `search_group` is too complex (18) match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) + corr_match_indices = np.where( + new_stack.correlations + > np.median(new_stack.correlations[match_indices]), + )[0] + match_indices = np.sort( + np.unique(np.concatenate([match_indices, corr_match_indices])), + ) + if "PLOTDIADEM" in os.environ and os.environ["PLOTDIADEM"]: try: ax1.cla() @@ -242,7 +250,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) f"@ RT: {scores['RetentionTime'].iloc[0]}" ), ) - plt.pause(0.1) + plt.pause(0.01) scaling_window_indices = [ [x[y] for y in match_indices] for x in new_stack.stack_peak_indices @@ -417,7 +425,7 @@ def diadem_main( results = [] if config.run_parallelism == 1: - for group in ss.yield_iso_window_groups(progress=True): + for group in ss.yield_iso_window_groups(): group_db = db.index_prefiltered_from_parquet(cache, *group.precursor_range) group_results = search_group(group=group, db=group_db, config=config) if group_results is not None: diff --git a/diadem/search/metrics.py b/diadem/search/metrics.py index de8394e..5ce06b6 100644 --- a/diadem/search/metrics.py +++ b/diadem/search/metrics.py @@ -26,6 +26,16 @@ def cosinesim(x: NDArray, y: NDArray) -> NDArray: return out +def max_rolling(a, window, axis=1): + """From this answer: + https://stackoverflow.com/a/52219082. + """ + shape = a.shape[:-1] + (a.shape[-1] - window + 1, window) + strides = a.strides + (a.strides[-1],) + rolling = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) + return np.max(rolling, axis=axis) + + def spectral_angle(x: NDArray, y: NDArray) -> NDArray: """Computes the spectral angle between two vectors. @@ -75,6 +85,7 @@ def get_ref_trace_corrs(arr: NDArray[np.float32], ref_idx: int) -> NDArray[np.fl >>> [round(x, 4) for x in out] [0.8355, 0.8597, 0.8869, 0.9182, 0.9551, 0.9989, 0.9436, 0.8704, 0.7722, 0.6385] """ + arr = max_rolling(arr, 3, axis=1) norm = np.linalg.norm(arr + 1e-5, axis=-1) normalized_arr = arr / np.expand_dims(norm, axis=-1) ref_trace = normalized_arr[..., ref_idx, ::1] diff --git a/profiling/configs/tims.toml b/profiling/configs/tims.toml new file mode 100644 index 0000000..21670b8 --- /dev/null +++ b/profiling/configs/tims.toml @@ -0,0 +1,50 @@ +peptide_length_range = [ + 7, + 25, +] +peptide_mz_range = [ + 400, + 2000, +] +precursor_charges = [ + 2, + 3, +] +ion_series = "by" +ion_charges = [ + 1, +] +ion_mz_range = [ + 250, + 2000.0, +] +db_enzyme = "trypsin" +db_max_missed_cleavages = 2 +db_bucket_size = 32768 +g_tolerances = [ + 0.02, + 0.02, +] +g_tolerance_units = [ + "da", + "da", +] +g_ims_tolerance = 0.01 +g_ims_tolerance_unit = "abs" +scoring_score_function = "Hyperscore" +run_max_peaks = 20000 +run_max_peaks_per_spec = 5000 +run_parallelism = 1 +run_deconvolute_spectra = true +run_min_peak_intensity = 100 +run_debug_log_frequency = 50 +run_allowed_fails = 5000 +run_window_size = 21 +run_max_peaks_per_window = 150 +run_min_intensity_ratio = 0.01 +run_min_correlation_score = 0.2 +run_scaling_ratio = 0.001 +run_scaling_limits = [ + 0.001, + 0.9, +] diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index e9f9de5..6f4faf5 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -6,7 +6,7 @@ stages: - src/get_data.zsh - Dockerfile outs: - - profiling/profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d - profiling_data/UP000000625_83333_crap.fasta - profiling_data/UP000005640_9606_crap.fasta @@ -14,11 +14,36 @@ stages: deps: - src/run.py - config/orbi.toml - cmd: python src/run.py -- + - profiling_data/UP000000625_83333_crap.fasta + - profiling_data/XXXXXXXXXX.raw + outs: + - results/orbi/ecoli.diadem.csv + cmd: >- + mkdir -p results/orbi + python src/run.py + --config config/orbi.toml + --fasta profiling_data/UP000000625_83333_crap.fasta + --ms_data profiling_data/xxxxxxxxxxxx.raw + --output results/orbi/ecoli + --threads 4 tims_run: + deps: + - src/run.py + - configs/tims.toml + - profiling_data/UP000000625_83333_crap.fasta + - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d - src/run.py - config/tims.toml + outs: + - results/tims/ecoli.diadem.csv + cmd: >- + python src/run.py + --config configs/tims.toml + --fasta profiling_data/UP000000625_83333_crap.fasta + --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + --output results/tims/ecoli + --threads 4 plots: - orbi_qvals.csv: diff --git a/profiling/src/plot_results_base.py b/profiling/src/plot_results_base.py new file mode 100644 index 0000000..0eae1da --- /dev/null +++ b/profiling/src/plot_results_base.py @@ -0,0 +1,13 @@ +import numpy as np +import pandas as pd +from matplotlib import pyplot as plt + +out = pd.read_csv("results/tims/ecoli.diadem.csv") + +decoys = out[np.invert(out["decoy"])] +targets = out[out["decoy"]] + +plt.hist(decoys["Score"], label="Decoys", color="red", alpha=0.2, bins=100) +plt.hist(targets["Score"], label="Targets", color="blue", alpha=0.2, bins=100) + +plt.legend() diff --git a/profiling/src/run.py b/profiling/src/run.py index 324517d..ae96bc4 100644 --- a/profiling/src/run.py +++ b/profiling/src/run.py @@ -1,4 +1,8 @@ import argparse +from dataclasses import replace + +from diadem import cli +from diadem.config import DiademConfig parser = argparse.ArgumentParser() parser.add_argument("--config", type=str, help="Path to the config file") @@ -6,3 +10,20 @@ parser.add_argument("--ms_data", type=str, help="Path to the MS data file") parser.add_argument("--output", type=str, help="Path to the output file path") parser.add_argument("--threads", type=int, help="Number of threads to use") + + +if __name__ == "__main__": + args, unk = parser.parse_known_args() + if unk: + raise ValueError(f"Unrecognized arguments: {unk}") + + config = DiademConfig.from_toml(args.config) + config = replace(config, run_parallelism=args.threads) + + cli.setup_logger() + cli.diadem_main( + fasta_path=args.fasta, + data_path=args.ms_data, + config=config, + out_prefix=args.output, + ) From 6d774507f76bb622fe4d553e199216eb999ea064 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Thu, 11 May 2023 13:16:16 -0700 Subject: [PATCH 35/41] updated dvc --- .dvc/config | 2 + profiling/Dockerfile | 2 +- profiling/dvc.lock | 179 ++++++++++++++++++ profiling/dvc.yaml | 83 +++++--- profiling/src/{get_data.bash => get_data.zsh} | 11 +- profiling/src/plot_results.py | 64 ++++++- pyproject.toml | 3 +- 7 files changed, 303 insertions(+), 41 deletions(-) create mode 100644 profiling/dvc.lock rename profiling/src/{get_data.bash => get_data.zsh} (70%) diff --git a/.dvc/config b/.dvc/config index e69de29..4cf322d 100644 --- a/.dvc/config +++ b/.dvc/config @@ -0,0 +1,2 @@ +[core] + autostage = true diff --git a/profiling/Dockerfile b/profiling/Dockerfile index cbe6b7f..04764bc 100644 --- a/profiling/Dockerfile +++ b/profiling/Dockerfile @@ -2,6 +2,6 @@ FROM --platform=linux/amd64 python:3.9-bullseye RUN apt-get update && apt-get install -y build-essential gcc python3-dev -RUN python3 -m pip install alphatims +RUN python3 -m pip install git+https://github.com/jspaezp/alphatims.git@feature/dockerfile ENTRYPOINT [ "alphatims" ] diff --git a/profiling/dvc.lock b/profiling/dvc.lock new file mode 100644 index 0000000..cd81b3b --- /dev/null +++ b/profiling/dvc.lock @@ -0,0 +1,179 @@ +schema: '2.0' +stages: + tims_run_hela: + cmd: 'mkdir -p results/tims && python src/run.py --config configs/tims.toml --fasta + profiling_data/UP000005640_9606_crap.fasta --ms_data profiling_data/230426_Hela_01_S4-E5_1_662.hdf + --output results/tims/hela --threads 4 && python src/plot_results.py results/tims + --prefix hela ' + deps: + - path: profiling_data/230426_Hela_01_S4-E5_1_662.hdf + md5: 02f5846145d04ff9301f78d2b6299b75 + size: 4412473566 + - path: profiling_data/UP000005640_9606_crap.fasta + md5: 6a22e753f0e35eae9e0bf3d95759d089 + size: 13676378 + - path: src/run.py + md5: 9342df61b2738cdf083da7ece1581b59 + size: 944 + params: + configs/tims.toml: + db_bucket_size: 32768 + db_enzyme: trypsin + db_max_missed_cleavages: 2 + g_ims_tolerance: 0.01 + g_ims_tolerance_unit: abs + g_tolerance_units: + - da + - da + g_tolerances: + - 0.02 + - 0.02 + ion_charges: + - 1 + ion_mz_range: + - 250 + - 2000.0 + ion_series: by + peptide_length_range: + - 7 + - 25 + peptide_mz_range: + - 400 + - 2000 + precursor_charges: + - 2 + - 3 + run_allowed_fails: 5000 + run_debug_log_frequency: 50 + run_deconvolute_spectra: true + run_max_peaks: 20000 + run_max_peaks_per_spec: 5000 + run_max_peaks_per_window: 150 + run_min_correlation_score: 0.2 + run_min_intensity_ratio: 0.01 + run_min_peak_intensity: 100 + run_parallelism: 1 + run_scaling_limits: + - 0.001 + - 0.9 + run_scaling_ratio: 0.001 + run_window_size: 21 + scoring_score_function: Hyperscore + outs: + - path: results/tims/hela.diadem.csv + md5: 766ebf66ebdd31b0e3f183dfd070c53a + size: 9523125 + - path: results/tims/hela_log_score_histogram_peptide.png + md5: 2f06cdc3315a19ef9b85f1ba3d042efb + size: 18959 + - path: results/tims/hela_peptide_qval.png + md5: 5f65816667e975520669f8e30b733ec8 + size: 20114 + - path: results/tims/hela_score_histogram_peptide.png + md5: daee36c5016bd8da0fe5954ed8fbb4c3 + size: 20614 + - path: results/tims/hela_score_histogram_psm.png + md5: 4ddc3d4f219ba74e2b7954767ac55b37 + size: 17891 + tims_run_ecoli: + cmd: 'python src/run.py --config configs/tims.toml --fasta profiling_data/UP000000625_83333_crap.fasta + --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d --output results/tims/ecoli + --threads 4 && python src/plot_results.py results/tims --prefix ecoli ' + deps: + - path: profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf + md5: 764764d9da856aab25d562b4c8c835e2 + size: 17521478250 + - path: profiling_data/UP000000625_83333_crap.fasta + md5: 1b9a2db686eb2024edbd33b7f24117ef + size: 1891750 + - path: src/run.py + md5: 9342df61b2738cdf083da7ece1581b59 + size: 944 + - path: src/run.py + md5: 9342df61b2738cdf083da7ece1581b59 + size: 944 + params: + configs/tims.toml: + db_bucket_size: 32768 + db_enzyme: trypsin + db_max_missed_cleavages: 2 + g_ims_tolerance: 0.01 + g_ims_tolerance_unit: abs + g_tolerance_units: + - da + - da + g_tolerances: + - 0.02 + - 0.02 + ion_charges: + - 1 + ion_mz_range: + - 250 + - 2000.0 + ion_series: by + peptide_length_range: + - 7 + - 25 + peptide_mz_range: + - 400 + - 2000 + precursor_charges: + - 2 + - 3 + run_allowed_fails: 5000 + run_debug_log_frequency: 50 + run_deconvolute_spectra: true + run_max_peaks: 20000 + run_max_peaks_per_spec: 5000 + run_max_peaks_per_window: 150 + run_min_correlation_score: 0.2 + run_min_intensity_ratio: 0.01 + run_min_peak_intensity: 100 + run_parallelism: 1 + run_scaling_limits: + - 0.001 + - 0.9 + run_scaling_ratio: 0.001 + run_window_size: 21 + scoring_score_function: Hyperscore + outs: + - path: results/tims/ecoli.diadem.csv + md5: 7708c2c0d799763eb8481c5857573b3b + size: 39559784 + - path: results/tims/ecoli_log_score_histogram_peptide.png + md5: 93d29ba9829cdf9477f074c7bedb3b99 + size: 18936 + - path: results/tims/ecoli_peptide_qval.png + md5: 512cfec09b0fdc8faf632daebc5476b1 + size: 21246 + - path: results/tims/ecoli_score_histogram_peptide.png + md5: 310366668c75157baadb688eadd81518 + size: 20316 + - path: results/tims/ecoli_score_histogram_psm.png + md5: 88f05788e5257f329b7e0c5706daef1d + size: 17023 + get_data: + cmd: zsh src/get_data.zsh + deps: + - path: Dockerfile + md5: a5f9ec03e6ab364f6179b60079fba806 + size: 264 + - path: src/get_data.zsh + md5: 83ea92e422447d99c7d8c6508b5f0807 + size: 1896 + outs: + - path: profiling_data/230407_Chrom_60m_1ug_v2_01.mzML + md5: 19efb578f116318ce63f707dd0193ad9 + size: 1452019888 + - path: profiling_data/230426_Hela_01_S4-E5_1_662.hdf + md5: 02f5846145d04ff9301f78d2b6299b75 + size: 4412473566 + - path: profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf + md5: 764764d9da856aab25d562b4c8c835e2 + size: 17521478250 + - path: profiling_data/UP000000625_83333_crap.fasta + md5: 1b9a2db686eb2024edbd33b7f24117ef + size: 1891750 + - path: profiling_data/UP000005640_9606_crap.fasta + md5: 6a22e753f0e35eae9e0bf3d95759d089 + size: 13676378 diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index 6f4faf5..665794c 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -6,51 +6,80 @@ stages: - src/get_data.zsh - Dockerfile outs: - - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf + - profiling_data/230426_Hela_01_S4-E5_1_662.hdf + - profiling_data/230407_Chrom_60m_1ug_v2_01.mzML - profiling_data/UP000000625_83333_crap.fasta - profiling_data/UP000005640_9606_crap.fasta orbi_run: + params: + - configs/orbi.toml: deps: - src/run.py - - config/orbi.toml - profiling_data/UP000000625_83333_crap.fasta - - profiling_data/XXXXXXXXXX.raw + - profiling_data/UP000005640_9606_crap.fasta outs: - - results/orbi/ecoli.diadem.csv + - results/orbi/hela.diadem.csv cmd: >- - mkdir -p results/orbi + mkdir -p results/orbi && python src/run.py - --config config/orbi.toml - --fasta profiling_data/UP000000625_83333_crap.fasta - --ms_data profiling_data/xxxxxxxxxxxx.raw - --output results/orbi/ecoli - --threads 4 + --config configs/orbi.toml + --fasta profiling_data/UP000005640_9606_crap.fasta + --ms_data profiling_data/230407_Chrom_60m_1ug_v2_01.mzML + --output results/orbi/hela + --threads 4 && + python src/plot_results.py results/orbi --prefix hela - tims_run: + tims_run_hela: + params: + - configs/tims.toml: + deps: + - src/run.py + - profiling_data/UP000005640_9606_crap.fasta + - profiling_data/230426_Hela_01_S4-E5_1_662.hdf + outs: + - results/tims/hela.diadem.csv + - results/tims/hela_log_score_histogram_peptide.png + - results/tims/hela_score_histogram_peptide.png + - results/tims/hela_peptide_qval.png + - results/tims/hela_score_histogram_psm.png + cmd: >- + mkdir -p results/tims && + python src/run.py + --config configs/tims.toml + --fasta profiling_data/UP000005640_9606_crap.fasta + --ms_data profiling_data/230426_Hela_01_S4-E5_1_662.hdf + --output results/tims/hela + --threads 4 && + python src/plot_results.py results/tims --prefix hela + + tims_run_ecoli: + params: + - configs/tims.toml: deps: - src/run.py - - configs/tims.toml - profiling_data/UP000000625_83333_crap.fasta - - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + - profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf - src/run.py - - config/tims.toml outs: - results/tims/ecoli.diadem.csv + - results/tims/ecoli_log_score_histogram_peptide.png + - results/tims/ecoli_score_histogram_peptide.png + - results/tims/ecoli_peptide_qval.png + - results/tims/ecoli_score_histogram_psm.png cmd: >- python src/run.py - --config configs/tims.toml - --fasta profiling_data/UP000000625_83333_crap.fasta - --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d - --output results/tims/ecoli - --threads 4 + --config configs/tims.toml + --fasta profiling_data/UP000000625_83333_crap.fasta + --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + --output results/tims/ecoli + --threads 4 && + python src/plot_results.py results/tims --prefix ecoli + plots: - - orbi_qvals.csv: - y: [n_peptides] - x: q_val - - orbi_td.png - - tims_qvals.csv: - y: [n_peptides] - x: q_val - - tims_td.png + - results/tims/ecoli_log_score_histogram_peptide.png + - results/tims/ecoli_score_histogram_peptide.png + - results/tims/ecoli_peptide_qval.png + - results/tims/ecoli_score_histogram_psm.png diff --git a/profiling/src/get_data.bash b/profiling/src/get_data.zsh similarity index 70% rename from profiling/src/get_data.bash rename to profiling/src/get_data.zsh index 2519d3e..80784ac 100644 --- a/profiling/src/get_data.bash +++ b/profiling/src/get_data.zsh @@ -15,9 +15,6 @@ aws s3 cp --profile mfa s3://data-pipeline-metadata-bucket/contaminants.fasta ./ cat ./profiling_data/contaminants.fasta ./profiling_data/UP000000625_83333.fasta >> ./profiling_data/UP000000625_83333_crap.fasta cat ./profiling_data/contaminants.fasta ./profiling_data/UP000005640_9606.fasta >> ./profiling_data/UP000005640_9606_crap.fasta -# ./profiling_data/UP000000625_83333_crap.fasta -# ./profiling_data/UP000005640_9606_crap.fasta - # Raw Data # Ecoli # TimsTof @@ -25,13 +22,17 @@ curl ftp.pride.ebi.ac.uk/pride/data/archive/2022/02/PXD028735/LFQ_timsTOFPro_dia # Human # Orbi -aws s3 cp --profile mfa s3://tmp-jspp-diadem-assets/220119_hela_44m_1.mzML.gz +aws s3 cp --profile mfa s3://tmp-jspp-diadem-assets/230407_Chrom_60m_1ug_v2_01.mzML.gz ./profiling_data/. # TimsTof +aws s3 cp --profile mfa s3://tmp-jspp-diadem-assets/230426_Hela_01.d.tar ./profiling_data/. for i in ./profiling_data/*.zip ; do unzip $i -d profiling_data ; done +for i in ./profiling_data/*.tar ; do tar -xf $i -C profiling_data ; done +for i in ./profiling_data/*.gz ; do gunzip -d $i ; done # This is done in docker ... still waiting for the mann lab to check my PR docker build -t alphatims_docker . -docker run -rm -it -v ${PWD}/profiling_data/:/data/ alphatims export hdf LFQ_timsTOFPro_diaPASEF_Ecoli_01 +docker run --rm -it -v ${PWD}/profiling_data/:/data/ alphatims_docker export hdf /data/230426_Hela_01_S4-E5_1_662.d +docker run --rm -it -v ${PWD}/profiling_data/:/data/ alphatims_docker export hdf /data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d diff --git a/profiling/src/plot_results.py b/profiling/src/plot_results.py index 6aebc7e..27e91c3 100644 --- a/profiling/src/plot_results.py +++ b/profiling/src/plot_results.py @@ -4,51 +4,101 @@ import matplotlib.pyplot as plt import numpy as np import polars as pl +import vizta # First import vizta +from loguru import logger + +vizta.mpl.set_theme() parser = argparse.ArgumentParser() parser.add_argument("base_dir", type=str) +parser.add_argument( + "--prefix", + type=str, + help=( + "prefix to use to subset the parquet files, for instance 'hela' will only match" + " '{base_dir}/hela*.parquet'" + ), +) def main(): args = parser.parse_args() base_dir = args.base_dir + prefix = args.prefix # Find the data - parquet_matches = list(Path(base_dir).glob("*.parquet")) + parquet_matches = list(Path(base_dir).glob(f"{prefix}*.parquet")) peptide_matches = [x for x in parquet_matches if "peptides" in x.name] parquet_matches = [x for x in parquet_matches if "peptides" not in x.name] + logger.info(f"Found {len(parquet_matches)} parquet files") + logger.info(f"Found {len(peptide_matches)} peptide files") + logger.info(f"Found {len(parquet_matches)} parquet files") # Load the data assert len(parquet_matches) == 1 assert len(peptide_matches) == 1 parquet_path = parquet_matches[0] - peptide_matches[0] # Plot the data foo = pl.scan_parquet(parquet_path) df = foo.select(pl.col(["Score", "decoy", "peptide"])).collect() bins = np.histogram_bin_edges(df["Score"].to_numpy(), bins=100) - plt.hist(df.filter(pl.col("decoy"))["Score"], alpha=0.6, label="decoy", bins=bins) plt.hist( df.filter(pl.col("decoy").is_not())["Score"], alpha=0.6, label="target", bins=bins, + linewidth=0.01, + ) + plt.hist( + df.filter(pl.col("decoy"))["Score"], + alpha=0.6, + label="decoy", + bins=bins, + linewidth=0.01, ) plt.legend() - plt.savefig(Path(base_dir) / "score_histogram_psm.png") + plt.title(f"PSM Score Histogram\n{prefix}") + plt.savefig(Path(base_dir) / f"{prefix}_score_histogram_psm.png") + plt.clf() df = df.groupby("peptide").max() bins = np.histogram_bin_edges(df["Score"].to_numpy(), bins=100) - plt.hist(df.filter(pl.col("decoy"))["Score"], alpha=0.6, label="decoy", bins=bins) plt.hist( df.filter(pl.col("decoy").is_not())["Score"], alpha=0.6, label="target", bins=bins, + linewidth=0.01, + ) + plt.hist( + df.filter(pl.col("decoy"))["Score"], + alpha=0.6, + label="decoy", + bins=bins, + linewidth=0.01, ) plt.legend() - plt.savefig(Path(base_dir) / "score_histogram_peptide.png") + plt.title(f"Peptide Score Histogram\n{prefix}") + plt.savefig(Path(base_dir) / f"{prefix}_score_histogram_peptide.png") plt.yscale("log") - plt.savefig(Path(base_dir) / "log_score_histogram_peptide.png") + plt.title(f"Peptide Score Histogram (log scale)\n{prefix}") + plt.savefig(Path(base_dir) / f"{prefix}_log_score_histogram_peptide.png") + plt.clf() + + pep_parquet = pl.scan_parquet(peptide_matches[0]) + qvals = ( + pep_parquet.filter(pl.col("is_target") & (pl.col("mokapot q-value") < 0.05)) + .select(pl.col(["mokapot q-value"])) + .sort("mokapot q-value") + .collect() + ) + plt.plot(qvals, np.arange(len(qvals))) + plt.title(f"Peptide q-values\n{prefix}") + plt.savefig(Path(base_dir) / f"{prefix}_peptide_qval.png") + plt.clf() + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index aeec562..6042feb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,8 @@ profiling = [ "line_profiler", ] plot = [ - "matplotlib" + "matplotlib", + "vizta", ] dev = [ "ruff >= 0.0.253", From e9e67817d7e4e442e080f4c2c27ce961d5656401 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Thu, 11 May 2023 14:57:16 -0700 Subject: [PATCH 36/41] updated dvc run info --- profiling/.dvc/.gitignore | 3 +++ profiling/.dvc/config | 0 profiling/.dvcignore | 3 +++ profiling/.gitignore | 5 ++++- profiling/dvc.yaml | 10 +++++++++- .../tims/ecoli_log_score_histogram_peptide.png | Bin 0 -> 18936 bytes profiling/results/tims/ecoli_metrics.toml | 5 +++++ profiling/results/tims/ecoli_peptide_qval.png | Bin 0 -> 21246 bytes .../tims/ecoli_score_histogram_peptide.png | Bin 0 -> 20316 bytes .../results/tims/ecoli_score_histogram_psm.png | Bin 0 -> 17023 bytes .../tims/hela_log_score_histogram_peptide.png | Bin 0 -> 18959 bytes profiling/results/tims/hela_metrics.toml | 5 +++++ profiling/results/tims/hela_peptide_qval.png | Bin 0 -> 20114 bytes .../tims/hela_score_histogram_peptide.png | Bin 0 -> 20614 bytes .../results/tims/hela_score_histogram_psm.png | Bin 0 -> 17891 bytes profiling/src/plot_results.py | 14 ++++++++++++++ 16 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 profiling/.dvc/.gitignore create mode 100644 profiling/.dvc/config create mode 100644 profiling/.dvcignore create mode 100644 profiling/results/tims/ecoli_log_score_histogram_peptide.png create mode 100644 profiling/results/tims/ecoli_metrics.toml create mode 100644 profiling/results/tims/ecoli_peptide_qval.png create mode 100644 profiling/results/tims/ecoli_score_histogram_peptide.png create mode 100644 profiling/results/tims/ecoli_score_histogram_psm.png create mode 100644 profiling/results/tims/hela_log_score_histogram_peptide.png create mode 100644 profiling/results/tims/hela_metrics.toml create mode 100644 profiling/results/tims/hela_peptide_qval.png create mode 100644 profiling/results/tims/hela_score_histogram_peptide.png create mode 100644 profiling/results/tims/hela_score_histogram_psm.png diff --git a/profiling/.dvc/.gitignore b/profiling/.dvc/.gitignore new file mode 100644 index 0000000..528f30c --- /dev/null +++ b/profiling/.dvc/.gitignore @@ -0,0 +1,3 @@ +/config.local +/tmp +/cache diff --git a/profiling/.dvc/config b/profiling/.dvc/config new file mode 100644 index 0000000..e69de29 diff --git a/profiling/.dvcignore b/profiling/.dvcignore new file mode 100644 index 0000000..5197305 --- /dev/null +++ b/profiling/.dvcignore @@ -0,0 +1,3 @@ +# Add patterns of files dvc should ignore, which could improve +# the performance. Learn more at +# https://dvc.org/doc/user-guide/dvcignore diff --git a/profiling/.gitignore b/profiling/.gitignore index 1626de8..5e0e3bf 100644 --- a/profiling/.gitignore +++ b/profiling/.gitignore @@ -9,7 +9,10 @@ line_profile*.txt profile*.txt **mokapot*.txt -*.png profiling_data_bkp */*.parquet +*.parquet + +dvc_plots +bkp diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index 665794c..93b96e3 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -40,10 +40,14 @@ stages: - profiling_data/230426_Hela_01_S4-E5_1_662.hdf outs: - results/tims/hela.diadem.csv + plots: - results/tims/hela_log_score_histogram_peptide.png - results/tims/hela_score_histogram_peptide.png - results/tims/hela_peptide_qval.png - results/tims/hela_score_histogram_psm.png + metrics: + - results/tims/hela_metrics.toml: + cache: false cmd: >- mkdir -p results/tims && python src/run.py @@ -64,15 +68,19 @@ stages: - src/run.py outs: - results/tims/ecoli.diadem.csv + plots: - results/tims/ecoli_log_score_histogram_peptide.png - results/tims/ecoli_score_histogram_peptide.png - results/tims/ecoli_peptide_qval.png - results/tims/ecoli_score_histogram_psm.png + metrics: + - results/tims/ecoli_metrics.toml: + cache: false cmd: >- python src/run.py --config configs/tims.toml --fasta profiling_data/UP000000625_83333_crap.fasta - --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d + --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf --output results/tims/ecoli --threads 4 && python src/plot_results.py results/tims --prefix ecoli diff --git a/profiling/results/tims/ecoli_log_score_histogram_peptide.png b/profiling/results/tims/ecoli_log_score_histogram_peptide.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb564a1e01424f049002eac25e310d339367ff4 GIT binary patch literal 18936 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 B)cpVe literal 0 HcmV?d00001 diff --git a/profiling/results/tims/ecoli_metrics.toml b/profiling/results/tims/ecoli_metrics.toml new file mode 100644 index 0000000..6ed67e7 --- /dev/null +++ b/profiling/results/tims/ecoli_metrics.toml @@ -0,0 +1,5 @@ +NumPeptides = 7336 +AvgTargetScore = 22.988163949269012 +TargetQ95Score = 36.301172620664076 +AvgDecoyScore = 20.45939763445775 +DecoyQ95Score = 25.309875026034938 diff --git a/profiling/results/tims/ecoli_peptide_qval.png b/profiling/results/tims/ecoli_peptide_qval.png new file mode 100644 index 0000000000000000000000000000000000000000..3a7629be2ecf98ce518e98433fabe9b98feaeed9 GIT binary patch literal 21246 zcmdqJcT`i`yDq#C6-9xqQWOPLK$?I`uTc>dkWT1jBho@Ip#%bm8&DC@(3IXwLXjFs zC@LZX(g{ch>Am*=-;8Jf&bjA|bMF1!?>pZZcicZ5kz}o8t~u9w-sgSZ=bf)K)E?2% zveH5jM5pxlfffWE3xFW1(NibEJ7OJ!6W~S4T~Xg%+sVq^)7;e(QZ;vXws&&3w|#om z!_w8w*2z&sNJ8k=&8s%eIp=qE|31&|Q z=a1!#zB9;x;>@e%#W`PEO0` z59Fa+)IwC$JK94s<Ipwg65=wlzwk&Ku#$+lJ&w(M@{9eNvvG^Q#+vh#^mf%{^LH)uWKd#V+n7-c+XZ2j*9Enq99L`BxN|1y%^5qI42mA*!hZP=TU0>i5 zj(ozZrc+^TX99|YL;|40?&k_8J{M})yyUXoTQ9Vo`@tgWemC#okmv*JR_@u!-J|T* z1E=oRFb)Ap-u6l-S7q$p*;)mK{RYHNO3mI%Bx!G%I-+TAG5?)}X?E~t+6Gw5&F?>0 z#P(w5Nx2RJN?Q9pg2K|wb+<%Er;aGgYxCo!IYJ+%+^mN#IOhq|&)Mp&@IubWJQ2eN z*{#W?PnvyJ=kO~wq;|T5%XF&EyNC8Mac%XV_BrIOQnp0eju=utjgWW7H<#CQ6k=jy zV}my$qPe)Q$YUe)@_XMN9%&Bsiy`NJNL3#$T}NJWj`VOc$()1N9c}C%zLw!pBguru z%$JY7K(?{rXGX`;JOR~9XSl*eVQ(HF1($OvHalJ#*ed7HX z6T8I||3i2z+H-N(P9H1oZoAxh^iUys{!&}@URH^qgha1RM_H|Arnym_Rmr7b^NS

Kjtfr?E5{&B{@x<;c z;2~Ja?fB_M?voO7@Fx!o9XN>+HfoF7J&#u3qc=CVgZX;u?Mv!tzDdB-*VVIX>5oKIM|@Nv!MyR%=p|Ad07?#yeFBCwp` zhgLo+BmQ8UJho~@LXBtUfMp&hj>_`RgGZU-Pd!r#c{wNVcchCZkIWbemR9L zu5?_$GP7p|K>o2?Cp(ZP^2;#&TdlbwX6@Y{gpErJyR0!f>4qf#O}V~4DP>g^Pg@)- zdsb8QeNwu%40@*#A-CG@rF0_JUywQUK_86{Qm3^GML+x2Bk`mAkArbXy=6x>VZ#e- z2J>eAZNDP)X3KqU5KAvvZz*Ad>E>_%9rHLm7j;9k!ZyZuaFp@G3mE6}(19$zi9|HsI!nqo z(cVm-JG4D`6`39V;lrdWv-8Mv1x$wYIWcag;}Nx3j|WJHojE(pXU_=7G1dDFgl@&2 z@V8vvMmOjBGV9Eij+Xt@!G&(_K5{ZEjd+em&9&}VC&oVCW|PbbS*q^JoJwc1>&~|@ zs^0yjEwl(QAA1G0D-v++^K)fM_fn1hv2-3|`A4Kz?RzZ)-D6mR$?0jIK6&n32OKkE z02`DAlf8E)wxB)C;s&B)txa@pD%GDL98mn+hjJA!_O~bqmnA2X-QLGjcgtOfuP+lk z|7L}?`0u-YRwc8Wo-BNMt~mwv!}^#Be?OO56~W0hA2iBC)6cKQbbml^g zwD-NO&69n5BC7kog>6ShDi>d`)qdA1#4967eQMO{71l^aU0&3bL2fsJo=vI%`~voA zKrKDHRMuoNx}XDZT)5vbw3Jt9TK3winjB0yW=|7Yx-$;nTaA^A+a_OVRZL2s>(&CJ zovPV?&VO>Jd1OT0p}?kqFx~d+U|6i0h^vF^*{oe?n`?h(f#|+!*eq|9-Mz7KzpU^F zN{4NqPOnSoz`a3Lfysd9J_x^Ey#tVFW9-Zxa!(!GOQM;g7Sim;9=%DHQ#awXLz05S>5N6VQ z1VedtxK{6N-KU%LDNCDsz$?e8?L;04q0PIPA}>cI=X+E0P7ZEEcLF-6zTCOxZ+Yo$ zra;d{wtadLvYnU&<=@l(P4RE4s2@Lxu+IivnMYv{$HUQY7GX)inSgY7&{pWtzCxT#D%6EY`@eRXH3*_D2 zP@y*0(NXj9m?DOY@XKWqRXDLb2+>8G4WfDJYDc!+p?f%p)I!{XaziVjHi#Tox$y(; zWXAra$Vj^{ObcLV(} zpV{PWGHX~EKivT9f#XF7`y-cLvp9i(;<~z9LasEoA;#x`*LC(~JUdt;Sue3Gzmozn zdd{@Z#Hm(P8&A)%urvmJf_;$-0JQku(`ILes}%O^eDdFuD^5(V{kfMf z`(F$m3WGEpr)Tw@mt>Rl z`f3pQ;X~%Dh#wjc)?3$dJM`)Lw|Ytnt8!sRilG2MqI46ej%x@dN~3ihjzo0W+1Uy6 zTk(v#d^$J@`<}(|Y+gY*h4F?#e&SGppG0_1aEqDU&y*U1=Z2ti+xPBiGe?rJ%RVC< zA9J=&<0!ttrYpt?xgXY>s~8zS}J|n7Iw8h7%XsQ~&lO~qkmeloJ;ZgThY{AVKl_PDhote|{LPJF+ z>SMjcUom+uTxI5HB1gC{2kU zn^LhCM}>()sYw6^iiNOhf@tD6&*bD}GfPQhk8o==XmZt~`z#=iGIZ^e;9 zJysx%V-+-%FQ1&uwA5R79Y)(Q4l_yKZb14zZ&72bDV)LY&YRqz`z9?J;xc?4t zoP^y1v6AT;K@ctLw`R1?ia-Is>Mz}eCOd!RZrgSBIFY7c3KaMw<=l(HV6@A|r-uw{ zz2h;zYI_BH8M=Kq{PIp~Th9>LU^wjo-KHN+J$u4<+lB9!MX#1;7t9Ws3`v)h>eO3$ z05v96xxDE?7bzy^4X3Pn4mVx+1T}FTyBgri2M9YFzr_g<^NQfUeWEFPKaW4?IL_rs zsg;~-ZAPF-5Ki7pq0729E2vr5X0{25Wjvo3?ogo96}-vKRMj%xb>yQO@dKq93Ny{b z!}PO2)ZX}E!GQSn^W9Ryb%<=i{w-P1k^wxMu0h~_|L0R%=DpIr6MkSw?kxCNVq##OJHB${#Xax=7M_1yN%?0@ zM++zTRev}@t|j)s0hfQliT^KI_J2&w-_#FL13Y@mRd;LSb2xRszqsnIM+m>ZAfeiU zI_ShsrH{=dJq2qRi5r8m+*ww$Ar{tvjJmEb7Wc9BdoLt-BtMBs#>c8zPcZfu`bA%* zbJXpn;}ja9A07B*PW+5=Xx7s;ttoCj!jG1i7?k^3PBW1u-@ukj z9%`KETc9t?H&N6LjrF~mR4jA0EMHrZ4trdnG%&*VrfEG)Dv}cj5Px3h(JCpuv6SPw zwop7d5N+qlyG-_X*!7s47>JIv?)7}sO2#ec8s(-P%Nxed%;$I8$4fs9w$mt)rY+P@ zeQ6cw{{n~_cco(A$7IHeAEk-;gde0Ir1R5Y)zeSGSITn&N!MXcq5S&QwDLYi6V_#t z_+6AwV^>}JOY4BNhd?7qxgwdElrOxM++GxQlTE3yy^RO+Y;&!<@6DCIurIwuaftA^B7-YU zsDh2$(`xwc95(+`|MuADdKt+m6lD&TA3N5VT&nbzj>F`ZUWvcsu-o*bMLR~YcC@YB z*Fi}#cjdKTQ;AHJ9bQ0q9(W}2SA&ST6myhcU4wy7Hs zftRfi4PB?KE8}fqUHf54u5xSf?52Z{J1?KfhjQP^J~n^bqq@dR{<}m}LwEP5tTIv_ ziW~lM&5q5V&R-jXc37?ir@DQRQf_YSsu9UW*{%yqe~3Xhq8OA8bwlsi?;=bDo-pMx zpU^+Up0a{R;Q~o%zC?x1FM$(aUhuK8dCcm&UI$y&xY$@Q*Nh9(+O8KAD}R+kKY>!Q z=Q`lYGPL!%YFf-*1E$m&zbxF57dz7aZk(t4Dxn}t{X>dhHKDRQJ1f)fOZO$inmIQB zkT_3#ILiB*4jgA#pXoT@DI>bt@qxkwv09awZ^IrhZVaJmvcFP6vC)>zy`9PQ{8BP{ zAHcm_I>|Rqo*Yq#oSl?7pn_o*G|QE_K~`rN*)~Hick5C>=pN^{Ssc?DQaSw0v|n!oYoXcp zH@hW~u8H~i^pNDZ{GaCOrRak@1~*S=4<eZn%_ZD7Aa4At@e{{=byRrE;8N2@NO4HBJnaU6y zzDk{dXzA?B_u4InIOXc$!!a4v`xm-DqVt$r;`39f_a9>@Qjwj6Lr0BMPZOd{8IvWu zn_%iQO`S+a#{2r_9obZ2qCTR>VacLU%GU+C=roJm50pEk)xx@L+a0@=tQs96D@4#~ z;@I`}v3(r+7{0a*41n%3CNh$CtPdVxDZHcXP{kTD zt~M_L+5Y5Ne~hTThFOc(DiU3d&ISps_OX)V5Lp|b5CzqehE@k6>c-akJPsY0TVcsg zu*i4$^)eTPy7ryM3ezI+(hq{0dw+3rgvOGq1ZfLrvSY;fbE%rMQxbT^sGP?-nbrz5 z?ol6hDPR~`%vSun-=p3MA?fBHHCi=yedc_`q%ExMvU(cvhY7wN?F-)LbM(K*@!^93 z&=HF?%x-U9pBx)Yt=KsZU?J*hgZ3TLa#)#Sk6HG2EbYdoZcSZ#^totKo?#9m==k@D zY#a0QnZKM+Aaj!^yO-@9WfREA2~W+eK2Yq=@u<=RQQl3ihb5c$iF%(Si*<$7y^U!5^G!|X3~GY`*i3xu zqsmmsJr$7bUj(;qy)heXSLm{>0YThcf(=;!jyqk~OU1@A&MGvuHun-o8q8di=ZzjB zT-%trB_5|F@H2twEM~$ovgQp>bNqTs+d8VGg+LLo!68xuk=nGaQ$fMU6ARCDc3gNB zwD&|!)vd=^QsI5v)LwT{i;8FwL(M}6KN zV6}I<(EMITjO;Vn5Dx$J%Zu&;wF>D+E~FE@w2lk`Ku_88BApx(&8V%au1;KJS?U$2 zXjsPkZ2$Vj@Ex(K`)u))^$#(&g*~tV*@Ya*E6NgB5BuO z=9oJ?ByBrUt-Uc=+SnSD5-)~G`FwzqnrFW#p?pT(FcpX&Hh?vj+HK3rtvC6qCg1sN zX?qb05CH6ZG~@u~*j1p?vmgbp$VVu`KzN$#t%xB!)YDsRhk|rQNtSnQM|+_62^g%{ zWeGcSBHnZZX)ZFrns zFFne8N@v>WE*GD4ae0rRx5@?f52^BX%Ni7QH{Lx0BmxEt>&F8+f0$nc!MCe*e7ba^ z;dmcg@SWD$rJG$bh1#iRW%%}H2&SHCJM-bt^fPUA`RpMVna@w643ga*jq>PfTU+1KN*Z$ME1Q3Grk|4rs`( zks$WZvYbG?B7L34kTR1P*5M65%?|Vadnn2uP^>HrCJ_e~uBmB%@s3@Lpqbs)6=Z>V z>_Lcz$MgEG^wERjm_W}KtEs8x<}QT}^XF@2UX9b_w&kOR@x@uPulY#eHi@|^y9a!V zR1g3UcG^`l0rl2nyyw8TTPA_Kc>Sjox8-ETUxX({HR2001#fBPmD^gnKD*8O=ho^K z&Zp!wJugnEtup-Mr|fBKsEH0_Mt2%wob-YP&G`B%4R`9O01}T{&V$8aM&Q`aYdF8! zBwO~->W1D-N+M(;ivyf)BeFIE4{%w3 z>Ns?G4P5>8cL~ur3L44bM~fHBUu190H`dmAxzw%v>5cNT@r}KKfD7bZd`Jrcon)tB zZGoOU%hGo3^>XR7PLrh_`eqXCN0dN&8y28E7zKh&(|}5Tm~tQJJ`O;27lOOG4Rc8r zdJiO+PnMU0Qzz`>YhbPq2k4Wc>fZV(J?Tu%coW(qah5^13z15@QuMvKOFJw zpIJSS4T@que)rRaT80(Blz^U(S|;y^xg@xAzV*7$=Ke|Q`!U$Ow0y1ctvm7sYuqqi z%z6b%ISYn5`7>y9t=N9I-*4khc(#=^%VV{+#;$KW7h;EwBF2O>0wW4?43rka& z7=mm<`LJf~nGzGkt9>fquGHIjr(WS#jeBCZm0%!kXrWPS`Zc?XAGS1t-F*DY)?n@m zT(yS6u6=SJzO5yYa0P#w;mCPpH!5iI#8DcxS`BP;y~?o;E~*EHHLZ;uAs>EG;8kN~4;!TO+^ybzUqD#QPU?szOOE$;6DC zi(imOy>izB?Xy!V&LO#r-+SLr%;ewmY95)({{~lJoUj(m>No~9T{w1C<10Y8)?FUk zlJ$kA#x~&{rzDd;@7<=xlKE$-n52Qy`Qn4pLTyEi4(CrZkX`W?YbnA%8Z!PVs4vkr zf*3=WLjnHar^zZ{%)GY+Row7P9jO`alT$2ynSIH#0IB7f)xCYZX9-hCrVH%G8`LMD zQQ2cxS0uPBr)EHo9PRCA8x7J#TfL`aG`EBR4mI^YeFqX{-|FfKyH!jwUJ=Y2CIdXb ztX-+8bWAt+_=8kA;SB6GII z@F+dz(D!?$-1lDW6QiV)Vz~6%Y^;1=+nO-^z(;TuN*i>&7;HtpMy~dElQ8u;3Eh%A zcC{%{d)9ygTh-g>DwX}Tb3>#Q<&QomPe6D1?t@V{-{jUdTT<^X>V6{F?~>BK6@+)^ zBvV1o>{NeDewknzneB~i6mn?6hRl_VZ<6?mSs7e`ydrb_ZWJH5W{Hv1HV=*tLWM0z zeV0NcK|(M3_08{Rp-+=ug!SeTlhP-GM7p<5EjO@(qx_T_dU1(c!hl}hP|lP|;XBIv zBU!P-rWK_c9OCh+46@=XK;Mq_Uz{TB5sb<_{qLs|Dt4$Te@vPYE3x8;LLfoMv{(uB z?55gWQEKe&udq;VDPp4ak2svBt@d^Bsdhp05i;vEJ?olmuvSP;v`kBKP35+uaR0@9r8x!p;b(If#HNbajf+;CH?NNMwW5S{(KdQbyJk6 z-rGoLMK|A4($aE$jVPPJK3d|)R`MLVrWCjO#aK02PVbQ)`F`rP&zKDFWx>v$N?q;~ z4Z=Frh#gqmBcPsPGX7e;eAIEs`{UMGbcM)05ec%6%|JzXm?+ld&EgclNl?{G6d@~hf|~S%vt+4bSI_l?ONT%Wjsj8XXkWfYV<(>3C$lD5@-0}o zyXRa~Ao5g5a(AgYT5fj`hwc2BpGUfkQ%E-7+X@LZBh%2cX_}fQJ)Kh|EcAXfjpE`H zsu^XktvEPMktgm4zMv(U~Tv+Va0~{S26ao%diF4sK@CZpI%J*ZJ3VpNs%npCq z`#U_QS@iw$l4YW04b#n?sNhUvny1ufwZFr@D zbcVQaSW&f5NaLpo+0)@o${zg@mwq<`*-_9!m!k*)mOAs`fY;^3; z$IdChkt0)=tmC|F+6;=pcE(kpJ}jU_sc!cPOL6C?Y>~M~-i}_)%(7&hgGq6d4BW={ z4}bR7&k0SrO1v~(TDU5-TrcdenI$-~TZzoS)k3U^y!_}m6yROK4T(EysDnkQoGDKj zG*%?#asv-XeLYW(=l00`dxN{12SS6FFJ55F)wXWs2j4v@{|ThyvHLU@X`G4srf=h7 z3chMvGrAqt+>Zj)B+;ol!PXUtY9|3jJ$kKYlV4)jsGz&6&drIN2Ph<|GJ_G=m+&Qv zphD(Bt}@aSce>8Ofg>Po(j#>9T>6|=8*tqp{i5c&v3yE%r2KSQIk__TdP8efuqHqx zhok+r|8fDjo#D}BudSJsR>W?uO9yCHL1gGOiCXlrXC&k(0Xz5A^y@ z4F#N^h!F{(-Z#$g5saTByxX?fi`t)N?b2Nj>k+)g7*~2JGwuJ~SJ=EiMHUX75liO}aUC7P_nCrUGqjY;o3~M*oK*E+0=yO8irq3f!*v4Nc!!P0h60#z@{!mSJ zHG9=MkVns*IUnZWVpukvQ?ghiq8ZY_ z-1(LnDKg8)FftDGV)ha-z~c9#Er`qPca{?BIx};0VEy zE7JK#LVa2}a@T*Fb?ukycFmFa%?bRPnO5g+l$&d&G)d8~h@w<%0M^CVhJGH7n zR2Cwj@AqaA#+qi%vxA@D5oIS!3?PJslVQ7#^*y2wwvRf0_`B&nu@B7|ZP zbV{i+cO&oqn3Fs4;sM@*Qz8sF2^m?AUFA|T5GQgq&Q0d_hIoJsAT@6Upr@1~Bw9)G z37^}{_i>k$QouZ0-Ove*iyo8(1Vba?RKSh0ZJpOvjyp;)cGz_1aG{If?{AD^JeH!A z-|yDVO_uA(jz)FkgkS>x++du`I}T1-9wFD1Sb=MoBHXK2z{#TZ3vkDB3LDhi*;-1;?;@I*0l2 zqZ!c0m9#((vYBIMVov2pAl##qhyXJeC3@^nDNs=9S(Gbi6sQp>UF@GiA|2KVzt@JB z?o`0!_^H820{>dqzU~yr?kilPfW2}E9QzdZ<Dq-I4tqm}`Mk z0hT)VwFql$!M=i~6y;wKq3f~z7J}|xx(CJ)2WKFu^p<;W-=e(pYjz0?5K4IliopM| z5C?*&@AM1HwNp5=8SAz2wu$msZ;Q-u>P^;%H2NoVVL3;2L>HOkNtP3zV2OM;1UXF#ACp>xLb z(7R(TnqcweXk!I^1m9M3CL#T3sI4?C-{(p*nbzTa@QUIa|$5stYfmJ5Awpq4+h5#8gr6N5s|^C|(k zSdVU?0nyilWR?CH!8Q9qZwjYn^AQO0f;bYEJ#k?EM<17J@M83h=W(gl+)`IAs2yaXgppelq0g{&)`^4VI3VXs05W8Xk7zAV!W6?EO6W^vz|`^tAsYZKs_ zTidC#YiI3=&>B1pl)ocV;=bYaG_?lspC(Xd-!-vR0^$I({mAl?IT-StCc)SMTu_K@ z|BLOM7N!=88cwP6A6ZqUjK9hjweCq*R#T1SIze-iu)uQOctwg&3XX|Nh%?N22*}g1 zL@_?w>3v%4jFv?isMxfrD>d@Pve^N`7(3|kuT-Xf>;)SQP!XOfX$3@hzVZi%z&kK_ z9`f;A&KMq&k%Dc{^-%0kadO*ia=s(}L$@>TjE`2jFG_J7a@hhLbi6_bP6<_RAqIrd zza&vC6deA2>2Fg4OHjG9vshO4sXQe$KFN3%*7ICSw5MtRMWqHq|68Sw8Pl>T>5Ga^ z%x`}?+8i!mzW+_dkvQ1*`IqyGG2vwGX65>Njt3n`W@{fbfN5{4(c1mvd(i&op_jiY za0mlu$m=||vAhA|(VwZCpX20;CVwdX4@GfnEk7i<)q8pSasPR(tS1n?JIWxs}d+ri$}! zfnyu{z|5{NR7|sfy2eSW);1Dz&^N@x`mrFKJF)kI|Hcc1K;O54c9GxsFyy6?W&-*oMfyX6?g>|1)j?vZ-? z&vz0wy-R4+RP=6~yk%f>=dDaQ09d`6J$U#!kF;IL$qLWA0BD{7^6@5@C0ByQH^br; z&!n@Y4h&mh>a}J*gl=eQjT?Kh&nF;YMA3*p2)Agzn0YMdzs*#(Uw4UEzi!t4SFVUC z>X**s^Z*HGM^i29hFurDieY3kFq)64$~+BBHOXV1ym?wVS*nCb7aGEE7D*jl4!&l+ zl$aJ|k z_lcqcV%wn94G{9b^ani2^=7&vwA3&@z0nz4Tj6sN9DQZ%?4Nx2jqDfw-sA)>zN)lj za^5o!unx;_ZzxMO>@uY|RoqcNT5NyJtbPX!X~*;N1{R2eH^1U^ z->t4~IB$bz>8_L(|FO{vZM9gkuTSchVzb^{`01mrP3nD{&9q%aFE;aJ*u`w8@}n6o z&x4#*!|i(J7PM6y^AEJH55pCmg^!v|!28&n0yLyR60ZA~J`p)k_bnXRvM3(p z{tNVM9RLx;fc0Nx|KoFX*%+=cI^GT>(!(DOKHqyzAlT7ZtAYop%%)QV*W*8HZ$zoT ze%p#7?HRRS7SAcv((2mS+)Av!Ek=n`jrmk7>FT||pW=x_BCSJcsQO#3`tDG7TPI-s z7dJ`*X3x1iI}iqeCoO31*A~mhJK$tXZ@6$~6O>f7z`L!>;8D3njW;>g+SJtZ*CVT$ z+z=k#k5BKWE;i=cVlqZQ-HqLPI~v%g)R;RxuXTp>SnIJyO34lGMPIXfOeTmtT^#?; zE+3!NBxd}LJ?Q{55YDcy^B@cnJ4g@iF4oG$v!jtm&uo!GNBu43-(ROXV^r`Mq z#C&D%aOaw*sV2CQxcE3lns-Ouym=CIoGF2%qN=V3@|x+IM!pl)>p)%LQo45Uo;2z@ zFwJviXGz%!C0u)=+U%TL)Mww+)T(Rqc!j$m4ph&%@g^XVrBH{-7|3ho`|Un^?>V2k znfn6-x+(*Q0{em>x8z9oG4|S#rJ>MJ*i{_K=v^5n7t>$^-fvy}Qt+572QY>O#);<% zE3Iu8ro3b&(GJbyJ&*2P3f7z%z6)l4?FR_AMxwSksvJkj&20-jS&uPO^7|F0iJR}Q z|68pw7R~biGAu}$P}a9z&>DL2;uxt+AwJZRSnO?K(ot2!HCv?QIsfhU1tvJpeTqGd zVhR5T_HQm4;&qlC6GhjC`B0Ai&vy6rX2!Cua0cloSqBSaOO~Fyq?eASGqK8}0>6b< z-2)8^T_S})dN=qk7{>$yoZwv^todVO@+`-ox`9Do+-|3Ai?3}G@B|=siG?{s9IZbE zla2y!JV{LLxRA{c8!0d~k(rtNplw-17O$T9-fxJX5LESg7EEbhF^|44=Flk@C+YD_ zN&O7q<@vI2idXIoYR!F7xEkxDF_Em?Ql2}X5RJ)L=AnMak80djslB*SQOoMc z4)<-Py5XVGe}VEQL8@DP+;GZWU)d&GP{vV{dD_CUi{d-Hh94Qv>-$>}T{JA~o>N>$ z9((FLUZ?D>BrdWx%#iwg6?7r|NK52{LGJy*``PU>z{pQk6XMFHk=iNczjR%b)VyG`V ziIU#RBwX(OMrdOVdmC)8TZg zQp0o;EVW694tQZL@p0r*jRHn=JCPwOCMk7txhAds?Vn0oHF9PaJgQw$``>aPfG2~Kt!PQuz>LOH{Le`)Dg}ZD#iiP2*Y)d?ZYFOu%7@VoUM$&w_85fKA%XA zY;{rx8nv@V{r!XB<1|usv~!)bo5&(U=&OGR&O;nvn58cBmp;Kcs#1aAI3G5h+?}*@ z+{CTxB4{x1q(2P~7<2;%ScO?|DN%4`K1P+4HUJ48Ex3sx{t#3OiV0FuQ;OsYN~)c> zeEtfs1i!g|a3ecI8vN+7GrxaS>1WL1nE4BJi1FaA=lo?EOTUeX;1~wnOxZUxt6)pT&RYmVtuE=XCf3s@<7Y(A~ zpq)hq80Z&L2gp4!ZUM*BJ8X2&_KF=*csQ^J<*Mb!VKPjIq?@|Bv{m%-kUd=48Hgbk zBT}|Ci?z{zAq+lE0%Gdj*mw%%5$NnTj5ovloKC3^W0Xw8Mx3u!`*x?%M?HF zt-GG#7pL+ryUjaS%O0WWsx0%e-f*OY{A}Y>X||Zv*NONJKd!}<~w^syNxAG6+V?V-d3jgx*)J3VUzD2n$q@Eac|@JR>ch{j^n$TcR8D30VF-dU zpNE)V3?|ZO>Y8|+ns8-!O}cUZXSe81sulNT-_G2b%*M7*NYX3~#dL z8{=~|%yMmK2?xV`l;%^W`OCNne3H$L$ zkZNVO(g{bYF~o}-2n;9VezGSb8~!?)UA3N;wEKrCvX@~YofJBS1BI;L(n{Z9x(euO zVJ$?O*>?Imc)-svbTyqQ1YRD_jqiCZn}ax~ z=i#E(QYWAjdYSEi z3dTto>EhsW()ivFB}#Gf`(i_Kb5;QNRkpzOLly6gTA@REvPpZfEoWw?HLr+(%IT1B zn~~=)Sf#J_WLo|FRMWpg>IE@J38U-uGjBBIuE|i@i;}E+?iq%FdBNvv-ODMwncBjq z32`7eLm4yZ3fn{Nx27clvG;DSE`t47-`XMrR#VUz^b#<#Bwtl}oy#(-)deaC_T{o{ zvaUM7ZkU_`$T<*9G;@Uh6I0&14|+#Q@OW&-dqdL7|8D1|fyP|A_W=j%L1w z8OzrGc=dBI(8)wldjgtIa5zEF=huZ&tJQd{W$`LrMJI^yC49?7Q_3}$)9 zsz^>+0MLia;!bWo3o_0cGN5%aImsl!0emnYDK;M+8#~=eso+suc2~4Pw!m99=z7{~ zA)^6^mpA0LcbbB?X>wbgNPh||LQgNLaHO%00R8d#$tGK*>2Z~)_#wC zBdjN0jn#XZQBK04*cXl7SkW#a-tRbvqU+ZNy3;uA?1nIS(Xn@z=0wQ!L2z19snWI$ z=&`7m(WKD*?{%X=6-PgVLhcG}r+kO4?)AvU5gj6eOLK61dTy^+p@1|Ue>2r*s`RlvqRej7Y`Y3RNvtU?M#bmlHA&JX+gzna6(a)WOB z?y7k)!%9c()Hu+5G~~C;7}R5z{~BpHM;KVr{ngkNG*^zsD=a<@X#%Zpp!E)GAlwpZ zeo;~Gce32#K(*3ay}GxsF3f0xFJ~2dS!2_ZHYxBMbN8O3OHm!ZWp*U37Z2ZbNeuYA z=Mw$8z4G^;hEBV?AOY#K&2h6d z$qWbrPt(rqt^BPAY$DBTVZ)aJ>Cs4v?uRZKWXH@sB9Soay%nXxfVH9A5!1eL66KGq zxdHF2n{sH4L(|YBrVEDVx=KPz1i=UBVykM{QSoR9;Hywl#!QFoc^WxD@nEnroh86*RYtGB!^Ex|m- zuDxxR<6Tp)szHLH=s@~4!8t?|$vEjGl)}r@3xt4-?qO;$&jG*g{a=B0Pne+bUko#I z^N!KL^+ZYRZ>ahhipRGM)d3-nR}<{jl9BIm#Xm&BIBQ41p3gnt(u;U=Ts@ z@d7sr_cu{0=Oq~qN^5EJ2wL&C<;T~j=9^kv7TA&@#cF2*!R4ut&F z&-Y>nh5zx)efGDYSn={l)3=b`w}9b~QLgLwr}s0D>lf9XZB_ox_;Oh$vouW-fJYEi zVhF!=`h+`RNPsJs9X1z&65Eu$(OM(I8->E^;pu<7VeU5GQH8F6B#V;Ud@}lm7|Iev z`HX4F-CDE_yL9`F7^s^DRJKia^!o&%a6^L>dXK=~p1@B-_DFz8^> zWEK|oWT?xRq}~zU(MLa9#C9BuXVT1)(yS5`}~ac`3vPv?(2`6VByHdG5y#p;AK>j^0GPxY_tEwVD(R)1hL~8 z*gW9BjMVj4g$@G>YL;|&dmRg~Vf}WUrP-DxeTVKaP;XeT?}g(OS#}jg;l!V*l&vFz z=F}2oGwqzJRX|pxV^iJ3D(3eZVA^Z*8<}P1#NkcYtL!BYe$JzSjBPA>vp zxU>NPeP9nGrY?JB{IA39y<;JDsRBM{F<_e?plHQiPq^2o3C;wq<4v4-eyPde2ZWOv zqi9aP)&XuhO4EG$x|qa8J8^q)3hg+@=pA(1vD3nzunfwztUYsKZ@{(#&H6`^x)$v( ziynfK#~W%I1{phh@%WAmZL@Tx?3!=ccH$R9WAa52;`7fo*!&*3sNr<@mn^E+r_KJg zW4<(^dU&OfRCUo>o4~YPRI^l043n>WcxhS4(8|7lE$sGqP$i`ef4qmtezVm!TDcyb z*Xu}bs+j+5`z_56^rv=-+3#%)TDiGp1}MAP z)CtIblT7kj!|YpHPhWhFG-73tTh05ulMY3IS{wHHA$d!d1Km43^eeYxf^-w4mRvP?Q!UT z3dUtIkB5;5#$x%nk$G)kub;nq>>R4fMmf7qQch(#HVXE_5egrRRWUv$^c1w!>$V9k zGP0guu)i#w5&?E)0$+n_JSa!kD%j3mqNI?0R%M1_70V>h$ksfvd6Y>BgrJ9|wH!zE z(6D*@Q6=#b<95;3#>5dj3;0igF<)tWjbOYuNb;3sky$o3G#`SuYu+nQ*EU-ft1N+W zaiG?ukn`;if$GlJxf;v8wBr-jJT75;E_5I{<^4#I2xuOo)d9sQAkS88_^qDCWV}ad z3M~RbYW924g{Ib4m!A4=x5W|ACr?qf5S$hxf3h|aENBNa=&&HbEex*n(4r*Xbv4>XM1jul6vJo;ZKO0E@WXB?VtU-Ap7 z`eLC|V!$J}^7H1QT3h!lrBE9RoSFn^S)1O=z=+<$LecRWZJ}m za!hh^p}5~>SQ2_um0}|mU}+Ut+l```+VhtQ&=Ll*?1d9i1!>pYy}`F#=_5JW6E7^E z8P)Ah(F;pV{S@R1iEyxiRK};*R7>oJmA8)$7?5il2C6mIrNj`er84`T$Aa-sXbNBjR-(D7Son=^qq9=Q7VDsTzjg#Goci)K1V1_d481ssjI z6>~GjJnnk@eErI3-~mM@@An>cHSRQ8WYJ&h=C*8^Pkr3~*#=?T_Sh_4vdnV&Yv5#+ z+1*RA?|yu*deLAvX^DK@udnibyK0mf#5RJfzw*7}`lh}o%ly8rX{uW$bz z-Ur+$w<>h=uFsczefA#u{tLL@VlO+_ed&mMz>Ib*-|JC?wG7`~i)VFzKU`iPwKroQ zaP_J7A;~GSz(gt=w{%lh*y>e(+4=u>Pn>Rhv~taPHPzQKL0zEkmeZnWO_g0!XO+Lz z_mnEU-V9u!9WHo&S;^!3uP?PTL>vL_f%~PuKg=s@*5+K`z}VB5FINIPCox6)zpY&# zkZ_bi_vc-vg}_w}QQvMp2R3g{zyI}q(fqpJ;6IJd=eF_A-u@D35hS;P-PvDVFe_}$ z?G^s_s(pQ>O*MXbP7wq;5>(;w$y+ZxaZB)j9aE|YuzFY(dT7hb$N!6e{!F;{dD0S4 z>lZlYsktn2svl6p-i)7KoHkk(oYwrl)*;v~HsZ}D@Bh-hTc!6KfzI?0lqpSJ zcvNpU|E`-ISyvA)%{|6l%nLfZMEu#lx-C%$xNLOelgU?)O_=yy0(dHjg}pp5Vc&QjJ`0$z=l%P$8gz0O=zNaT zO6!2Vx68nxYW<0p)5L(8==~gvs}&W$J|FvZ{+c9krTMLjw{KhBN?y-%>s=<>yZQMf zuav%}uAYCSKUK5y%bUKnY5;1=modIrQF5*OnELAL(%I&9sWujGGY?1YooM)B+W%=M z{+v=`N-(^!*ZkfK!yjUxBVIsfnmzvKo-Va>=Q`~-pxg!i`SX_QJN(M+zxH@uJQ%v z<0mVPkJJ5@sLr`aQ0_AYWKlaXVTumy>qk+pt7#KWV{an^L HB{Ts5)0xkv literal 0 HcmV?d00001 diff --git a/profiling/results/tims/ecoli_score_histogram_peptide.png b/profiling/results/tims/ecoli_score_histogram_peptide.png new file mode 100644 index 0000000000000000000000000000000000000000..c120928f122febd5d26b61a401769dcc744385c0 GIT binary patch literal 20316 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 VWMSGC07Hktl{D_;-@gCs{{Yt4e{ui- literal 0 HcmV?d00001 diff --git a/profiling/results/tims/ecoli_score_histogram_psm.png b/profiling/results/tims/ecoli_score_histogram_psm.png new file mode 100644 index 0000000000000000000000000000000000000000..6314d7cc8d5cedf090ea87f4e699c565f7533efb GIT binary patch literal 17023 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 ZF>$RcwEZ74h``Vxn3&x2yr(bT{~sn4wYC5N literal 0 HcmV?d00001 diff --git a/profiling/results/tims/hela_log_score_histogram_peptide.png b/profiling/results/tims/hela_log_score_histogram_peptide.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2d4664997f2bf4a77c9b7cdf495e3658146421 GIT binary patch literal 18959 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 GFaHmCn6kzI literal 0 HcmV?d00001 diff --git a/profiling/results/tims/hela_metrics.toml b/profiling/results/tims/hela_metrics.toml new file mode 100644 index 0000000..55c93b1 --- /dev/null +++ b/profiling/results/tims/hela_metrics.toml @@ -0,0 +1,5 @@ +NumPeptides = 2922 +AvgTargetScore = 23.35560315415351 +TargetQ95Score = 33.983086669309074 +AvgDecoyScore = 20.88152948080362 +DecoyQ95Score = 26.329978361311163 diff --git a/profiling/results/tims/hela_peptide_qval.png b/profiling/results/tims/hela_peptide_qval.png new file mode 100644 index 0000000000000000000000000000000000000000..42c910d0215c5f158d0ae5f5bedee07e96c661ce GIT binary patch literal 20114 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 literal 0 HcmV?d00001 diff --git a/profiling/results/tims/hela_score_histogram_peptide.png b/profiling/results/tims/hela_score_histogram_peptide.png new file mode 100644 index 0000000000000000000000000000000000000000..85fbfc73307d89002d2ed9aea2ca8ab16448875e GIT binary patch literal 20614 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ⅇ z=IY#_<+RA@OY1tb14q%yGY~)@Hyce{YtOLfVHn7}#?39brvg_pIT)iPhL$HW_X5vj z&{nHziJfQ*5g;ZzGIWF+cRZH*DSJxhSJ37lZiMg>G@iQpX-gS-8B*cgl?chMPovlX2P2kGPu zl&s603)j*$%c5P~|Adyhpr3_vTgyZv^2oO*Z=N==gtD8z|F2KwbMgCcB-viG7So+M znWWpYliM|NsJu-t(RKscQS=JZ$>c)b85@sI&BYHFfNBjkzR>Z?oj+HEwe!eJvWp+$ zS%ihPxd*pQ5uY3O&yXu#-y{RZA{6LT^~{b`2zX`{W*V3!r6Z?8MTd^BBURa_&mpW6 z;}N=C^j%%0nIerd<)Wg1W}i}9B^Voz&5pequRKTT9Y0>U6?hDVI~;yTebU&ehEl}H zDHLqX8A8Kk&;yX{I;!L6+FDTa;m4)od~+E zkwF)gRJcn-c9CwKv(A91`km(SPj4XH?lDS*?D(!t9h%i z`)A!dhuMbz5iRP*_nkqY#>r{^qjowSbHiXi45m;1U*ZR>Vxv6OGLkDWZbLtzdIP-m zcioJ|2lxBvf4|p)EUZKL_(89EcwbfZ2&xkKya(>6?6>>}Q6{`QMIY+w+yH|ZO+bm| zSuC`jR0!avHSqr%+=w1N=2IQ@=U8-~isZK{Pi;}Z?te|?T9orzbDp=4bUna_T-pC0 zK-r5%@~S(pQrg#6E~8Z$H`!yXc)nEnEG?+IRrCsBd1kKahb@yJlO&l@QMs$bv}2$5 zv8bOpm0@ZyNjO5sbJvLuKJ0yexX|G4v#+MWePQcPfIAzTZlz(k9k}g zlGvIg%aQ8DXZ5XAj-gkuIr}czlPq6|5hZMwnOt#)N${J@-29@nL5HH>k?9Hv_^7?gH+wRq3GP&_SJx@^`Cv2zAR-#UolP#5F_A1U%FuTNl(?xw6UXZ= zX(_hAqi*i5+r3RUOq&d4er~3|)`F|tWZV^Pv|2mGfGP#*PE5#r(~ey9$l4%E4UK@M zgPVAw=F9Br>2)2{x5$V>5{~_3_pDlc7>@i8&Zz1}-j-UGS7i;&q@k*%(0sNQ5x8f8 z!`ybF4;HWvs-#hv*;K$i=o#ckR{jI@vWdofl)%-X{#IfU%2)3HOHewj;m$qFG7tx9wUO z>zB9hi*Kw03D!2~EuO6pV5B`y&Tp&m=YwWEfD!YS49rP&M))}WIa|U$aDvMfK?w*O z!2xV;h|hUgd7@87Z-<;YRK;kEitzurce?Mu0YUbSOun%Cms8P0!i83pbLG0APWrrC#d-bD8M9KAKP&u9jiD>SqoJB~zV^_=+yQL5SG zfg3m5&>`zIdk4J)cvUSs;_(mM*K+e5a&_)#<89>cpw7WDNsN3Pba3RoR z_AXdk(@?_@T4mWa$O6vjKHLG}UCZwVSrtckJ-fH~Jhte6E1mMPWf2*6v1aP=l4^w4 z|3)mS*UQhLDJy`ip%*WzW)oT=%;qBpCs${$aH@mR@Q3A>h}`cda!DbTdsx zMK}B87wZ4C$qhRi#A86%H*W1K`ZFmLMuLK#&sSmKpe;td&G+s281PUWnpv@6uvA6I zf>YxF5*)O3i1hPCI7+Q8M}ZHlK{67%Zde0G472ASZZ!Lt)XBtG*KBx#`PZoaLNyED zN6NeUOT2%%3a5##J^&uarla+t$w?c}kF5QJOb(%qjdjS^be^Hzj~uIi%S~F+H64hn zgGL@GGU}U)ilKwbVipt#E3~}0^|oMidfFn1gG8(N)NaV$NpMb|Hr}6Y?nl>gDL)xv zj%}pUI$EgS2jh4L&jFl6V^Nt2hWt14+BH|2H3Xt-eJm_;MMV zM}KXH;N!?bY&=rrnZq8-df@aaK=@U6CXmOskDY;_@cs{64{XYP4GTK|1&^Vv5iGaQ zGAzYxeZ33DYsIhkXDdKE{=Z;7>eYG4EvokWgVTRVRLnlvrR}WG$+h1eLn!@@h$36y zn?X!|x4yCO8#(ElxI2o+5cldqU7r;c%V^Z3nVgw9j?6wwh}Xf)np48tERlo6Z) zQ|Dz8ycjS$(ECVUtw(?5Y5@DbUm;?Pxqt*=M&Ho0#Mxg`r;V(&?ErH01!(m?`om7V zRSB>q19Cq;VStPzn-=0)*(a+fr`r+M2k!);DC{l?;#(Mq%FQ>l>vx1s}2#q>inIDf^jLxy017*SnZQeSvgirR@tq+ zZcf|H?Xh9`gEM+Y#(TwMM_+utMDpN$t2&yKV6Ff&2?D(MSp%q|2||W5_zz@9w2G?Q zMP-#`@5UP~o)Nx$Cw$6PMF31kdsP&WiXl1+)h=i(BP1L4EV|@cISO;o|W0T>Yty zBc`5y4=J~kK4n@O2{H1!aVLosC%vwG6{K?g`n!>w)sdN2zH?F9MRjNMd&H~vSAmEG zPTdr(`LU_DpcI-{HW4hU`HSMNIBJ0Tam{M!O4gpEu-dBo#9El)mmiu0@dZbw;w!or z2ZNMkx+i;gf^}$(n0P1mevS@SV~LOJ24)lo%1l&2*?#Vp&kP!9i+I%=; zT1RO74vTl(1arBwTdc72HSVS+O6$q0qf`jrFxK(cY`;YgQ8)X9k%& zR%GlXw%x^2C#GgoDiUx()#;udPv(abct7NXO{NHlSv)8koZ-#IaW`?--(C>$f#Gp; zE#+ht93y6fAT~NX^=f4 z5&*dJwdeq<((P{Q0&AR!g%Ve?lTRMG4=gbB%LhW&WaCVySE)W23DiOlIA&;_M33N= zM}G8F4?1yAF?oK_AM$d z?UDgm8j0xTYoA3IyFg#1nO`N8HSykMfv z-oha@t{&mmoP5?B{p6Q8QRd*U{DQyWA6zN_VnHOD3D9H#Qub5>7pHoj#aIT<=JCW7 zTnHA-3CV#>);Uc@S}hqDe&#g(uObjj?UjhBsT7mJTGf!Oo3*K6fTsUi76aR7Om`)C zTlBSeZwiQBFYDxf#liq2V~^u}ih=%BGe{r5+U21KzCNZ_0fg@QdNCQ?^0Hb8n83`T zNr09>aX7zuB7ykN)(+<<^Ai=V(LpL5rK&FhB3 z*tDQh3oDFhTb`#uo|YzFTUrgaHQ}qMd=V1w)oHU+o(8*>!*{LZ6x^!U#kR%w(s*=l0*Gtxps_tFT6F1Anp2)4tyTQ&QZ#$&s#@m-o@$nP zQ=LI=cZcQXJFG>+;qpf!vUu6OeZU9rC1_(0&#b>TlTzU=5t*T9qq^&Z-QzB8comdX zV0-vVs0ma>!GepU=BPmtblQ74mG-@~sJ9zT+}wN(4VNgVQ-n8T^tsS@W zEpR(c$3-0h;>n@37e2^P;Du#Ukpq*o8ZD{+myMrSWlqzv5MJJDHzu zHru)yZ@!`?OaDC+oCRa``!B$53rmq1s!(F*JGE>%@pb(K7(_2R&VZV-=UkT(zs+43 zY$jh(%iWtdeM>4%FLB?;;t4hc;Ypob#K;=S3zgTA@NR$Rfk`x*GMw-4}JZ( zHjY=1tn8_?fvhppocciX-Scu?`}l@{;KDE<7v>EiFU1aa-UaA zg2}$SbHC>jR^gOybYu*FDqgR|Zn}a@P5=T$rdHIQVe0H~mmQJP-|RaUJE>G!Fnb?< zt}cFFjL!A{Lu?$}+m0!abG^`VIR{mIVey;qgb0ss1jD*)n>Mxhv7#jl-h{hYr~};` zXH%6VqEk>y0+=%z8u+GM&J(#_Y`D1fbp4daeHuJ=ZI2r?Oj42 zXWOY(hZ7r7aC&N9M^_TbKOr$Ve0Z5BtsurZRUz-w_zL(c)Dphx*f}~D7&}JI{E0Q3 zRc$>lE^ff!PSM`-Ovbb1ZW5R@%yRlwg2o%H+$g z0ggXg2X)HKA_U}=iqNg4I`y~sJwH6CF{ntkq1-bYpw)Yu0d5(`XLmNx6hogxsf`m$^=gqhCY|p^!;IF+-*VVu{IypQy2FpGy5)VHe2>g<4ljNf18AE~ z?$qCm2AhZg`b70=?{e8Q6zJN5D#$bRHb%Og7ka6MWCWvr>}^HqEqY!3ux4NNtVo`u z1IItcq47Q}K}Pu3wexn1zJ$GgZPwC+<`l3q)-jqL2Gr&ENxcgG`de-L|C^WHFA10Y rr^oQGK<*c*@*f!3f4{BXw#3lkI)d2r(`2{-f{{|ZnRVUh|EviBulu+@ literal 0 HcmV?d00001 diff --git a/profiling/results/tims/hela_score_histogram_psm.png b/profiling/results/tims/hela_score_histogram_psm.png new file mode 100644 index 0000000000000000000000000000000000000000..cff58b3ef25d4778ba32f6b4a9b21e1402d069ce GIT binary patch literal 17891 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 literal 0 HcmV?d00001 diff --git a/profiling/src/plot_results.py b/profiling/src/plot_results.py index 27e91c3..114bb29 100644 --- a/profiling/src/plot_results.py +++ b/profiling/src/plot_results.py @@ -99,6 +99,20 @@ def main(): plt.savefig(Path(base_dir) / f"{prefix}_peptide_qval.png") plt.clf() + metrics = {} + metrics["NumPeptides"] = len(qvals) + metrics["AvgTargetScore"] = df.filter(pl.col("decoy").is_not())["Score"].mean() + metrics["TargetQ95Score"] = df.filter(pl.col("decoy").is_not())["Score"].quantile( + 0.95, + ) + metrics["AvgDecoyScore"] = df.filter(pl.col("decoy"))["Score"].mean() + metrics["DecoyQ95Score"] = df.filter(pl.col("decoy"))["Score"].quantile(0.95) + + # Write metrics to toml file + with open(Path(base_dir) / f"{prefix}_metrics.toml", "w") as f: + for k, v in metrics.items(): + f.write(f"{k} = {v}\n") + if __name__ == "__main__": main() From 0b3d434bac817ba0b310dda843251dc070d10602 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Thu, 11 May 2023 14:59:09 -0700 Subject: [PATCH 37/41] updated dvc run info --- profiling/dvc.yaml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index 93b96e3..ed4fbfa 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -41,10 +41,14 @@ stages: outs: - results/tims/hela.diadem.csv plots: - - results/tims/hela_log_score_histogram_peptide.png - - results/tims/hela_score_histogram_peptide.png - - results/tims/hela_peptide_qval.png - - results/tims/hela_score_histogram_psm.png + - results/tims/hela_log_score_histogram_peptide.png: + cache: false + - results/tims/hela_score_histogram_peptide.png: + cache: false + - results/tims/hela_peptide_qval.png: + cache: false + - results/tims/hela_score_histogram_psm.png: + cache: false metrics: - results/tims/hela_metrics.toml: cache: false @@ -69,10 +73,14 @@ stages: outs: - results/tims/ecoli.diadem.csv plots: - - results/tims/ecoli_log_score_histogram_peptide.png - - results/tims/ecoli_score_histogram_peptide.png - - results/tims/ecoli_peptide_qval.png - - results/tims/ecoli_score_histogram_psm.png + - results/tims/ecoli_log_score_histogram_peptide.png: + cache: false + - results/tims/ecoli_score_histogram_peptide.png: + cache: false + - results/tims/ecoli_peptide_qval.png: + cache: false + - results/tims/ecoli_score_histogram_psm.png: + cache: false metrics: - results/tims/ecoli_metrics.toml: cache: false From 510b75582b4f2e54391a57af8585637be2339157 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Thu, 11 May 2023 15:18:49 -0700 Subject: [PATCH 38/41] minor docsting change --- diadem/search/metrics.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/diadem/search/metrics.py b/diadem/search/metrics.py index 5ce06b6..be6d20f 100644 --- a/diadem/search/metrics.py +++ b/diadem/search/metrics.py @@ -26,8 +26,19 @@ def cosinesim(x: NDArray, y: NDArray) -> NDArray: return out -def max_rolling(a, window, axis=1): - """From this answer: +def max_rolling(a: np.ndarray, window: int, axis: int = 1) -> np.ndarray: + """Max window smoothing on a numpy array. + + Parameters + ---------- + a: np.ndarray + The array to smooth. + window: int + The size of the window. + axis: int + The axis along which to smooth. + + From this answer: https://stackoverflow.com/a/52219082. """ shape = a.shape[:-1] + (a.shape[-1] - window + 1, window) From 1c16fc630a97359964d7baee3954d98e385a2410 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Thu, 11 May 2023 15:39:32 -0700 Subject: [PATCH 39/41] updated run info --- profiling/dvc.lock | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/profiling/dvc.lock b/profiling/dvc.lock index cd81b3b..09c0140 100644 --- a/profiling/dvc.lock +++ b/profiling/dvc.lock @@ -1,10 +1,10 @@ schema: '2.0' stages: tims_run_hela: - cmd: 'mkdir -p results/tims && python src/run.py --config configs/tims.toml --fasta + cmd: mkdir -p results/tims && python src/run.py --config configs/tims.toml --fasta profiling_data/UP000005640_9606_crap.fasta --ms_data profiling_data/230426_Hela_01_S4-E5_1_662.hdf --output results/tims/hela --threads 4 && python src/plot_results.py results/tims - --prefix hela ' + --prefix hela deps: - path: profiling_data/230426_Hela_01_S4-E5_1_662.hdf md5: 02f5846145d04ff9301f78d2b6299b75 @@ -66,6 +66,9 @@ stages: - path: results/tims/hela_log_score_histogram_peptide.png md5: 2f06cdc3315a19ef9b85f1ba3d042efb size: 18959 + - path: results/tims/hela_metrics.toml + md5: 01b8ef67d59b1afc4e2ad11aa8e2adb4 + size: 159 - path: results/tims/hela_peptide_qval.png md5: 5f65816667e975520669f8e30b733ec8 size: 20114 @@ -76,9 +79,9 @@ stages: md5: 4ddc3d4f219ba74e2b7954767ac55b37 size: 17891 tims_run_ecoli: - cmd: 'python src/run.py --config configs/tims.toml --fasta profiling_data/UP000000625_83333_crap.fasta - --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.d --output results/tims/ecoli - --threads 4 && python src/plot_results.py results/tims --prefix ecoli ' + cmd: python src/run.py --config configs/tims.toml --fasta profiling_data/UP000000625_83333_crap.fasta + --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf --output results/tims/ecoli + --threads 4 && python src/plot_results.py results/tims --prefix ecoli deps: - path: profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf md5: 764764d9da856aab25d562b4c8c835e2 @@ -143,6 +146,9 @@ stages: - path: results/tims/ecoli_log_score_histogram_peptide.png md5: 93d29ba9829cdf9477f074c7bedb3b99 size: 18936 + - path: results/tims/ecoli_metrics.toml + md5: fe327b9c34d60ff26c52c2cb5def5829 + size: 160 - path: results/tims/ecoli_peptide_qval.png md5: 512cfec09b0fdc8faf632daebc5476b1 size: 21246 From bd4aab417c26515f44e728cc4801ee811f6b71e9 Mon Sep 17 00:00:00 2001 From: "J. Sebastian Paez" Date: Fri, 12 May 2023 21:33:42 -0700 Subject: [PATCH 40/41] updated metrics and fixed mzml --- diadem/data_io/mzml.py | 366 +++++++----------- diadem/data_io/timstof.py | 3 +- diadem/search/diadem.py | 19 +- diadem/search/metrics.py | 14 +- profiling/configs/orbi.toml | 50 +++ profiling/dvc.lock | 147 +++++-- profiling/dvc.yaml | 22 +- profiling/lineprofile_dia.zsh | 42 -- profiling/lineprofile_timstof.zsh | 23 -- profiling/profile_dia.zsh | 6 - .../orbi/hela_log_score_histogram_peptide.png | Bin 0 -> 23397 bytes profiling/results/orbi/hela_metrics.toml | 5 + profiling/results/orbi/hela_peptide_qval.png | Bin 0 -> 18880 bytes profiling/results/orbi/hela_runtime.toml | 1 + .../orbi/hela_score_histogram_peptide.png | Bin 0 -> 23869 bytes .../results/orbi/hela_score_histogram_psm.png | Bin 0 -> 20897 bytes .../results/orbi/hela_scores_over_time.png | Bin 0 -> 241248 bytes .../ecoli_log_score_histogram_peptide.png | Bin 18936 -> 23229 bytes profiling/results/tims/ecoli_metrics.toml | 10 +- profiling/results/tims/ecoli_peptide_qval.png | Bin 21246 -> 18364 bytes profiling/results/tims/ecoli_runtime.toml | 1 + .../tims/ecoli_score_histogram_peptide.png | Bin 20316 -> 20845 bytes .../tims/ecoli_score_histogram_psm.png | Bin 17023 -> 24514 bytes .../results/tims/ecoli_scores_over_time.png | Bin 0 -> 278837 bytes .../tims/hela_log_score_histogram_peptide.png | Bin 18959 -> 22777 bytes profiling/results/tims/hela_metrics.toml | 10 +- profiling/results/tims/hela_peptide_qval.png | Bin 20114 -> 19942 bytes profiling/results/tims/hela_runtime.toml | 1 + .../tims/hela_score_histogram_peptide.png | Bin 20614 -> 21475 bytes .../results/tims/hela_score_histogram_psm.png | Bin 17891 -> 22476 bytes .../results/tims/hela_scores_over_time.png | Bin 0 -> 227873 bytes profiling/run_profile.py | 10 - profiling/run_profile_multithread.py | 10 - profiling/run_profile_multithread.zsh | 22 -- profiling/run_profile_tims.py | 24 -- profiling/run_profile_tims_multithread.py | 24 -- profiling/src/plot_results.py | 21 +- profiling/src/plot_results_base.py | 13 - profiling/src/run.py | 5 + 39 files changed, 396 insertions(+), 453 deletions(-) create mode 100644 profiling/configs/orbi.toml delete mode 100644 profiling/lineprofile_dia.zsh delete mode 100644 profiling/lineprofile_timstof.zsh delete mode 100644 profiling/profile_dia.zsh create mode 100644 profiling/results/orbi/hela_log_score_histogram_peptide.png create mode 100644 profiling/results/orbi/hela_metrics.toml create mode 100644 profiling/results/orbi/hela_peptide_qval.png create mode 100644 profiling/results/orbi/hela_runtime.toml create mode 100644 profiling/results/orbi/hela_score_histogram_peptide.png create mode 100644 profiling/results/orbi/hela_score_histogram_psm.png create mode 100644 profiling/results/orbi/hela_scores_over_time.png create mode 100644 profiling/results/tims/ecoli_runtime.toml create mode 100644 profiling/results/tims/ecoli_scores_over_time.png create mode 100644 profiling/results/tims/hela_runtime.toml create mode 100644 profiling/results/tims/hela_scores_over_time.png delete mode 100644 profiling/run_profile.py delete mode 100644 profiling/run_profile_multithread.py delete mode 100644 profiling/run_profile_multithread.zsh delete mode 100644 profiling/run_profile_tims.py delete mode 100644 profiling/run_profile_tims_multithread.py delete mode 100644 profiling/src/plot_results_base.py diff --git a/diadem/data_io/mzml.py b/diadem/data_io/mzml.py index f7ccbf1..58f809f 100644 --- a/diadem/data_io/mzml.py +++ b/diadem/data_io/mzml.py @@ -2,6 +2,7 @@ import os from collections.abc import Iterator +from contextlib import contextmanager from dataclasses import dataclass from pathlib import Path from typing import Literal @@ -10,16 +11,13 @@ import polars as pl from joblib import Parallel, delayed from loguru import logger -from ms2ml import Spectrum -from ms2ml.data.adapters import MZMLAdapter +from msflattener.mzml import get_mzml_data from numpy.typing import NDArray -from tqdm.auto import tqdm from diadem.config import DiademConfig, MassError from diadem.data_io.utils import slice_from_center, strictzip, xic -from diadem.deisotoping import deisotope from diadem.search.metrics import get_ref_trace_corrs -from diadem.utilities.utils import check_sorted, plot_to_log +from diadem.utilities.utils import is_sorted, plot_to_log # TODO re-make some of these classes as ABCs # It would make intent explicit, since they are sub-classed @@ -70,6 +68,9 @@ def __post_init__(self) -> None: self.base_peak_int, title=f"Base peak chromatogram for the Group in {self.iso_window_name}", ) + for x in self.mzs: + if not is_sorted(x): + raise ValueError("m/z arrays are not sorted is ScanGroup") @property def cache_file_stem(self) -> str: @@ -126,6 +127,7 @@ def _precursor_elems_from_cache(self, file): @classmethod def from_cache(cls, dir: Path, name: str) -> ScanGroup: """Loads a group from a cache file.""" + raise ValueError("Why am I here??") fragment_elems, _fragment_data = cls._elems_from_fragment_cache( dir / f"{name}_fragments.parquet", ) @@ -605,251 +607,167 @@ def __init__(self, mzml_file: Path | str, config: DiademConfig) -> None: The configuration object. Note that this is an DiademConfig configuration object. """ - self.adapter = MZMLAdapter(mzml_file, config=config) - - # TODO check if directly reading the xml is faster ... - # also evaluate if that is needed - scaninfo = pl.DataFrame(self.adapter.get_scan_info()).fill_null(0.0) - ms1_scaninfo = scaninfo.filter(pl.col("ms_level") <= 1) - self.config = config + self.cache_location = Path(mzml_file).with_suffix(".parquet") + if self.cache_location.exists(): + logger.info(f"Found cache file at {self.cache_location}") + else: + df = get_mzml_data(mzml_file, min_peaks=15) + df.write_parquet(self.cache_location) + del df + + unique_windows = ( + pl.scan_parquet(self.cache_location) + .select(pl.col(["quad_low_mz_values", "quad_high_mz_values"])) + .filter(pl.col("quad_low_mz_values") > 0) + .sort("quad_low_mz_values") + .unique() + .collect() + ) + if "DEBUG_DIADEM" in os.environ: - logger.error("RUNNING DIADEM IN DEBUG MODE (only 700-710 mz iso windows)") - scaninfo = scaninfo.filter(pl.col("ms_level") > 1) - window_filter = [x[0] > 700 and x[0] < 705 for x in scaninfo["iso_window"]] - scaninfo = scaninfo.filter(window_filter) + logger.error("RUNNING DIADEM IN DEBUG MODE (only the 4th precursor index)") + self.unique_precursor_windows = unique_windows[3:4].rows(named=True) + else: + self.unique_precursor_windows = unique_windows.rows(named=True) - ms1_scaninfo = ms1_scaninfo.drop("collision_energy").with_columns( - iso_window=0.0, - ) - self.ms1info = ms1_scaninfo - self.ms2info = scaninfo.filter(pl.col("ms_level") > 1) - self.unique_iso_windows = { - tuple(x) for x in self.ms2info["iso_window"].to_numpy() - } + @contextmanager + def lazy_datafile(self) -> pl.LazyFrame: + """Scans the cached version of the data and yields it as a context manager.""" + yield pl.scan_parquet(self.cache_location) - def _preprocess_scangroup( + def _precursor_iso_window_elements( self, - name: str, - df_chunk: pl.DataFrame, + precursor_window: dict[str, float], mz_range: None | tuple[float, float] = None, - progress: bool = True, - ) -> tuple[NDArray, ...]: - logger.debug(f"Processing iso window {name}") - - window_mzs = [] - window_ints = [] - window_bp_mz = [] - window_bp_int = [] - window_rtinsecs = [] - window_scanids = [] - - npeaks_raw = [] - npeaks_deisotope = [] - - for row in tqdm( - df_chunk.rows(named=True), - desc=f"Preprocessing spectra for {name}", - disable=not progress, - ): - spec_id = row["spec_id"] - curr_spec: Spectrum = self.adapter[spec_id] - # NOTE instrument seems to have a wrong value ... - # Also activation seems to not be recorded ... - curr_spec = curr_spec.filter_top(self.config.run_max_peaks_per_spec) - curr_spec = curr_spec.filter_mz_range(*self.config.ion_mz_range) - - mzs = curr_spec.mz - intensities = curr_spec.intensity + ) -> dict[str : dict[str:NDArray]]: + # TODO make this a more generic function + # this is pretty much the same for timstof data but + # with ims values... + with self.lazy_datafile() as datafile: + datafile: pl.LazyFrame + promise = ( + pl.col("quad_low_mz_values") == precursor_window["quad_low_mz_values"] + ) & ( + pl.col("quad_high_mz_values") == precursor_window["quad_high_mz_values"] + ) + ms_data = datafile.filter(promise).sort("rt_values") if mz_range is not None: - mz_filter = slice( - np.searchsorted(mzs, mz_range[0]), - np.searchsorted(mzs, mz_range[1], "right"), + nested_cols = [ + "mz_values", + "corrected_intensity_values", + ] + non_nested_cols = [ + x for x in ms_data.head().collect().columns if x not in nested_cols + ] + ms_data = ( + ms_data.explode(nested_cols) + .filter(pl.col("mz_values").is_between(mz_range[0], mz_range[1])) + .groupby(pl.col(non_nested_cols)) + .agg(nested_cols) + .sort("rt_values") ) - mzs = mzs[mz_filter] - intensities = intensities[mz_filter] - - # Deisotoping! - if self.config.run_deconvolute_spectra: - # Masses need to be ordered for the deisotoping function! - order = np.argsort(curr_spec.mz) - npeaks_raw.append(len(order)) - - mzs = curr_spec.mz[order] - intensities = curr_spec.intensity[order] - if len(mzs) > 0: - mzs, intensities = deisotope( - mzs, - intensities, - max_charge=5, - diff=self.config.g_tolerances[1], - unit=self.config.g_tolerance_units[1], - ) - npeaks_deisotope.append(len(mzs)) - - # TODO evaluate this scaling - intensities = np.sqrt(intensities) - if len(mzs) == 0: - mzs, intensities = np.array([0]), np.array([0]) - - bp_index = np.argmax(intensities) - bp_mz = mzs[bp_index] - bp_int = intensities[bp_index] - rtinsecs = curr_spec.retention_time.seconds() - - window_mzs.append(mzs) - window_ints.append(intensities) - window_bp_mz.append(bp_mz) - window_bp_int.append(bp_int) - window_rtinsecs.append(rtinsecs) - window_scanids.append(spec_id) - - window_bp_mz = np.array(window_bp_mz).astype(np.float32) - window_bp_int = np.array(window_bp_int).astype(np.float32) - window_rtinsecs = np.array(window_rtinsecs).astype(np.float32) - check_sorted(window_rtinsecs) - window_scanids = np.array(window_scanids, dtype="object") - - avg_peaks_raw = np.array(npeaks_raw).mean() - avg_peaks_deisotope = np.array(npeaks_deisotope).mean() - # Create datasets within each group - logger.info(f"Saving group {name} with length {len(window_mzs)}") - logger.info( - ( - f"{avg_peaks_raw} peaks/spec; {avg_peaks_deisotope} peaks/spec after" - " deisotoping" - ), - ) - - out = [ - window_mzs, - window_ints, - window_bp_mz, - window_bp_int, - window_rtinsecs, - window_scanids, - ] - return out - def _get_iso_window_group( - self, - iso_window_name: str, - iso_window: tuple[float, float], - ms2_chunk: pl.DataFrame, - ms1_chunk: pl.DataFrame, - ) -> ScanGroup: - """Gets all spectra that share the same window. + ms_data = ms_data.collect() - Gets all spectra that share the same iso window - and creates a ScanGroup for them. + bp_indices = [np.argmax(x) for x in ms_data["corrected_intensity_values"]] + bp_ints = [ + x1.to_numpy()[x2] + for x1, x2 in zip(ms_data["corrected_intensity_values"], bp_indices) + ] + bp_ints = np.array(bp_ints) + bp_mz = [ + x1.to_numpy()[x2] for x1, x2 in zip(ms_data["mz_values"], bp_indices) + ] + bp_mz = np.array(bp_mz) + bp_indices = np.array(bp_indices) + rts = ms_data["rt_values"].to_numpy(zero_copy_only=True) + assert is_sorted(rts) + + quad_high = ms_data["quad_high_mz_values"][0] + quad_low = ms_data["quad_low_mz_values"][0] + window_name = str(quad_low) + "_" + str(quad_high) + + template = window_name + "_{}" + scan_indices = [template.format(i) for i in range(len(rts))] + + x = { + "precursor_range": (quad_low, quad_high), + "base_peak_int": bp_ints, + "base_peak_mz": bp_mz, + "iso_window_name": window_name, + "retention_times": rts, + "scan_ids": scan_indices, + } + orders = [np.argsort(x.to_numpy()) for x in ms_data["mz_values"]] + + x.update( + { + "mzs": [ + x.to_numpy()[o] for x, o in zip(ms_data["mz_values"], orders) + ], + "intensities": [ + x.to_numpy()[o] + for x, o in zip(ms_data["corrected_intensity_values"], orders) + ], + }, + ) - Meant for internal usage. + return x - Parameters - ---------- - iso_window_name : str - Name of the isolation window. - iso_window : tuple[float, float] - Isolation window. - ms2_chunk : DataFrame - DataFrame containing all MS2 spectra. - ms1_chunk : DataFrame - DataFrame containing all MS1 spectra. - """ - ( - window_mzs, - window_ints, - window_bp_mz, - window_bp_int, - window_rtinsecs, - window_scanids, - ) = self._preprocess_scangroup( - name=iso_window_name, - df_chunk=ms2_chunk, - mz_range=None, + def _precursor_iso_window_groups( + self, + precursor_window: dict[str, float], + ) -> dict[str:ScanGroup]: + elems = self._precursor_iso_window_elements(precursor_window) + prec_info = self._precursor_iso_window_elements( + {"quad_low_mz_values": -1, "quad_high_mz_values": -1}, + mz_range=list(precursor_window.values()), ) - ( - prec_mzs, - prec_ints, - _, - _, - prec_rtinsecs, - _, - ) = self._preprocess_scangroup( - name="precursors " + iso_window_name, - df_chunk=ms1_chunk, - mz_range=iso_window, - ) + assert is_sorted(prec_info["retention_times"]) - group = ScanGroup( - iso_window_name=iso_window_name, - precursor_range=iso_window, - mzs=window_mzs, - intensities=window_ints, - base_peak_mz=window_bp_mz, - base_peak_int=window_bp_int, - retention_times=window_rtinsecs, - scan_ids=window_scanids, - precursor_intensities=prec_ints, - precursor_mzs=prec_mzs, - precursor_retention_times=prec_rtinsecs, + out = ScanGroup( + precursor_mzs=prec_info["mzs"], + precursor_intensities=prec_info["intensities"], + precursor_retention_times=prec_info["retention_times"], + **elems, ) - return group + return out - def get_iso_window_groups( - self, - workerpool: None | Parallel = None, - ) -> list[ScanGroup]: - """Returns a list of all ScanGroups in an mzML file.""" - grouped = self.ms2info.sort("RTinSeconds").groupby("iso_window") - iso_windows, chunks = zip(*list(grouped)) - precursor_info = self.ms1info - - iso_window_names = [ - "({:.06f}, {:.06f})".format(*iso_window) for iso_window in iso_windows - ] + def get_iso_window_groups(self, workerpool: None | Parallel) -> list[ScanGroup]: + """Get scan groups for each unique isolation window. + Parameters + ---------- + workerpool : None | Parallel + If None, the function will be run in serial mode. + If Parallel, the function will be run in parallel mode. + The Parallel is created using joblib.Parallel. + + Returns + ------- + list[ScanGroup] + A list of ScanGroup objects. + Each of them corresponding to an unique isolation window from + the quadrupole. + """ if workerpool is None: results = [ - self._get_iso_window_group( - iso_window_name=iwn, - iso_window=iw, - ms2_chunk=chunk, - ms1_chunk=precursor_info, - ) - for iwn, iw, chunk in zip(iso_window_names, iso_windows, chunks) + self._precursor_iso_window_groups(i) + for i in self.unique_precursor_windows ] else: results = workerpool( - delayed(self._get_iso_window_group)( - iso_window_name=iwn, - iso_window=iw, - ms2_chunk=chunk, - ms1_chunk=precursor_info, - ) - for iwn, iw, chunk in zip(iso_window_names, iso_windows, chunks) + delayed(self._precursor_iso_window_groups)(i) + for i in self.unique_precursor_windows ) return results - def yield_iso_window_groups(self, progress: bool = False) -> Iterator[ScanGroup]: + def yield_iso_window_groups(self) -> Iterator[ScanGroup]: """Yield scan groups for each unique isolation window.""" - grouped = self.ms2info.sort("RTinSeconds").groupby("iso_window") - precursor_info = self.ms1info - - for iso_window, chunk in tqdm( - grouped, - disable=not progress, - desc="Unique Isolation Windows", - ): - iso_window_name = "({:.06f}, {:.06f})".format(*iso_window) - - group = self._get_iso_window_group( - iso_window_name=iso_window_name, - iso_window=iso_window, - ms2_chunk=chunk, - ms1_chunk=precursor_info, - ) - yield group + for i in self.unique_precursor_windows: + results = self._precursor_iso_window_groups(i) + yield results diff --git a/diadem/data_io/timstof.py b/diadem/data_io/timstof.py index 85475ca..94e2841 100644 --- a/diadem/data_io/timstof.py +++ b/diadem/data_io/timstof.py @@ -10,7 +10,6 @@ import numpy as np import polars as pl -from alphatims.bruker import TimsTOF from joblib import Parallel, delayed from loguru import logger from ms2ml.utils.mz_utils import get_tolerance @@ -442,7 +441,7 @@ def __init__(self, filepath: PathLike, config: DiademConfig) -> None: self.unique_precursor_windows = unique_windows.rows(named=True) @contextmanager - def lazy_datafile(self) -> TimsTOF: + def lazy_datafile(self) -> pl.LazyFrame: """Scans the cached version of the data and yields it as a context manager.""" yield pl.scan_parquet(self.cache_location) diff --git a/diadem/search/diadem.py b/diadem/search/diadem.py index 45e90f5..49cf527 100644 --- a/diadem/search/diadem.py +++ b/diadem/search/diadem.py @@ -67,7 +67,7 @@ def search_group( # noqa C901 `search_group` is too complex (18) IMS_TOLERANCE = config.g_ims_tolerance # noqa IMS_TOLERANCE_UNIT = config.g_ims_tolerance_unit # noqa - MAX_NUM_CONSECUTIVE_FAILS = 50 # noqa + MAX_NUM_CONSECUTIVE_FAILS = 100 # noqa start_rts, start_bpc = group.retention_times.copy(), group.base_peak_int.copy() @@ -218,9 +218,13 @@ def search_group( # noqa C901 `search_group` is too complex (18) match_indices = scores["spec_indices"].iloc[0] + [new_stack.ref_index] match_indices = np.sort(np.unique(np.array(match_indices))) + min_corr_score_scale = np.quantile( + new_stack.correlations[match_indices], + 0.75, + ) + scores["q75_correlation"] = min_corr_score_scale corr_match_indices = np.where( - new_stack.correlations - > np.median(new_stack.correlations[match_indices]), + new_stack.correlations > min_corr_score_scale, )[0] match_indices = np.sort( np.unique(np.concatenate([match_indices, corr_match_indices])), @@ -235,19 +239,20 @@ def search_group( # noqa C901 `search_group` is too complex (18) new_stack.plot(ax1, matches=match_indices) - ax2.plot(start_rts, start_bpc, alpha=0.2, color="gray") - ax2.plot(group.retention_times, group.base_peak_int) + ax2.plot(start_rts, np.sqrt(start_bpc), alpha=0.2, color="gray") + ax2.plot(group.retention_times, np.sqrt(group.base_peak_int)) ax2.vlines( x=group.retention_times[new_stack.parent_index], ymin=0, - ymax=new_stack.base_peak_intensity, + ymax=np.sqrt(new_stack.base_peak_intensity), color="r", ) plt.title( ( f"Score: {scores['Score'].iloc[0]} \n" f" Peptide: {scores['peptide'].iloc[0]} \n" - f"@ RT: {scores['RetentionTime'].iloc[0]}" + f"@ RT: {scores['RetentionTime'].iloc[0]} \n" + f"Corr Score: {min_corr_score_scale}" ), ) plt.pause(0.01) diff --git a/diadem/search/metrics.py b/diadem/search/metrics.py index be6d20f..55b6b5b 100644 --- a/diadem/search/metrics.py +++ b/diadem/search/metrics.py @@ -38,6 +38,13 @@ def max_rolling(a: np.ndarray, window: int, axis: int = 1) -> np.ndarray: axis: int The axis along which to smooth. + Examples + -------- + >>> foo = np.array([[1,2,3,4,5,6,7,8], [5,6,7,8,9,10,11,12]]) + >>> max_rolling(foo, 3, -1) + array([[ 3, 4, 5, 6, 7, 8], + [ 7, 8, 9, 10, 11, 12]]) + From this answer: https://stackoverflow.com/a/52219082. """ @@ -96,9 +103,10 @@ def get_ref_trace_corrs(arr: NDArray[np.float32], ref_idx: int) -> NDArray[np.fl >>> [round(x, 4) for x in out] [0.8355, 0.8597, 0.8869, 0.9182, 0.9551, 0.9989, 0.9436, 0.8704, 0.7722, 0.6385] """ - arr = max_rolling(arr, 3, axis=1) - norm = np.linalg.norm(arr + 1e-5, axis=-1) - normalized_arr = arr / np.expand_dims(norm, axis=-1) + arr2 = max_rolling(arr, 3, axis=-1) + arr2 = np.sqrt(arr2) + norm = np.linalg.norm(arr2 + 1e-5, axis=-1) + normalized_arr = arr2 / np.expand_dims(norm, axis=-1) ref_trace = normalized_arr[..., ref_idx, ::1] # ref_trace = np.stack([ref_trace, ref_trace[..., ::-1]]).min(axis=0) # ref_trace = np.stack([ref_trace, ref_trace[..., ::-1]]).min(axis=0) diff --git a/profiling/configs/orbi.toml b/profiling/configs/orbi.toml new file mode 100644 index 0000000..d6b2586 --- /dev/null +++ b/profiling/configs/orbi.toml @@ -0,0 +1,50 @@ +peptide_length_range = [ + 7, + 25, +] +peptide_mz_range = [ + 400, + 2000, +] +precursor_charges = [ + 2, + 3, +] +ion_series = "by" +ion_charges = [ + 1, +] +ion_mz_range = [ + 250, + 2000.0, +] +db_enzyme = "trypsin" +db_max_missed_cleavages = 2 +db_bucket_size = 32768 +g_tolerances = [ + 0.02, + 0.02, +] +g_tolerance_units = [ + "da", + "da", +] +g_ims_tolerance = 0.01 +g_ims_tolerance_unit = "abs" +scoring_score_function = "Hyperscore" +run_max_peaks = 20000 +run_max_peaks_per_spec = 5000 +run_parallelism = 1 +run_deconvolute_spectra = true +run_min_peak_intensity = 100 +run_debug_log_frequency = 50 +run_allowed_fails = 5000 +run_window_size = 21 +run_max_peaks_per_window = 500 +run_min_intensity_ratio = 0.01 +run_min_correlation_score = 0.5 +run_scaling_ratio = 0.001 +run_scaling_limits = [ + 0.001, + 0.9, +] diff --git a/profiling/dvc.lock b/profiling/dvc.lock index 09c0140..dec5cfe 100644 --- a/profiling/dvc.lock +++ b/profiling/dvc.lock @@ -13,8 +13,8 @@ stages: md5: 6a22e753f0e35eae9e0bf3d95759d089 size: 13676378 - path: src/run.py - md5: 9342df61b2738cdf083da7ece1581b59 - size: 944 + md5: 074ac3e7f0c1020f179d8fdec9c677c2 + size: 1095 params: configs/tims.toml: db_bucket_size: 32768 @@ -61,23 +61,26 @@ stages: scoring_score_function: Hyperscore outs: - path: results/tims/hela.diadem.csv - md5: 766ebf66ebdd31b0e3f183dfd070c53a - size: 9523125 + md5: 9d3443227ddc4d7c0c742548336e4e9a + size: 15179553 - path: results/tims/hela_log_score_histogram_peptide.png - md5: 2f06cdc3315a19ef9b85f1ba3d042efb - size: 18959 + md5: 3b04c6c5d0871886be7c9c366cd558ec + size: 22777 - path: results/tims/hela_metrics.toml - md5: 01b8ef67d59b1afc4e2ad11aa8e2adb4 - size: 159 + md5: 592c82dba0c84c0e5114bb1ca058ef5b + size: 160 - path: results/tims/hela_peptide_qval.png - md5: 5f65816667e975520669f8e30b733ec8 - size: 20114 + md5: fc3f68c6dd84ef4306915186c3aaf69e + size: 19942 + - path: results/tims/hela_runtime.toml + md5: 6235b1e40562e00e58c2a7eaff18a064 + size: 27 - path: results/tims/hela_score_histogram_peptide.png - md5: daee36c5016bd8da0fe5954ed8fbb4c3 - size: 20614 + md5: 03826bdd891ad915a2187016b8a82a6b + size: 21475 - path: results/tims/hela_score_histogram_psm.png - md5: 4ddc3d4f219ba74e2b7954767ac55b37 - size: 17891 + md5: 3e88a2f284818880ba3abddc95d7b465 + size: 22476 tims_run_ecoli: cmd: python src/run.py --config configs/tims.toml --fasta profiling_data/UP000000625_83333_crap.fasta --ms_data profiling_data/LFQ_timsTOFPro_diaPASEF_Ecoli_01.hdf --output results/tims/ecoli @@ -90,11 +93,11 @@ stages: md5: 1b9a2db686eb2024edbd33b7f24117ef size: 1891750 - path: src/run.py - md5: 9342df61b2738cdf083da7ece1581b59 - size: 944 + md5: 074ac3e7f0c1020f179d8fdec9c677c2 + size: 1095 - path: src/run.py - md5: 9342df61b2738cdf083da7ece1581b59 - size: 944 + md5: 074ac3e7f0c1020f179d8fdec9c677c2 + size: 1095 params: configs/tims.toml: db_bucket_size: 32768 @@ -141,23 +144,26 @@ stages: scoring_score_function: Hyperscore outs: - path: results/tims/ecoli.diadem.csv - md5: 7708c2c0d799763eb8481c5857573b3b - size: 39559784 + md5: 999fb7588b75653e5a245753bf4b4070 + size: 70538015 - path: results/tims/ecoli_log_score_histogram_peptide.png - md5: 93d29ba9829cdf9477f074c7bedb3b99 - size: 18936 + md5: df9d4b3a8a21c54ae46b476ec0697224 + size: 23229 - path: results/tims/ecoli_metrics.toml - md5: fe327b9c34d60ff26c52c2cb5def5829 - size: 160 + md5: 0988437bd1b818d6aeeb1d23bb7793f4 + size: 166 - path: results/tims/ecoli_peptide_qval.png - md5: 512cfec09b0fdc8faf632daebc5476b1 - size: 21246 + md5: 498b784d3e0cd132eac8f93b8bf3fd7a + size: 18364 + - path: results/tims/ecoli_runtime.toml + md5: a076a35fbcf20c8aecae926d53802364 + size: 27 - path: results/tims/ecoli_score_histogram_peptide.png - md5: 310366668c75157baadb688eadd81518 - size: 20316 + md5: e0edfb0a28f3c264c6de36dd1d38c9b4 + size: 20845 - path: results/tims/ecoli_score_histogram_psm.png - md5: 88f05788e5257f329b7e0c5706daef1d - size: 17023 + md5: 0b62302a8e3cbd56d80084501158cd49 + size: 24514 get_data: cmd: zsh src/get_data.zsh deps: @@ -183,3 +189,84 @@ stages: - path: profiling_data/UP000005640_9606_crap.fasta md5: 6a22e753f0e35eae9e0bf3d95759d089 size: 13676378 + orbi_run_hela: + cmd: mkdir -p results/orbi && python src/run.py --config configs/orbi.toml --fasta + profiling_data/UP000005640_9606_crap.fasta --ms_data profiling_data/230407_Chrom_60m_1ug_v2_01.mzML + --output results/orbi/hela --threads 4 && python src/plot_results.py results/orbi + --prefix hela + deps: + - path: profiling_data/230407_Chrom_60m_1ug_v2_01.mzML + md5: 19efb578f116318ce63f707dd0193ad9 + size: 1452019888 + - path: profiling_data/UP000005640_9606_crap.fasta + md5: 6a22e753f0e35eae9e0bf3d95759d089 + size: 13676378 + - path: src/run.py + md5: 074ac3e7f0c1020f179d8fdec9c677c2 + size: 1095 + params: + configs/orbi.toml: + db_bucket_size: 32768 + db_enzyme: trypsin + db_max_missed_cleavages: 2 + g_ims_tolerance: 0.01 + g_ims_tolerance_unit: abs + g_tolerance_units: + - da + - da + g_tolerances: + - 0.02 + - 0.02 + ion_charges: + - 1 + ion_mz_range: + - 250 + - 2000.0 + ion_series: by + peptide_length_range: + - 7 + - 25 + peptide_mz_range: + - 400 + - 2000 + precursor_charges: + - 2 + - 3 + run_allowed_fails: 5000 + run_debug_log_frequency: 50 + run_deconvolute_spectra: true + run_max_peaks: 20000 + run_max_peaks_per_spec: 5000 + run_max_peaks_per_window: 500 + run_min_correlation_score: 0.5 + run_min_intensity_ratio: 0.01 + run_min_peak_intensity: 100 + run_parallelism: 1 + run_scaling_limits: + - 0.001 + - 0.9 + run_scaling_ratio: 0.001 + run_window_size: 21 + scoring_score_function: Hyperscore + outs: + - path: results/orbi/hela.diadem.csv + md5: a8d10c84ee9694b796b94fceed271bea + size: 28876637 + - path: results/orbi/hela_log_score_histogram_peptide.png + md5: e3f464612fa3dd216b493dbddce16b89 + size: 23397 + - path: results/orbi/hela_metrics.toml + md5: 8a906bf8cf89ed1bc13b4efda147bba1 + size: 160 + - path: results/orbi/hela_peptide_qval.png + md5: 965d5a315b0911a3a51d3c9acc980500 + size: 18880 + - path: results/orbi/hela_runtime.toml + md5: cdc4367963d2d0888807c4875405f114 + size: 28 + - path: results/orbi/hela_score_histogram_peptide.png + md5: 2c4ef5bb0055f822bf5c8cf88c85e21e + size: 23869 + - path: results/orbi/hela_score_histogram_psm.png + md5: 4d496191e3cca110117abb5b5f9ad478 + size: 20897 diff --git a/profiling/dvc.yaml b/profiling/dvc.yaml index ed4fbfa..5f093aa 100644 --- a/profiling/dvc.yaml +++ b/profiling/dvc.yaml @@ -12,12 +12,26 @@ stages: - profiling_data/UP000000625_83333_crap.fasta - profiling_data/UP000005640_9606_crap.fasta - orbi_run: + orbi_run_hela: params: - configs/orbi.toml: + plots: + - results/orbi/hela_log_score_histogram_peptide.png: + cache: false + - results/orbi/hela_score_histogram_peptide.png: + cache: false + - results/orbi/hela_peptide_qval.png: + cache: false + - results/orbi/hela_score_histogram_psm.png: + cache: false + metrics: + - results/orbi/hela_metrics.toml: + cache: false + - results/orbi/hela_runtime.toml: + cache: false deps: - src/run.py - - profiling_data/UP000000625_83333_crap.fasta + - profiling_data/230407_Chrom_60m_1ug_v2_01.mzML - profiling_data/UP000005640_9606_crap.fasta outs: - results/orbi/hela.diadem.csv @@ -52,6 +66,8 @@ stages: metrics: - results/tims/hela_metrics.toml: cache: false + - results/tims/hela_runtime.toml: + cache: false cmd: >- mkdir -p results/tims && python src/run.py @@ -84,6 +100,8 @@ stages: metrics: - results/tims/ecoli_metrics.toml: cache: false + - results/tims/ecoli_runtime.toml: + cache: false cmd: >- python src/run.py --config configs/tims.toml diff --git a/profiling/lineprofile_dia.zsh b/profiling/lineprofile_dia.zsh deleted file mode 100644 index 1f6aab8..0000000 --- a/profiling/lineprofile_dia.zsh +++ /dev/null @@ -1,42 +0,0 @@ - -# This one does need you to go into the code and decorate with @profile -# what you want profiled -mkdir -p lineprofile_results -sed -ie "s/# @profile/@profile/g" ../diadem/**/*.py -python -m pip install ../. -sed -ie "s/@profile/# @profile/g" ../diadem/**/*.py - -set -x -set -e - -# PYTHONOPTIMIZE=1 -DEBUG_DIADEM=1 python -m kernprof -l run_profile.py -python -m line_profiler run_profile.py.lprof > "line_profile_$(date '+%Y%m%d_%H%M').txt" -python -m line_profiler run_profile.py.lprof > "line_profile_latest.txt" - -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results/raw_td_plot.png", plot = g)' -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results/iter_score_plot.png", plot = g)' - -mokapot lineprofile_results/results.diadem.tsv.pin --test_fdr 0.01 --keep_decoys -R -e 'library(tidyverse) ; foo = readr::read_tsv("mokapot.peptides.txt") ; foo2 = readr::read_tsv("mokapot.decoy.peptides.txt") ; g <- ggplot(bind_rows(foo, foo2), aes(x = `mokapot score`, fill = Label)) + geom_density(alpha=0.4) ; ggsave("lineprofile_results/td_plot.png", plot = g)' - -# 20230207 6pm Elapsed time: 1571.8632419109344 -# 2023-02-07 20:02:42.108 | INFO | diadem.search.diadem:diadem_main:284 - Elapsed time: 1022.3919858932495 - -# Python deisotoping -# 2023-02-08 20:46:37.470 | INFO | diadem.search.diadem:diadem_main:290 - Elapsed time: 313.4971549510956 -# [INFO] - 83 target PSMs and 20 decoy PSMs detected. - -# Sage Deisotoping -# 2023-02-09 17:07:52.691 | INFO | diadem.search.diadem:diadem_main:289 - Elapsed time: 193.32771015167236 -# [INFO] - 180 target PSMs and 40 decoy PSMs detected. - -# Changing score tracking from objects to list and increased number of fails -# [INFO] - Found 356 peptides with q<=0.05 -# 2023-02-10 13:26:32.888 | INFO | diadem.search.diadem:diadem_main:290 - Elapsed time: 698.4216511249542 - -# DEBUG_DIADEM=1 PYTHONOPTIMIZE=1 python -m cProfile -s tottime run_profile.py > profile_singlethread.txt -# 2023-02-07 18:48:02.815 | INFO | diadem.search.diadem:diadem_main:284 - Elapsed time: 572.985399723053 - -# Changing num_decimal to 2 from 3, this will make buckets larger -# Changing number of isotopes diff --git a/profiling/lineprofile_timstof.zsh b/profiling/lineprofile_timstof.zsh deleted file mode 100644 index 926459e..0000000 --- a/profiling/lineprofile_timstof.zsh +++ /dev/null @@ -1,23 +0,0 @@ - - - -# This one does need you to go into the code and decorate with @profile -# what you want profiled -mkdir -p lineprofile_results -sed -ie "s/# @profile/@profile/g" ../diadem/**/*.py -python -m pip install "../.[test,profiling,dev]" -sed -ie "s/@profile/# @profile/g" ../diadem/**/*.py - -set -x -set -e - -# PYTHONOPTIMIZE=1 -mkdir -p lineprofile_results_tims -DEBUG_DIADEM=1 python -m kernprof -l run_profile_tims.py -python -m line_profiler run_profile_tims.py.lprof > "line_profile_tims_$(date '+%Y%m%d_%H%M').txt" -python -m line_profiler run_profile_tims.py.lprof > "line_profile_tims_latest.txt" -python -m pip install -e "../.[test,profiling,dev]" - - -R -e 'library(tidyverse) ; foo = readr::read_csv("lineprofile_results_tims/results.diadem.csv") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(decoy))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_tims/raw_td_plot.png", plot = g)' -R -e 'library(tidyverse) ; foo = readr::read_csv("lineprofile_results_tims/results.diadem.csv") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(decoy))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_tims/iter_score_plot.png", plot = g)' diff --git a/profiling/profile_dia.zsh b/profiling/profile_dia.zsh deleted file mode 100644 index a5c00c8..0000000 --- a/profiling/profile_dia.zsh +++ /dev/null @@ -1,6 +0,0 @@ - -python -m pip install ../. -DEBUG_DIADEM=1 python -m cProfile -s tottime -m diadem.cli search \ - --mzml_file profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML \ - --fasta profiling_data/uniprot_human_sp_canonical_2021-11-19_crap.fasta \ - --out_prefix "" --mode DIA > profile.txt diff --git a/profiling/results/orbi/hela_log_score_histogram_peptide.png b/profiling/results/orbi/hela_log_score_histogram_peptide.png new file mode 100644 index 0000000000000000000000000000000000000000..ec05210027d8ae7eb2396c5e94b9a7d687eee770 GIT binary patch literal 23397 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 literal 0 HcmV?d00001 diff --git a/profiling/results/orbi/hela_metrics.toml b/profiling/results/orbi/hela_metrics.toml new file mode 100644 index 0000000..3abd18c --- /dev/null +++ b/profiling/results/orbi/hela_metrics.toml @@ -0,0 +1,5 @@ +NumPeptides = 8697 +AvgTargetScore = 32.83279324619224 +TargetQ95Score = 44.880507377442605 +AvgDecoyScore = 29.001643309619226 +DecoyQ95Score = 35.013654165931776 diff --git a/profiling/results/orbi/hela_peptide_qval.png b/profiling/results/orbi/hela_peptide_qval.png new file mode 100644 index 0000000000000000000000000000000000000000..09408493a2ec477b123e81e8db0a59c06c6a9137 GIT binary patch literal 18880 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 C!46lstyrCYib5Tv`MyW`H~ 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 literal 0 HcmV?d00001 diff --git a/profiling/results/orbi/hela_score_histogram_psm.png b/profiling/results/orbi/hela_score_histogram_psm.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4ed6bac41c16a12cf4ae16efd9ed29dbc4afe0 GIT binary patch literal 20897 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 DW=E&5 literal 0 HcmV?d00001 diff --git a/profiling/results/orbi/hela_scores_over_time.png b/profiling/results/orbi/hela_scores_over_time.png new file mode 100644 index 0000000000000000000000000000000000000000..5875b14d0c3b95d7eb1533b264e802e8e7ef6440 GIT binary patch literal 241248 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%0AWRbk4?6g>2h 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∓|Iv{JNa?qJDgclvo-C;5R)-9VL;6wO=%dfyO*tP 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

U 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 literal 18936 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 B)cpVe diff --git a/profiling/results/tims/ecoli_metrics.toml b/profiling/results/tims/ecoli_metrics.toml index 6ed67e7..7c9e580 100644 --- a/profiling/results/tims/ecoli_metrics.toml +++ b/profiling/results/tims/ecoli_metrics.toml @@ -1,5 +1,5 @@ -NumPeptides = 7336 -AvgTargetScore = 22.988163949269012 -TargetQ95Score = 36.301172620664076 -AvgDecoyScore = 20.45939763445775 -DecoyQ95Score = 25.309875026034938 +NumPeptides_q_0.01 = 6904 +AvgTargetScore = 22.11691792428762 +TargetQ95Score = 34.39576249711483 +AvgDecoyScore = 20.221616627128082 +DecoyQ95Score = 25.000040717505303 diff --git a/profiling/results/tims/ecoli_peptide_qval.png b/profiling/results/tims/ecoli_peptide_qval.png index 3a7629be2ecf98ce518e98433fabe9b98feaeed9..e800d51c8a39dc2cf08d64501a1048735159f9f1 100644 GIT binary patch literal 18364 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 literal 21246 zcmdqJcT`i`yDq#C6-9xqQWOPLK$?I`uTc>dkWT1jBho@Ip#%bm8&DC@(3IXwLXjFs zC@LZX(g{ch>Am*=-;8Jf&bjA|bMF1!?>pZZcicZ5kz}o8t~u9w-sgSZ=bf)K)E?2% zveH5jM5pxlfffWE3xFW1(NibEJ7OJ!6W~S4T~Xg%+sVq^)7;e(QZ;vXws&&3w|#om z!_w8w*2z&sNJ8k=&8s%eIp=qE|31&|Q z=a1!#zB9;x;>@e%#W`PEO0` z59Fa+)IwC$JK94s<Ipwg65=wlzwk&Ku#$+lJ&w(M@{9eNvvG^Q#+vh#^mf%{^LH)uWKd#V+n7-c+XZ2j*9Enq99L`BxN|1y%^5qI42mA*!hZP=TU0>i5 zj(ozZrc+^TX99|YL;|40?&k_8J{M})yyUXoTQ9Vo`@tgWemC#okmv*JR_@u!-J|T* z1E=oRFb)Ap-u6l-S7q$p*;)mK{RYHNO3mI%Bx!G%I-+TAG5?)}X?E~t+6Gw5&F?>0 z#P(w5Nx2RJN?Q9pg2K|wb+<%Er;aGgYxCo!IYJ+%+^mN#IOhq|&)Mp&@IubWJQ2eN z*{#W?PnvyJ=kO~wq;|T5%XF&EyNC8Mac%XV_BrIOQnp0eju=utjgWW7H<#CQ6k=jy zV}my$qPe)Q$YUe)@_XMN9%&Bsiy`NJNL3#$T}NJWj`VOc$()1N9c}C%zLw!pBguru z%$JY7K(?{rXGX`;JOR~9XSl*eVQ(HF1($OvHalJ#*ed7HX z6T8I||3i2z+H-N(P9H1oZoAxh^iUys{!&}@URH^qgha1RM_H|Arnym_Rmr7b^NS

Kjtfr?E5{&B{@x<;c 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 HB{Ts5)0xkv diff --git a/profiling/results/tims/ecoli_runtime.toml b/profiling/results/tims/ecoli_runtime.toml new file mode 100644 index 0000000..71611c4 --- /dev/null +++ b/profiling/results/tims/ecoli_runtime.toml @@ -0,0 +1 @@ +runtime = 4270.734843254089 diff --git a/profiling/results/tims/ecoli_score_histogram_peptide.png b/profiling/results/tims/ecoli_score_histogram_peptide.png index c120928f122febd5d26b61a401769dcc744385c0..1240aee041c16d57fa2551a54b7af617dd39bd31 100644 GIT binary patch literal 20845 zcmb@uby!qwyEeWEyHP@rPzIF-DQOgm0THDe6zNjB22dVFB^@LL3F%G&X$(L>Kw4Vq z9%|_LUF&(D{l5EozrBy|`0f4u;ej&@v+i}rb)DCFp4WPL_m13&qqIj61UZ3~zomj8 zq&^5j^6T(Hct@aZU<`f;JIZJ|s@j=4y4<%nL6q)0KC-rRw6=J_Q)jGOGQ%Eb31M(&O1d#sxlSd0C|T2@z1;EiY09LdS6`@wT~@yla#`lp)u*pi z$Zrx@FsmUChb1p<7qFh@X}H`=n0)u{%TTH4Npk1pBej*r7 zQYHlP3A>1pB8b-c{|7I8w4CO|)nZvPsy_^u9*cPRXuV@(+$uDlZT4>VJzaIH&hB;- zM85X0kJPn1*G--C1G~2+ryVHqhP@&);*RffhChax*H_xlm(_WRsa7+Jhnv^C(ytRF ze|UsS+EISrnlRerd9NOM(DxQ9KCYe>1Bh49NZxjxYP{6>xGj?{g8ARt8)In z=eynN3*A41&1P)nr@V)#eL7pa+YcZc)k|NtSnU?{+;=u!t}*N=IZ$!;q|lcWdtwP; zyvEPTC5OVSg!pZjt=-(mRPsL`_+E_T#XY^m9C*a*dxYE8YI+90b>i7n-_G|kXNlA8 zch(ujehgo^W7XSt!crx-*#2Qt)R+@Xa&{(F)w0LhzWc4w%dHyq;Y#CUQz3`dN#vE# zyQk19e5F9i&Vik2&neZ@f5&gratXKn~_JZxUrd8#8~}KE(cMc#8q66^dLT;HmnhgZ;7oo zx7>E`Dq+(ue0k0FT)4lTMbTWJAvL$5fQMm`tc`k_z4-20y z%?GjV=pGo%55-{J6uWt1njEOPu=vkkz7Y31mB;nVQrgEkuC`Ki2|W0$ChkUI>KyRx zn|$D|Yl{83jHG~4|1|eeXA$S+4`PaHny|M$0qRxuB#poBY2|pCbY`g< zs&cli=dVvn&xr2eN+-G5uMqI=Wll z`Ra@m(|J(`F1C%*vg?X?iAX`Y#49a#S7e{ev=_g%9jZ#yUa3|Idv4=6R!=mI*n3KN zo@SewV>!fgH#@4cLulAmBiLm)RKM@TOy>mW=C-@lPM#Mn-_ltptKpkrO<{P9`c-|g zZk+(xMB1X+_mzjdRo72)juR%couZ4=rQ_c7ZtdYl-mrCBrN8SfwA325EohCrG3;5c z9oWC#86{KLnPd=iUDAOe_#HlpyIMQ<9+^j@5zp7&_10?TG<)A}h3IaL!M>spvxRpe z4hso_)`RJ%WKZ&Usg&NZ5`Nf{bg{CW%|=HTw>N%N-D*Iiyjnk_fW<#*zHstKzP*c6 zTjY(()nC7vvF)0}S(<)B(v){A@s|S`viyP>tXf9KGt{cXf_6R{#FjeLYLD`*FG&8# z?ss|cLGs8k|KyTV?kb)1D(1^m*$3^?3`^GBcdp;|W)OYvTfO_f+fMLqhr82i>stq% zD(A$MYRQ|nIrsJ}KbDutt-Jho`udkE zx4Y^KtM2;x&>l@QzH6{1g$|rE8DtCfTqJJ~q5+=@m}B}A*gt3b!+dQQ-w%)ZNY z;WJC3Quj{XsT7>)+E8Ns9a@{h&FCUco1b-8g$zeKTd5-ZV#VAZaA;(|=Y`W_tfdv% zX3kIRExw$3&$g;grm6dDS~?uzIgIne)dgkXx$KGQ{Y0M}PT`WYE zqcXg)v2<2d_u<2&{F_F{67Amhaqn<3NO(T{u9*{rej9wpdC@fnOT9%R+bEH)0XTC0*@w^;R6 z+qS)ZeHn7~8YzNwbaaqsTC#>?ewA-6rN(tt+>c)vthUQoS&K0`(3zH`dSlXYbEfEe zaOg#8clv<>9m3keC9F=xgKUv?#`km2*fLu4mQtF+h3p2~V#%sARA)=xh-#iJ#EH=f z1iX0hqg#Kw^Lk!*g_uKhu~}=lO!lyKJQ^ydDz{Au^L;s`*4Eayb4%ujQp+#Cx9s{* z)w^zM94c#~c&@9Za$?v+ukQFlvP~v+?MAw4cDv&6?AWh(Ma`V)9%k>lS*_Oo54aMH zg;8)xbKfE26b8HH)z5`(4~jc^RZL7zeAw2wn|o^$XRu08cqQW9Du&xqD(ncClRv@4af*OCe*zPuo@6to!Grgt2MJ-)J2?O7g4vHw8~ffTzf`_@kE?zV!H&YqWvOoVudj>nTcBd$aGDb;D_d#<$%(6!0zj6-oBV zuPRk+Cb5#=rOlziF*!uM_k(w3CsQ zoq+?*wlN^FEL;B0gKeq;lP(*&me0VvCI0d;>s*|2QHDl9+a584V6Zjx9#?L;9lOh> zI?1^?)|@EP#g;*gKB1Ox-4Ic=zLU(Z5PCV#b9sW-sx`bzAL5IM_mYBIwSNQ)R>I98 zn&|M@8DAJ`oV6Xar;lwu=}gjomccJ&GN_|1yZ7 zyeq!e#>tpFkY8MXIauzFljTZyRA(kf82I6})K~AnknE}7R?Uu` z;q$DZ;xx?MBRJmORH+@G3ei63+gZ?$bhvC*{M^$G${hTLInDIxH1g!!=YGBtNO{W> z4J?v`3hsID@xDMBMTZ@SiFcTW4ra@-3H6&d7=kbKEp?C#R`1tXpIaQ8E;QY(Say}i zxA*nciHs1--o;!PpMR_C9kWqCcip?v=yFbZwEs2J@A09jz7MQLAB)Iav?slmYnjWh zSlO@GuXKD@+bnQHvNf}yq&1C7X(nAJ)cxqOW2zlNmBdf0rL;r2(%7=vYczb}`5$b-a-D|<3LAt{TMh0Gc~|RV%Wzk;KDkJb=ugg^&_&^< z620U==Nd%g!4ofqw+_|Ve>`+r{$f;jCNso{z-TeiI{6oE=AHARpG<9j|NgzT(?prl zbN~MREzgFdrRAk*58IyNG27l|59=nU&p27BtGl{>oDeH{bNkkZ)YZWKx@*0zFFmH)ZiVACLf};&^ zoo`mi<5N$rT4rFNQoz21S@XWanKNf{KQJOM{Q*!tTWpEeJ^OsUXYq)t)52#HfgD}M zw|u>`bI%lY)^`O_XyohRBuH$1&FO!+dF$pRmtJ^WNvcN9aMJL;NRC^_kPzqc-28bb zhrI!orZC~q%DH*IH0zfMrp!{U{D;%tzcNuU|GBN|2w9l&+hk(Vu$}t1%1CC>xI6C3 zVKXHvzcYwkMK%s@ZthcCf&C(nre1LASEfih+q_tDuD7{ z#w54p?*ak$Wz22k;d$d3(UmXv0QeLsbXsTG&g)#Yi`5fx`CU*oT&!44cvV(n`+1S1 zuh^=Zt;;Ss)~+Nx+otmxkMifwXgo65suYE377ndX*Kc)EPcS`OWUZ&XVB1pmXgUM^ z20-LL=CFiHN6CGe7}J++~wh@8!&UtTlDjvfitrLvsC) z!%0kEwknw=?Ua?`%C(?&3dgvOeNRDS-)}mB?^`0G!j=tl5Ds>5xFw=ctNp`3LT-N-5USeoYgo0OI>ZT*t{hiNKktt#jWA znQ?*~9k3lC0I!_02vCDeMkJc4KFA`Wi9nHRkhM1dQm9+h&a|iakz=YY*4pZ&daRX$ zTg|TXqt&jd(#JUUD{pj6aJBCqIu*w5AxNC>Ge|tR#!qC`uXr)U*lp+#!O?R{axV$M zM$GQK9Mp!LJLeg{HFZe)Yi4zoDfNfJNGN zu%$r3nxV3*p%5pbUtzgB_T^qdetuiK!0`0aD33aa>dpQh4h^Z57H)4oAB-`Ny(VYK zjG9%aM#%#CY;Igti6g80Z-`=zy{T_5A7_kzP3EChb^$r^hX+D};1j|hy|k^H^k>0# zv4vwtED_9QE)p^Xg9V`3K0;}wUZSCHQqYB5FVKMqTiV>uU>0uuvM7})< zdzK)VMk1v$Lc!ysHoM$qHPh+xv4HAouA}g=45Q>SosW)1+Hv*4aprUNeB+wMLG6=l zNplC-Gc4*2L>$(hrnmYYIwfHKU2gfRj?A<@l^RrwQOnb$mDZG;Fwzh`Ued!%>SYl7 z8!OxN4*K4x6fFrW&-520{7h`wmAkq+gd-A$?m=M77*2_Ho)eA_hnxautITEpNikrt z>N`8kgo;uW5#{N-%nx|njp?sQe(-Fk$Yj!>Qq&pGWmuUreO6X$E0MH@of#pO50mw} zu5{+iYmqWDU*H^Au!Ew-Dw(!q{Laf9l^ySsHL|u1MAt6-_^|G0CAD6nM4;~`(05D? zxk%3a2$tN zv%9T#0IKy>7`xy-FC+5u0WfMO+o7^V0G?6n;|k5&_9j_4+kvudzDcgDaV`eEeRaE< zzc>9g-!SV^nY6rV$Qm4O6}8+>WuLCBW=>GJ@duqBPRUuk%t!Qi6b?nEQ__h;a4_A1v z=4|TIgyPA@*Lk&MQWU=!q>%KWz`($vEXH`a<*D4!{jWd0R=Vl2?a@Rs$64n*2&f{m zab}@XGu;^(FJ2sOj%<@f9uWz=xeewS0b^p1Djbr#?9xZ?|6Ge14jPVTth zI??Z}8FVuYTo!$8 zg6mRnf;vBL;S!N?^9k@*LjCp0>^+^u^+u)R?1{%2o~b9_utF)olzHH6XLo9wwj6(} zsN(!1a+OvIEJY8WPMm~E#Rd9n zC*@OZE^6ldd9HA+c`eVZaL$zNa0X`r3wO3A9~V#``^?Ks?Sn+XOv}^ zWvKMxX*&x;F%A8EF73D*>BmWcQ~xGvXIolcHU*Utn`TC&_BNkv;yv}oh;F#))y)WY zWAd8kcAcF`yo4wNyS`#Yzx^Fuk1E}a#&JbOyrmjjs-8^dw}!S)opz9_oP;oH)z;eC z91A&sJ=NL1bYtfTrELIPo0Xu z^!`k`*L5@*WtfiazyEVjJ)Zq(770YMmhnNN%BP(w;8L*;RGdAH#OMdB&0B=2K|amg z;Wy=(iUD5Q3B+I$=}T14D6Vwlk@etndz~nx308NVKnj`IX1qUPMCLfA%JK z0S$dSH|~#TtPok9oh_7X))$6*Ty(txdiE5k*|Q*6_Z9AIs;8fJI=`{Edk8JF3Ox`j z_4N2$P46pE2Ln6_D6jfpLELBvy)MgjRh;3;%?Cl+Ypp@Oj?KOqaiT%W9 zWF8gS$yS1CmWjnPJ=vA^li%ONO-}v#Es5re6H|M|=~ayDds#N+YYhPA+#Duhxf~L3 zjOpj$W^ab;oTEZU5*?yV(O`>KpSHuLnI}1;%zFnc+|3rvou>HIa$4@p^cPhwNV>+e z#&LO0??IhhwQCtPbe>;l@L*tgVPfKzvrTGfYU|8;bx`ycu)MRA&BNxMavcJWJLd*Z zp46Zc(hOtQzO6LVElAvcG$uuKT^Fz1bhaBT-r(PE zo4Pdqoa0q5>EC%=xpZqEZy#q8I0kj*lUGhCXyl9%XuBk^NDyL%Ib}Kn8S@!v7mbsl znovse151f!2KK5*+v)Z!TJ+RlqE@HdEk?e(iMT8kVA9P$?QD7ViUGOu^K+QC5W2VJ z`GKLlw@szQp?@#|yRyuMzj~6=~yPIYiDu0Qr!p05ez>mlhKX6l?eIP+*(_n zDc6XT;p~SIBTAsmv{Y6PEiD=`AW{@4!t=R4c;ZOdr$J>1JVgK`r9j0%m|nl5=P^*2 zd4ltOG3c2Z zJQ##3JY5CYKLr(t!g1Ph77kRF-##|b{-{h$38G6G-GA5i?NloJrAs%@)>p9qS=^74 z>B{3JXe6WzVi`mz9SzJ{^m*ndL8@47sNfc`G&%+B{Y|=(1-120#;dE-s>Fr%JI{`d zsrKS!iXQb^cI9ZatyVEQ&$WSK)tOF=#x4dX8KASmgpJ~v=Tqm>RPZ-ddV>LyFsokq z^C}1)ELilcdW4!<$P>7>9(6KdRpu=zP0S)R$m%_lTs&M}XZjbWG;1f(mDtXH-D4bk zD;fb5)%bGC=s+1xu5iLT+r`7F`{zX*3)U$^vprk%6RjjsmFQYt1T0}El)Xd-2?N+_ zV{>Cyu{s~E1KHkiz2B)C!md>&$-=r)eEWmbui5LtsHFGHoocNML_Tk977nSQfCXCZ zXY|g*R`!|B+X)=z*>&6JmUGM&>~M30;>Ahv*WDIR{;K zS>#F5>W+wa*5)Cm^QG_ypa_N4Hg{!-XO>4#w?bK?h=&UN!mI?BK`9$7*JKXL>8)se zy7>Oyb_>OJK2(A7c%TL{)H}9x@*IoYooq96HF2qq&d!!Y-^8wubfIO24wn4(dCTnp8g~Sa>UuEIT2jb%=w8cQ?}H0XwY_bf z-9`EiaE6ojHfBFkWoso)1d@iahVEv1iQW3bVgxG<&^hd?iqve?uxWRd`xmN?xTXlM zoPhIuQvd_l)UzH1FlthpVO7w%8y$*s`rRI;6|A0e>rApzv_@uCzjgOsb4hd<eP-5uH3D>nOf3+n)lNdv=SYkm`RY(@3UM^J@!TdCxV8<=wd=QEE@l8VjI8)-pov^@`)J% z$9Kz{W9qd9ETLOg*f7vhAN+=49WVG6DrKPgMaa@};my*P(bk1*t^LZz@T(bNhPO3) zeN0m%0GevF%xMdN%Gw;Y9|ja(BT$4hJ?UJcuV>GdBQq|JUYY%!D7U=& zEgiTO!FjXgpYPGsvXiT6@%e)=`hKZu-E7QBPUFt5BwHIIt;n4i9llNG_cQ^rd+P4` z2FaH4_@^g#&3`L|T(}jtIlEsw($oL?)8c-fInPwHdfZOQXN2jQE|iZ$=fz~Hy@#`{ zwFmcC_BPg|fxOzhpTdL411A6h#T9uJZq@qY0c1Hn3k1C+_|nWBXhu~K{U znUD9wxvr?+Cr87$qQ9AP`$%snibZcBJrli^-^G=}2?xmFcuiCUR`JhwU_W--B?e_CbdXLKQyC@d_Ab07 zCLz?Ug)Ps78u**&t#>Cya(yOZ!r)j6a_#Ev4o}DPhDx-7#C5q#?jR&0K@R&Pu3vf_ zU>7}b*H5%}8$4N+3BlK5HQoRv$cLi&6*;3jJGQ@UE8;UW*-|BgV>&lj3yUmjJ+bXi z41>*^TH-#8M?cF@t;Azkf%)ImJlxqAsM}TX7-;h`9*A6!6uX{C%Ta8N z0$YKxYA`(?iOMptk;8;lFUMS`#9$izja?<+g4eU`ztzf`?}Ci~~+!9SPR zSGr$z+}n0t{Pmv8po-Sma1&Pv+}G`T@O$J@H9VkN9Ev+-QCmh(vf3f{`$GZ$?!txd zTf(9;l~rdiEZbFw(BLo)y#RBTZcqsnjSp_M)b@@W`z(EC>$5V|l7z46d+!zbE;#J81jXvUEXVo)8_QCG5g zB9J-N&saPQJc8*q1dzMW+H22_GJih0*=l%cpPNc<_q*YPX?rLfLS?u})-PPsfIQ#X zx%36yJiQ|=wa2LMmN`1piY5j3IWl7GlutpGa0jK!fSs^xbx~Bpp>f`4Caf!c_^=Ya z&~3O<2LOyr`6r&1I@Lkx!{wD_=F>ALEW2#t-JLD76YKZdU^CD9o*`=ve@?O&Iw_an z)_5FYI$tP{3;?rRSg_!O z68pX5Cob&1E;~V+y|{R|D_!GW;bq_o=JzF|f;Aaz^o#DIU;^s2#=$O`(dVmOz>-p| z`@*inNFQWA^%ZB+dihC(dBWkW`lynv%Z*;aF7q^rHfmBYZwXT&T4Z~>cMdkyz;ZBI z{KeFvK((gvG~`nx>vn-F6;DX}?VtA7WuvPP%m4}CgsMJQ-4PQF9^{w$qa!r``t)P0BmfI4jn((L`jC!utQzzO5es9|Aov6L0Av}=P&=WkgRFsIYKT!@gVw_LX_zQt6)<}tES#-DV;H|Fl-EaC$W0Uxi<2dqy3=+5JqXtSOSWhYxk^`0cw>beqHr;1`hXvoZu@B7V(X01MpCU*D zEeyzV(R0IpUI0N_|9jWjeJY;QN?ClCs_|3G{O4=4jF6gYnEnJ~Sx$s044BOSA8s}O z12r8tCcAKpG?ba$cZ+*>!1Yu~gUb-GWcMvM42V0M+7d}TcBC@3WmooFZOMmg9#lO* zrnGfg2|bvz+voOEm&hwc-BGb4UHldkV&}C-PoS6g7e6va2u)qxE!Jn#O-Q>GC=1$d zU0?2lw-iy=Y$LU!Q{}h{dpOHhdxozlTP%z{M%fYIU-h4GmQVdyD=H`o+hD;BuJq8@IPp`Z$dwj9G`v0MphD=#JMSje=-`vUe>L-i(Yv;gHgxk2`#k*EQw(2W&PAu3^$rx@;>MPliLi*q{ z$k0xHiV~`)Qd;i;2m6LAM*p*y6eO^RjSnJxI+C!F(sutTKXJwdN4%n3?n#+1Z{89( znZ+VnzJI6^m}u0~bn=1-o~a0qX%cyZ7sa=Q-RedO<$KAk>hj>uXp7ygjt9`S3ln|+#Ni9ipQGuEFPfNN zSg2Ze{Ji6p4cm7Du3;HxCX1o0Y-BcD)7;g9D^P`Hk^TDke)qdUPp+!|0rsAbZB@yx z6v`D&HA-M18pE^5j2ze2&a3BYsAms-tnHkbn@aQ7%&D8AyFkH4Z6=4k_J3p~))wP~ z^jo|wHvf^6@VnwZ+~~diE#o+~##Q2ELC#_rHK;Zt_koP2i#M44J;lb zSaNhy#)17hTIph#S-pJ&uU2&Pe9Uzx*-%OS_{&aKO?)nQ^h=$kRV8)PV1HP4JypKF z&1zWoku*b7Bh`zU-)VrSsxh#_MD zjd?NuadhZr`H1Uiw~fH;Ko7uy*_hdCm7KZWb5v_;%o33){hwS+;&^?b0?Yx>oPHZ5 zRD!Os_w$26d-6b!Xt=umF{755rhQf}I+pYjR>HvTgHzYd6Bkxq&lJzFxoA?O{wcuj z3OlEc0fq>m2@pHJUL+n(zMSy#lKfuFNg8hPZB8C}KklIdbMYhNu027fxpD{ID6C#2LzN(FrTWI zsB4a2Rl}0W;?kt68I|Tl;KLzO1{U-+BQi(L8$aGJ+o)%-(XEsRF0dnE+)7O_{_Rn< z?^dQZp9S}U227QaBr_e z^b5INxh&!%3glICW_3qYpe`kVhNIit7L6a@TdtT&gLTNfZMkUcQJr4VEHS4zR~t_w zK=$I${-$D*S*F+^x-w?z7Z z%)<86!j510p%lqR$kOT#MciYKb>aI>_X~TnC=Vmu7Y#`ATm$PGLoQa*9_m)vo(NPg z*O5uao?Y)6eY%NLuR7P-1Jt*C&YP*-p$%F|AbS-swKhXh{m~)Hzy6tOxDO?>rv+-AB}q1 zKt(<~xkkF_$xmnsgHNCnsa!$&>un)5_#7!G*LJEvk6XC;loCea^Yv^lVh&_SkL_2S z#oP=rn)@9kY-FD?W`p!>y@mwTGzH!&Mq^$1DREAnDh*f!#i>5{(vZU=0>?HgUABQx z6lBw=e7)r|8xJ?gg7v1COg_ItH9JG3(-CyBOrSdYtNLZt6dzLK4%;Q1Q(KL$YEO@F zoFUtbSu*64Q3wWfvGL|m#bKO)=D1?vA|IS2h0_ExlAaumd#T>NIm~jw)8Bj3jxd6< zmU-*-(-bLSaDB$Ohe+AV>DCUC08l{=C&2`tI*c`+Ti;N8UYXJymuOI7Th z?QhSa=I{`C_~kmV`OI4`C|`WGoB`g&6u5Gd&rghS)PH|_wwwxm=LxLa0=>@kY}YUw zP&oOsu=S-b`S-9GfrKw}>sE$2c(psXl-$O?ae;lPH{W8{hnySW9CCymwvJ5CUW()? zFFTbi9B`+Xa^mDe641-L!sT6Zw%PJ$;guA{OHCw8C;v7aWS9udvVt zP^0!5%h<#WUdstRqp!bOQRQKp->oMXP-mnm5Ovcn>W8*geqyoh+5IkK;kf}+T>3HQ zGH{NVet%_0xO9qTcja*LLY8XXPtP+(iJxp~?vfE;3+s7B9KNFS8m=#YeW{FG%&Fz3 zz|pL9lQ`uLaM`Y9zC+#%?h#W+?5RHWU+hh%eyA=z7j6nV}xK zwdJ>DU||4dvbgJ;m|4J-PX1fiVOfSZ0N=JCYc#r7=o4i;Y>7YzT+3^rThFv3XJUEe#7_)x-XR5%i& z+EbM1Cf7b#*f>qk6#eVY^X_^X;)>1Ra@A8TbLp3+v{G@Z-uJF1=SFY&2)DsHa>v{I z4^kb+aKlnWAYE4%yoZYTI|pKVwwn9Fod8Cc(6dE%(fJ90*-{u+nErJtlOKGQ+5I1s zf*HiaF(%(a3`n}zEe1lW*z}kTA%eno{R8&HRqV5!-4C`6X0HMQI-7M}Fj(-NDTF>R zN`j-Jf_FVsV}{1V)#(n_P@L>%#*oiwd3}CAhQa0^f&=Gapy)?pex2O)QnTYHWPzXm zTUTy?Ghtr_?>M_gFJRdtUA{(t`@4CI7nJA~?N5{s#b^R?uLe9@ zujz^=w68pvFZngAzRa#G2Td<3ZI&lKJf3Z#!TlFZ8xahPyKDak)fW2t9)x@&69|sp zySr&aw#1b7W9d z^j+ydQY$PT{5KiW-aC|utspw2n}oW%d%JpLRfr1XtCRVms0BeV`-S`J!2g^`Y(M3hm(}!Zp;{j5uLyLv!JSSaG2l$Yu z8bW!n?GyAKV`EeCeO+u(TVpFrD!_vGfpaR$UrMr3eeG% z=g+f(f{jtqMP7N8VdK}EM@3WV32UfP@AP*pFJ$H zYW%L>fL{b)Pl#USDnkvbwu6DKy_aR4qxXMA>}gkAe&T4|t7hm`XaJVjm-@jkBg||! z-6p#-2;a>ZbD^LTV2==Lo;Z=EEd%=12jf;R)wusueW~giHM4=ab>1C%z%rH5cI6DNpCswZV6;Fo+gkDL^iTbo+Q$8$LSX3feT=7$No2ffcZ5T|-^b+B;2F{WCHd_ewogx)0Yp zg~V#T76mAo)esg!>}+smEv$ZF~+Us zV8X8I@H}aP36r|%aUZ!LzH~M)cH^bCZtF#3<5fP|z}$-CC`RC5Zb3epPsm}l;piiH z`FA9F(oHFUy*eB!1#kWb6IStv;&2H5miL2am`&YyNR@a|QYMuXXazPIr(w*al>;0l zo|lV+nfPKa?Iwf%VMHqHun&BL#&dAd-F{FW;PoHe<2kUwZAfw@lKH5kYWY7~HGeZm zC26(w>>4*qBFKo}8T9(7@$p|S$S>kPhym8RRgVE2N2Fm0VnB`B0{&gL|G)dPXj#Mm zpBdC_D=-|NRT}~UJ`gz2sr|vM5%JV6QQ<46_4XX?3kS`z98r!4w~09(7e7L`XAnEgUaf&XSl~vM9_Bdys;su8=a-Y zhf6oYHK6;^eRf^~ym7KBxN{X5lUJyC8enYXVV7hPZ>>@(=uuJ1buewU+**0%MAzl? z>nf_<3a~|VfNiKVNt%22!~M67VQi)|Jqe25ySU{y6L%O=0Z3Vl`E^kKEbu>RW5}-C ze{Kn=S_Jdr+-pw-D$c7ynh{zh;BpGr5ll~A_gjy^psED)+@&w6np$uGRUn@-?U!IJ>GWt?8cuoWISg3f{#FS-gsW`>^-2@ z)sBE8^iT~z9RXW&!!XW!YQRbk@8(G-N<_VH(#*A5OIkk%{2$id{t=HyT%l^`tmvnU zC=vNjk?aZ=Xun@e=b^y|RHfZ8iL$jzx$rW284pbCZOPQ{94-5;-KI*MY_l`~U_ z#KqstXvP9_m?~r}=-NzgjNk$#Ic&wu{MS@)rww%8`pKdFC{Y5B`ISjvjx_3x0=$eb zjLi39La3F_eVWx?(J)ku@X>r!VO6i`b!rwN%{vFu)002_H( z`m528B`$hYi~02o6p6)Qac_3s5=t_u0=a(+yLlh~sglU+Vv*PF4IQ6Pm!8esL$O(?3j1k{&7U!9kVgCYSd~r_%>3#;OEZ4{55J}QO01z*HlL_?X zK3{JlU{0@Tj|e*dntV|tk{mLIkqO+t5BZviVh4}?AtqKBqVcP6^(!S34omK%7W7&gY5U7xjQTKYvL61aQ!-Iv z)0!#=t{hIXg@M#qUBr;GQj?s1X{l7`HBl&h0#zb^N#1{A#YAL6E(=rPNzvNbp(ANz}9hZ!)mct48 zmyZ*;MMXswBf>v?eE3`~2&ns zGGYsgS5Zc|&@)M_UnB!wR0-AFg<%UU!{rFyZl`JK2BP! z)spnr%Z?i}oij>>f?$C%0S_%`iCSrF2zJC|f8|{LSQ-uP$Se8F6jp)`)=xsVsZ_6I z9`S+70&Kvi69;=6?zt>H|GFsw9`%s{=d~RcVC^gNTD_NkZOz~6<@VYCHK}jP9cS8N z{Z_>177yo>t?MSYheFx84XW65>F5+A?%&fmi=6cZLOEM$1ov!BTYq%G8FV!)9-0zg zI(uR+v}iVd5mwZ`Zpa$U()u<#FuPs5|0q+NmJ;F!ESCIKCCxnQ)EgP2ncN1;Qe>n_ zWwBXeP(zVK2PPkrXWPV8px$q7lpEKt^il;k$v-r+_QEKH z21Ki3fbS!`dle8fy*Y^qHp^Pr#>*JQuu->EEDP! zPMo(2*D8Yqiue~q{TU>0!2?djs*eF+jQZ0}|KGG7L&9yz zx2Jy)kH38QJzENmB{KSlg4)63q=bWU z*2k1zWYaB31Av#yIn2>nvtT#dWY?XkJz13^;o&WjbeY~g9lo)z;z1R90q8z5C%Q-i z!V+5n|JgYPhBJvV!-Zp$eqqNPjaGzG;-LR0s`zmg$JZARfqVYEI(f44In6PR47$W% zc#=z<#^AxoqOeoew#Tj8gxh9)OQTC{>&<6HjFQ?e+CA%)v-&6MHq6N+#ACNJzyerS_$xi>mc+Y|^b`FrkT${m7Ut@vPg zfC0_;1l{a#aO^>gsN-V)vRqjXT?Hqo$*4POJ%@*Sgh!5Fqy_LwV}tr? zps(G1aq;3L`+-MJ&UnnTXX0I?Hf! zSI}V(hIz-0;A#P{##GAFm9QL#V_G-SqUvbk$}J zs-NJAH=zR6ApQN25+UIrlH}}r>wzV))`{VuMkY`k^K^Q z0Etfif(L2{qL^eaZB(s@f^GyZtb3xY31_sZI{uue(2VDG;kw6F3-(nUzro={dwqf# zE%`58GkcYfE;NgWhD#t_pC&Z;hkY{ofwq5vv@o^YH5(;9E#4Aw;0*HQ)}!f0fugBC zL*P5%w9j+upENM)Sms8b=CKQp6uEpmF7cpEIa;NMa~*pe33g-?Czr1N?BNfNtS|h0 zXu~K06;C_G%!Ivx78tbGlljnUo{`BfibqE$b_m~YeG^|XVSh@H-DM2(FEqyYkfWA^ zf7!=u$|6En!o(<9BZ1GP){Jm&CeBC1SxgSa6)kMr*gL-xVQhhr=pFy70Pul39w3Vh z5ck}W2j^`#CZ^g^=KcCYG*x`I&iepRefXW_8H=VRdQ8T0q$wo3=iX-5!t$Jiy*15VU9hoEJ8ZcD0?5 z-Jj1yF^@>}LBDSFX|ASr+JmpAa1mn;avT0-k8r(J`n{^QlG`@M2ZKM?L295qrkQTH zGq%D~2kp6qUhi^~%y?)M#nVu+I-T~f_SL_E>HpyiOvs^A3_4}x$O~$;^Klee~RoAk#J9BkC zKnl%4n5BWs?CrrY?^kDo5vQ*ER5Q3Cty>RCMselA&|u+2^Dt)cJD zvvPUr%WE=FoLI1ox1-Tk>@*-2oGkip3)u~~Wo;U!D}4F?>NxY4CgV7cKNgl^6lCVr z@JJLhjp5W9D&cKjG@Pa(ijwA$+Tk_b7L{Z=m4+>2I%A5ZvLuFEp;IZEZBCrWW^T=) z<~-&STpn{CeV*)>e&`Q)czB*4zTeOBe!YD0>+>Y=intg`jv}MpHZ6O#rignJd?&i* z$&4di67qoDGB7xtuMw>jwSkjl2J=6_HR)}0GW%umhCRN&fcvM?r0$c0zU1N(lzh7! z(HjTgCQZ)ymGP2&INh)#_ST_19c;{^=AUlLs!Rf+byUnjI6NT#;6#=vOa@Ow_va`p zgm5|RiW_ zQz;1c;7-6wKU!#=l6jY8pv!qN*>e zsr%zrFt1tb!}1KqvRsHo|2ou+;d&NFGANPT@zOHb*22%hXtCq%3*9LQV@Mr>5Nb8G z+D=FhB=fVGyq}6LQHz(fEh+W3jy?UXSaM={;+3F|Mr(k(1eUqCGA#AZQLg32kSrBSVw=|*`!Sn~~l4%=d zY!m*xUt$(_Y~WjIxrPB$;f(*sAJ)SW&WOu#U``#0RaI3`;0ADc!0(Ac^uT&j*#Suk zG1KiOie*BTnz_dssT_tkqxm+vl~%H|TeRzNN7WNol$$`##>8l4&w=d~#;`a`_g6tD zlCL(WsOrrSbF|Oy_#`l%YUqlv6385{hFUBnx*BPU{_+7zz`c0%7=-EW<{N6PE1 zCKj~uZ~72HE`!de0Pf^?cAdROcdf^;2N@}7z!1;R&P+Zg9CP9dPcG}n@qK!Y{K@Q| z4~Pt!dkd3lwQ>S*ZTRw8x|}=^qf+ptU|z8_YLSQ5>9#gVM-XgJ!pOK_=vs(ULrpae zCkB5G%5;M=;=H|zrIOGvyM^Yd;1s2A;+mH(nq0g(-M!^HGCA8i)Bt)7 zqDRA68}>0oXEIjwdA5yAHwuFZWFbkO|D6qUEAY|H(_QMDrqLPEhEC3k`ixwxI~*W` kjRcDCzbV~+5}}du@A0xDIyA|)cdgpMd65Sl=!QY?UqfP|)0k($t?gbo1_ zrAQ}1I!G@8q(dlgF84Y2?sM-w=id9h_x|wP`vbDp%v^JhIeue|-+HB`p+ZZ;Oap_# zXyK}NbYL*bKp2c-?8I^K6S1zLN${VW?n;L4y3RK4o|dlGFm+3J7YAo|2fGIsJ*-{b z?3|rMgd~KnUA<`Q?(X6yB`oatZ+{@Bd~w{?jO&?E>XR7%Z^)4;Tdub}twP zK6U@bQ3de(s7wFiBY}5NPe|%Jv3Wb4l~Gw8EkDm@2c_iQeQN$*Q8g;=T=yimisump zW@XsX#JbNN2F*Xu+U_^MD|kUE&`Ur%hxeugm}!)D$Oin<4D<5D`X7)Kiu}G#l>6@o zk0h@=y{m`(aP^p)fbW5 zj+@7!bBc1cwEpQx?L%)1ShpNP#S>O1uS`3icKg`s+$KqS05`+aX1x7g+<~l&U*6Ms zFY?IeXNu>+PYdRok1L<7&FH>IH#(I1iCVw;`ST6W!FzhQttB&k=e>_~Y$tU?4BC2g z)TGVuj}-%0r82p5h!~^ik79Tb?dO&ecL$1|9N_&smN>SMlV9fMe^ge;$PPuNB4FKT zC@+rPxs601DsFId!_5Y8dLGW2H4@0DwLePK9pfeLb=9V8rpPJDFDHw!l-eIUQx-sQ4jZ?DTPg6g&nw_Db;X zSGtdK9aDr3Z)^5BW!~FSYpHVa#w`sX9<`f&^0olaMDL^Vk^S}7WvnSa=EjsT2K-_7 zVo-_^(&I^)%2bMH`^e^XS6 z$Z2e?nKUb}zA-J3>WYd-k}@6XWv3^2G}X{p8C{~2$=5d*CX)qohG$9^hg9U01bB51 z(EK`u$s;~9vI+6~cy=w@oOV~?bGf<4TC+XYHg_$TJH*Adgc<_Yh7Wrmj-8NY(9_<`=Hr>+0%y{ql_tIoY{M;6q|HWsBWdlc)80 zKKaYE>H2SAlp2fM&D&#f!-Hz*^>*K4QHMRUeLt6;nYqucxO`JWNkvMw&jb_ha6wEw z?%*@$WqrIl0_VVr6>npnT@x0cCrFIMn(VI6uRl0>jA64c`<9>RlRc_=Vr>jo|0)k0 zYbqz%ReL@-^$N+HkS{Bb@eLu;(5&~((^_B@3nWPPhm!7k`phK;BSSaEvOOiNn-k$F z0fpM?gIk>~$9j5mZw+%rRNOzPMyE%}TcoA#`!+o&G>^Vbn&;N^JY>aklG;$Eye6&<7^uwza4t*!$*{MDkKZyuMr2 zNRv3lun4scdjo=S0IN<-Y?3>|uU6cyWh)<+E8G`ma6) z5o+~|L;NjZ>`y7`#u={~+3~7aK3tF(y$~m4alJjU>i$(T+(;9<;Yd?7KK=3L-mwX= zii6eKKYo>7ZY%L#cBZ0Z$**|kFn|>G@6rnY{B`pETCSt#($Mvm7oy_HG+5g^Dk?0& zmgnV{4hhijpK%M2c6(E26w>ayyxiD`r?t4yqW#0mb3lby3id{taf7%AIeWOeM!U#I zaRR_4v9!EgGR@UiAZ9m`p$f?e@;}s zckc~ue*m`-oORfv+~@Dy|Kw|8i-N6akDPD&s`=l-6#N##w zD^bTotorav&n#x}wP!osKHvl~%PrS_c39jVJuDxErs!L%!5}GZp; za2c%$J~AgK$DH)z>%jESpC8T52Ksx=Xd^oNMh`y3hAE;*yC|f5fhjGb##kREx|rT> zR{m~s#;PS++gH4xlcfL^$t7r7c{hnnr@eGWzZcobyI|d_7FS?Dvn7w!9c1KxD;&`# zw-mYqy7b8t!;SiC?!7N{Hr|T^4{#c~9r^ z#|o?!?0U?9_nvB>OUpGX{!zk2&1-sO*a zs+{S2p??^WnG(mDt$0zlOyTb__>6`u zAS9##&S0%yUCzyA~ZO>eaaVl->>~bJ!*%e zDQOr!Vb(TRqvNtvK9V)I&EVa6CS_tv&*^bv#W7NzPxSaxdf?y{kBOkTI7L6QB)G{?qyvi6VE|es|#6+$q=Nc7pt>#C2I?ui9 z^Yt*OiGLO0bZnbJwM6#%v9%ffEGCm|z=oy}`K62nrdih&cV#1MuS_4DzRGvt8<(zW zqoD`(!4E=J<(0z#<>!<~+_%YX#(3*0alidMgZ$Bt?@z|sZZF7+t+vTUltfUqWjrwj zYXd$n<`vEOr1Y+z#oULxQ_rTxlhqkRd%=!d@bt!iIlw3CRn@CstUC*ctPXX+DCLb) zia|8fx*q~kLx?H(?YQA?R+aFSXs(^<2#$&!iPR7G4`c>5Nn4eGX14K;jX&gZnBSwB zj^oPkM5()fS$OR2?b9?`7U(bU{$<+DKcFtDT<&Id%uTl|Y;ue?Wu(r+YZ+>B8ar## z8k0$fD~IInU%EGohX`xfG zREd3NbJ}r}L8sB$w3n*nE_qmZ1FOQt7~7E#`KG3(qOL#Pt0MnsxaK}(U#<=zmE4{e zW&`E!ULt2&q6bF5;Xd~hoqjc7G;)VmGa}m}EBL{K2ciqL8CDM-J`{6Y;t@w_?pPsI zV`z3)Caf!6ehF9Yn^rmm@GS28@9f|I=-HYK=nqvmYq7cOX7hO7d`t`S0L>ufw^^-D zs#*Tl*rOrT|aN^Wjf&G`HK6PE9YNg+9zBxX)pOk~nF(&HN(Ve#PM>7sdm3*A;~# zJ%xwIw+Lg`qfo2sH^uTS zRQii-x?c4>!tJ&e^yc{q)Ql7t3;F+0Qd%WdK7simv} zk3|E(ka{PkN?lQ57RRSG+)GCMe8&{=p3p=if2!gZeg(G>oid5)EKNKiaT+N#AB6D( zsr(($gmr3Xd3X6krC}y1zCPWXdQF?+=_9d? znb*$;A%-5ei+dFs(R-VblzRYb5i#)sP<)DW%O!8xZ{wiz$u zR?Cd9MxPY3uNPIL@Zx|J3A`d(@E zJTms{57<*ZE3Fj7x!L6$zG5bOP)DKD_q^ROPO1!I{`s-$d2v}GjOr;L%%7_)q-n|{ zG*VdO<^0PlOOe)j@D#Zk=y&xGujG(foCU62Q9hX&t|?C;M``CH2*u{2xD=K za~_|0$){SgvOCYe-6NLMPxH3F)D0@i*qHkc_dvp~WRTXvqIvpsnGfq()f2BHJU^zz zNFX%ya||_v%lr-^H-A}Fcc;cM78qu2*$8)TfD%yHQ)pvpz+92M^@$iwFO<>seAj;S zV@Ju{f-?gsV$RC@J#M@*{H$CzGlG?KH>XDQVNbsIaTfa47tKNAy^pm~KsnX^hxi-)#JtXPepmso7F0xh?(L8$Qfx>&-8>qv1m>)C2Hd$Uqd+5SMj8(JNubc0T!6P z&24%HZ0aC%`dj{vbQfnB7mpCd5f~S|mFUn3rd$uNK@0ek5yU<^4Dt}eZ?O?2(-#AK zp93Z0EmsV`!JYhkAs^3OmDiOXiZ}g=ax}~6m_^D9(yES+Q9q>ntzBHB`bSkGkmpO8 zgE}NoEG)2%k|G;?r@jpqA(p-P5G1^{+3fq0?Udg2Mg;W}I4j=h;C@?q~J)eGEh^D=Vqy`@Q`3pHKO2?gX<;uhWf z+G~`%GpO!&cLzEC{$$}g9qqz%K^iq1^@&P8d24E`!g?2{=>LEn908A2)$LS6XUm33 zZ??Jmh2YdyM80vrJRvu;DH}PkwZLIq;a}`(wWU)aL|WXmEHvbTLp%{Iwv8nl6Oj)p zJm527gSGI;hm?UluTCq#?AkM05zr}llS8;CF(QuJc&%?rK+1o${_xfit{2}}Qj@?xMU4>)qs>)j{A2&wwTcaV1(1me+Vl(4MZ zu%mCMwFWbZICL*;g?5hLT;1 znGr8*k<2sb4O6A~P64h^US2-C6TDI4x^J`Zzw&Z;6A6D@jV2syPq@RHMGq`(1rO#S>pW%_pHXWA6MbkEH1-Dv#<@XZ*lw^sECG@3b9S|8H4v%GWntzv^Uw-ulb z{0u025&`DwtiLjkug^|4DIN{{W^Hzl=*K)Q#pJ%m~}T*Fb2ps45#S2USQnTEr|8CXZ=*J z7haFp_lLpWu0DA1(4A*DJgsV6i;jh2Z(>E;Z-+pq&ue1*I5-tNX)dJmaAlr#z$$2t2!F z?yWSQW7I_R&abOijhWPa^rkD+H$uIRylSb{=X4dq}Z1mwek%6~qu&h}9g|ckxo?UeM zE0Jmbs3ai=B7MoB<3avoHCGr@zMKsRpJ%xDuQ<`lDBjHjh)HEZ~{>f(`9Cuq&6hmIA(~ zfhVo%mv|sFCg&WcO^#L9ue_u6agd&kRu>Gb$ndH->DJQ&Dfg@Q(Cz>hlWuz6QaVZQ z?>#5D$P?Q5QBkD6!SGxRVA%owHvyZJ(~5t@4CU&zDWS*6q>Spfq1yUFm$k z#@vRN7CRd_hD&7^FSH1*y}p4~k}*z+_6EU>r9?#Nhbnx&M- z&uE|ulnhQYXlWE*3D*Mz_)>dmr8o`LPM=Lk@e#`bDw~|6#o8-oc_#T?M&qhVAP3?cXPg#%BUt5A z_wj66`%+@Lj;Xkug-~@96ey#SgGFL2P!ZiS^;~zU4on8?cT3Jd3Fna_p&r-yMQtD%_)D_UD@lpGT?z2^p5)X5CU?`Qn8vx=tqf4wel2 z*7&vY&@^jbDlFr+?;qMC1pJUhv0S@%{dysmMMtUUD}Axtu{&tWJsSJg)t!0yCyDcA zbNnfbBany&0A<~Z4%iVFV?LcfoU$yx;<@Hux z+18^8{tn{i6#Bt`q!8?x^oHyR3aZis8c+R9%T0`w;9a^CT$l;ar%JM&yS?y zZrFVPuzXOanMAJ506HGZc6i8!WBB-~9yMXnuKBn}O#p|2EwkZOh>e+y)?JrwUP$5i zGUtW@Ya~N$tK+<$*PV2%xC0PJZXgHB$5)$T^`Vl>xNTrzHo0IuTGJ!XTvBmk&|M## z4JL?{l`X-yt`=I%zfC2;3KW3Y3tdeAD&lMAK0AdZ#YfF%ih7Wne(6|bzl0N@;WRmhRb?uvn0n6 zRFrKC903RF(6v{1;6}wZ+9dm!^`3$N!@<(UZ4I#;gSTaIA)rF$f(r@F>7}`P@0TBz z@dRjHgi$tg7dDTYG9G_B;JzrPS`{#qHGaegoSUB^-Mg6aNv1VY{0Cur&v$((`DB1O z#OSIjv(EII)t<@c*gVKxuL$iYxW1S0hO9P)k5EOwVN$wKqy3zagY-rEnwtJZ9y7km zn9x_@wmaJ$ljDI3Fspp9HZa(#*uj9!?0QZfAlQsO&TD=Kq?$j{UceGxC7AdO*ZO_i z-34{v&!0abCOhB(q{6)6JzLFAUW?51%a_wi+_oaH<~I*+tZXbul#m9O9j9SLjJZ)t zzG2RX&j&4NZny4zwG{KRvQ5vrn}QSOTcG~e|rwXdNeGw9Jd3&+q4VSee( zGyy>!C~l(u^Q>Ue5G>*rRW{Gi+?ZD zZEmALA4WMUt~blXm!T;^1#&x*{qVe?An%1J=K)k<`Qav{y}c`Qw4Od`St7+ajH(!a zmHNGxdv_X{)^AVD^deVyV*)n4fo~We``urYB8Vm%7#jrNrt^@xKTzhdt4c05yt?}{ z%81{rI=hngd?w=fDa7+fih5Z-*4|dFHhHZ}qzn$Q1aEM^o^R8R(kq*4otJRP)kv1h zqOn#pS@>E@u-I{(>0T4YXRdb06`&Hz1@;YXu75M6{lfO zC($k*$BG4E@3hFkk*w7h0X8eO%UJ-E|7PK;)hW>UL`w%zz*L<8y@Xs}&cM2d_qpg{ zZ{q=u{G-7e$OTGzV9!GKt*bj?<>+;QtASRoA4hrlGWr6`RD&{4X^zt?**BcH2!k~@ zo~DQWOjYzc3R%SWH@bLQCrwH-fd!W_&^Ea6`6MzK6AET1<+tknIQ9 z??7*2m5VmXw>CAgk)IE;1dTDta@#JED6sGozursc8!Zxxx#awqAx}?}L&(^E_+ap9 zL(%rp`5d;<{zVCKaRYBP143QovfMbQ1QS$5!ru})3=UOto5 zRw2gQnfQd3^``cIrDyMC6-UggTh3{do03mEFve~Y4jCwwpoM=-j{FD2f)d03~46nF$=yy z@g;q*Yt8pVB6@&`uMKK5$Q?eYe4-TM;f!{E`UIVBOyD1aCa_FzBjkNSezutOej9^-a6b|*bQUxNqw9Rpf6V*g+&m7cDzBZPy17~H`W(0 zZm_rwxn`crBZQM-ijD&&VtECi+yQ>uS#&xi7S-hBK>jPAoMirBky(<3!>=#lgSqD^ z041vgqBywnx?cFAIO(RJtKs339s6=F43N5BElqsiISD-xPQ8z|<6QCrGB^1BM>V+* zj1Uc++;F2JD+6Fah&lhF*;w2|%bC?0To@YaEqdkX@&&H~0~cRy9v4I)5c{Z|*Xrby zI3bh6#r-YoANnV=ev>RR@A}qD3*fpZ;kOa!6qK_(KvisYgoG-X@m1THnRqdv$Sx{f09B;IW|y&ztQC z6`EJD6Ef@#UMonLYS7617B;@bS6_IG*{bH`cGQ%GVVSQ_IkGpQM$D!6PEyTE)@^=y zsJ0GDl1dX6cOn397ZS2J7Dlu=0&0~BEck3o;OzpP=(9$0SLCOt_-lK#Q%y^G=4R&I zUoNagUw?R9|4HQ=$h1^5$nPn6+%Wm`=YyVte3CJCCV^9*>%&{Do|jYxK2{Wr45r{( z*mOyg(%UpFB`2>xN*R*BOWfPyAO$tnPgIFvzUF@3h0e5MV5nfWYy>-j(G4w`J-?IL zmc#L`Zfe4CfcP29wWe4g;~~egUsZZ9#DZLgeni}$@s`NrFA1ybE`39qameq!#ghs0 z1vZ+@83?J8(gj5EZU;0b0!C0GK!%U8;r{H;$2&1O+;EyfGVZzWv*^+yCFiQr=VsCa z)pWbIszEpP8#`8)uIn6A)**|H-cq)mnkDD@=rsYuW9 z)s|_NTt?xo?w~tFj)8r=ANJgU&imCkz51~~)Za;|+eLy zq|OBqn@*F5S-d4ECem=_-TeGtAzoJ(qvvspxsMe2k>AH^cQ@)RX_;jKB@*(fc{FoQ z*5h4V+wN0M7ZNxG4ru?2lC`YgJX=AI?eKBVjYsWPjyd|ck2JA)nD=XOwgcPa==9}) zp@OzSJYl+HCda7_i%!2x5p+)Prd|%wS-UlJgByPK(RY2YX9bf)lt4v>HLEESGONV` zRRU6vqAxu+xyvfv{prQHkoH!Dq{!|mn9Cm_W|SInOZ6N{+-f!ne`9wFsg&XoasB0v zS-^`2wKxOPBoZbc!*@Cz7_NmFB(t%KlhfhR8VJ9DhB>-Cd}u*Wb#aISrW2-zVsg1- z6%EWFJy5|COF^ls2NwNRYGqTvjZm|O@3`i^h9yY3Ej@e9xLJ(hGyb8;7^)N!9?Tsu z1FFr$JJ`yTUQqZWQfXMaw6HWtMY*MCcsQTf+FmH z;o5n>#Ta_;`+GO0Gk)jASF+J-??1y}T-=1Cb;r~U<{{zsv)x3Du%-}@S-oy}WnP2T zQRpbahCo;bDy|xiuj_>n+Z;>1y}f1uw0bO4N4!A;q~kJDIl-BhKiCVR0SnW4CaasP z_g0HJ*5AFO3ZnxwK~f+q<;Zr!NDASjn={ImpqF-hqi_&t*eRe&wJb{>1vM@|^B?jv z!fLVKTT&#-hE4Il%W9GmU{hQabeo5Hjjdc4vmI2&VGVj0;b~{`sPoLs#!p8Ml)9DS zNZ_0pI2@!P6zDc&F8vqfYSc2|7qG-P4MG&7)!wa@_gXQHf2ec_G|8U1VQ0lF=G+Xc; zneA5!Mda!xIPDYCbWQd>#$H}&f0yGKyk2I+eB4F+q4;t!lEt_n_lOUW-lb^wm6P$_!t-+_?3(6pC@zFSwTj&#qRpYY?QEcA5+TXiAk0++hQ=-;hDdF{(lyB zeA7Vrp9RKSA5m*^X8CnAsF>>()G>5+>7Z^KB-~Lp)18r1gtBl&JIA3|ocs7s3lCJ) zVVP#kZ4-Wo!v#y2q}h;9|y7y`njqKAH~!V1&F=_MFQ5&&(Ty0 z5z}%k$0#KEcP0*%SGi7zWvdeP{i!OLEDH~bNYS!g5PJRURUwh}q;v+@3<91S9C;Rd zsk^n^w;Fh!d;nDg&x|Fsnb|^QPnK)f7~7vQRzdgv!b#w^;zmjKBgxm)A*MWA@5c=X zF9!sf03xkb$0Ri4H%<@;|C}M zBJKH+8_rTjip~nDN~Xm;$m2URmUV;sS&8L>1hEuYhFCjWwu*}Vv+KqZ$Pb|W_rrT0 zMoDsk<)v3|=S+FiUGyFrQr6UcwLaiKrY+BZnG+#af+NJL&~R~{jxBWb8%Ru|riYGI z`7!*~86WIl1P*}ITTTk}=({%Z;y^G_U?DIWEXN~RY!J;=SalgV ztsoq+tzFZE>;r@%0w*hT*gDGc%(Kl-k0+Qh+;U}e@sh5tq}#i zeZ8;rM*uOHr-F1V?Kv1~dyAb4Y`LvZO;UcjvugsPNBCzud8m+xq)#Rt;78XOXkU)1 z#9n z6Fen{Wk;|e5P%ab@j&?%+T|>b)F6w6HR(O|g*XzwtAGk?Oor8idV9LfglKK7ktACH z0z$Z-%byTwt0I7Q=bb*{JJ+~$WO6yd0$NKzdn|c0S};)IQHYZNM;S5w%>|L!yh2?) ztl?=fXTv_@E#(d&`4RjWk*y*AAdEi5lP3lbzg(NND|nK7I6i$LG3k; z14}g{Kot#|%>E$W#xf5E#4L{r|Eigk268sWHMYV0+~*5T{!2ifoZsyj2Vs@o*r_!_ zz_uj8xkn!gBD&sprk(BL+o5L!5cl`?HQEL?yp?tar=b)7DD`V z>J*GC9FI@(g6u{Zn(Dv7m9KUw$4+sDoZVN3ZwWuKdTfY*!CcNO!z_iU5j9o}_h5n4 zpbSsH4dK@o(WT7L{gWD1Rs8jYt^R5oOIui->`{exEx)lYh-cz}YJ24x2dobNh5J6Z z`@c+O{}TZIS3&Oo`9~(^XX#+gcHgJ{#0#FY_R@pHn)*)^|DR#&-?Hb4aggx3&z4vQBxV~K5#;erHY$B~0P@c=ms6w)Aui-DRq{X zqw}v0FTh}DAxgF7^rpzV?E!57dNNWlAwA$gh+f*;7^IpyUyK(FR+piAj{7{EJUU!) zHt*Dhp_0dg8j$qX|CXN^4|WE>?qyZru`0su#K)~vG@(UH%#EnlkHW@mHTHP zxonr)v|Ho?hn5ChS8>E!vz|K?#Lc$x<#cScQCt8%AFYjiPzX>`86|)$O)d&3S^Y1X zeNjMq&Zz4GPeGC@i=86Z08?!s9G_ZRu#V`EP`QhST%%0uHBd8N28!(85VyLoRpelU zu+oHIxJ9HjX-vcg3NHJOElxbtI52SWkl`IdOC2#MsgyNwqO8>$d;PrZ-zh1O>r11wwx5lfLV~~#(%bt>#JrMu zRi^1pzg*P#>0g}BN6gvLT*CB!VzJ0)&a_;ySJZ=K6ncFNbQ^E*A7yZ#MbhNX`L?}o2-yT+4`!$yHR7$?H|+^sF#k)f+I=kh{Fs$ zIC_DP!v;G!-J0fyKd|fpA;X(M0TvqF^X7*8*B(1JYoXfO=Tw9-DYbcSkI3Ny2Mt`u zmNXB^)}={Rd1BDRJs73S_D^DNe!``&T4c2RR*aqkMnB#g7g*(Kn+C!x+9{VFLM}Uq zO{QMgwVA`P_7jTW**K>!b>=>WWY`XQ8qtPjfYME*{;eF3d^3kUjGmk!h&JnH$tgks z7$}zp<$QD+UuSzaREA5+j5Wi{zg|MJQ^MHmFh}k$JCR~8Jx{O^H2vruir@uOE>^Gh zO>75Qp}(dk7X_LZdfO7pAQ?C;MM_19a<9av>y$n}heI*m+HDkZXgSgASa+@Q%NHz& z@TjxM-^A z%{FC1VFI|KDS;DGVs`lnqk4Q6$LufqDPJEk`}eXQAf&%zY$ zKKPSrL;BwTUFXbS2KQHz{21j<1(S?`4gnl(9)d_AO98SnkQMdccjYwPxE2UM+GfUe z;j26?Vjq)Y5g5>`zgb`eLXtHgZvUWpDdhLcoBsyEjph{k{!FWLn4AQCN05i zNFK|g4_vO6P^&o}VUUx#f%EQv;wNcW0s?FWKYkd(yMgu^!ZnMv1+|@&|xN;gS$#3wb-X3^C8MdB+KZ6d@vD|nq0rB?60USu_vFY?7Oya!n=XJCbrcx6~D7I2(?PU zb#}(k{90G;0BITfw5Bq1OdHIsWUMX~R8?bv-5@@`?C-gB2TAq>2F{9v1E8zO4(4x^BjR?{ijYdk@k!c>ao*ftQIPXuCmU-_q{`$`lUvkou-GRTlsdvvcs z6Q2zN6F}F-WIf|=5!8IFj~O+F1XQ&9-lW}djV}VS!K;O{KjFFNI>C_r0iZ(rcxr0S z65&~J9Me%N5{Fw}9F>10BwZ;DaD`bzmeT82ff^*=t`F98#^1DjapO@-cHwe|!7!6K zu}jthl77*^|8-iE6YkjVLmZB@wca^XRW^#Jh=uvTyw zu)!vNo`QOt1f~Nrbs9tOq5+a}0zqEPZzKzgD8@eb1=#p@R}uwVUv)b$F zSlrc2V36vAKmb?slE%o&%k1p|yzq{Xw-4`)`O4vg2F}ZQV(|Y6uypr-T@H#G@*uaf z?T{I@cGu@f)4$kuef{vhZbw3oZ_jn8#pT7l$wR3$EKdK1R1_E@3z4=fCg=1n2>KIC zfq;)&?mOaB=}rw9fbshLwvAGQVu!JL{U5+-wZqs1T46v5)19G~6|*32f`qJd>u;Xe zlrL;w!Md`G(Y2s-GCnkdOfAMFpM|7L`_X*5%3x&@pNB6Yj)Sv=^3uE8tYK(KK3=a3TSYs4MJgxQ?^=y&$ zNrh*()yjE2$}X~L&b{^U%G|&P3wHIMk)v6K7W>ksClz4l>DG)5*WQH0UAn&IMCsib zc3D+Uz+~OSok^z#NS$&eHo)9&9z6|Hy|uSbnskG(WM%U7Um-=q4_HUvAS=8LIenB; zVIC;z_pU;Y)QI2xKau1IKoXs!KrI!i3OEg81Pi9;Z&Jcv0p)*10ssGkXu8lp2s&P- z0Ot)Lt{wbK^LgXP=Vnrp`HDaxsRw-euv~tRdy^UV77z+BJAdzg1mUig6z=NlFA{Ep zn9M(TW^jFqmE8@N76djP`27RqHD~d`X}?NS0G0f}@#1IeR$VA_DDw@l>J>vRiQE|p zY86Ou{$yM|q{bw45Ti3p1&RS03_mQ~-#HU_BBX)1eDi|*{Sd5v&+b6Edp>{6NrPfK z*$p7XInEoS=5hq3J?u8WL>kNZqUku~Q#*-f=gyvux~&3M@t7Cp3XEOK$5domkk};Q zJLu@^`&kr93#L(R=>q~F=G)B+t#IwAOcP}7;bXkG^P$1o2uDt=`a%v!!r~qr={ec6QY}W2f$t@IQe>6MRPR+^)z$%zon4lRNzsL-AVL6>hj%J z1nLYicOqA(0d)yHXGuUTK?jGZH}MG+U;tG(Fy)+wN)DERIX*dcS{}>fO^SD&S}&}X z0JXbSCve}$xX*?&j`0h$MD%7 zq1enUHSFSb0L&cvz@S5|O$$Oi5O~+xKw{4`7gYobz+kUn0vYI_ELl!Rg88moF#x>+ zzyYvyMX8^)00>sc33VzJ6aewHG}G-JVEWvQ^WxnkmSjO1CLdZ zoCTz9^-t_*n3PDT;n@vCFQw$(hZlh~o6J$pKvlLg?RwVJ{2(|O_E&E8xB6G&Da`A` zhl}rf{v#0l14ZJ zK;$sPwcehXn&tn4T-;Xs&(ZI*3beirHTJKLp;Fon!WTz~eK@Rscg)HO$UYCIoyCB2 zcaB8e0~$7ugs~4H&`>xQf;wgO_3)JOY7G2<02;TRZ8er{>fdBd^?#Q&(dmD;oft#ShEpF}^7T9h)5ZW>I#m1 za0ytV0`>w06j3uqk#geo1+qQMHp7?bz}m*f@GHYOFQ=Uws0;-S8g^69=I+J`$(8y1 zwVvMidp#Y{=l$DfBGX2^DI#)lJ1qCXoqRiIFW$fMTj7d*RP=AP+KPe(2MmhQ20K>q z-I$NB9WB2H8t8Hdm)iD2Qzo`LWa>bRd25?s4d}}%BRUROm;_t?fB;nV9`|!f-C5N@ z(ZvJY&9k{Zb0?^s*~gVn!#+**Qd|QaAA(iuUG-^}yB@=5rrMHPZK_h7DUR(jb+6(Q zdbvu%aen>rv{C38(p zI2-(v`SkTKrW04?ad0@=XT)3I2P|D&K|jT_8)3c;I9M*jB69=t-@}> z+QR|L9#XhbQKTya;8L}@%1c|<5AduG{+REAg6sHinI_5-l+KBD6Ybnq(SOuU0P?87 z{P~F@ECR&XvfM$=sCkh!PhpX7FPKzU(V4cV7K7vrCP7Ie#d!5{+#;2CmW030eXe;c|5 zqlW0ONBO-|>5gE;be6@}O1}K;#QbsTv}z4FT39*9A9P-&BWamlIsQEe_U`Kn$75J{ zG;<%@vF|Ud0)JD)({-X(3cdBw2X@7VTn9>Y7lUM$WNs;+c{-^CyN6^KFL>>30ApQz z{FzN65vIvV(fMMVo-Z+`?S#1JF=@8x^GEEc02P;vAgg3w$U7cr1qG|J8jYV$*EL;! z3(mJI(LXG(56|?vz|U(uy_xIv`5onvFPyOW(*YPt)g$G)=e?c)ehR(019~!cpQ9?P zTdg0wqX1a{*5>hZ>bH`V7g?RAPWqh*@Bugz_>KD2n=_Yj59uzlMkIhYov3OvHjFtB zego~Av0Qv$x6&;&%R_>N;LIR5a*r3&s%D;8zy@!aIz0Er!{n+F0tN9zEk zA=ndd1?|(=6Q0dM-7Fg%avFdWc7q-^=}KQ1JCnp|iaNYaJWSGe{1IpkGRt9V1JTVa zF1{<`%HU-Uy>I=8!P_}BzIm)%=iWoy*Sg_POfxl5Gvqwbr{Q_~gZENg5TMj8+2d)Z zPSa@7rAP6wa-g0PFxt(`2V5BIs9n%yFgQ@OFO72O8G4*VreCB)xcWr5+Ed%JPsy7$ zUofVCgL&1r{AHhh6pUH%+vi?P+nTr|2$!}b^{9k|vwLb4B*UoMQFq;!2x-K0J=+U1KZu8&XFtsi%$6z-V7Zuuy}Y;Tw?(A6`yIT=V`_dL7{iku6CDC{WQU9D zJC@%b){vY^9XuT83e0?MX~#~D_}&&vey`it77alCZBA$?)VGjH9oSs|pszJYi!M)>#2g4Vc|?OKMQ3Y|;-GlLf0MA6kFK9eeg*c*{VV^1 z5Pdi+#prQ&*n%Vf#-*^ScmY*zte9$v(@0h;gJEhuq>pu93M=t%-8{F1@fHL@Ehu@l zb5~zC_F0DhmGr`CVe`}%TY8VfS&<;jte5sHbd}He)`eBZIN%%3%RA%_yVdtO9vB0{ z1cf4@jxq2}-L)bf@9)x13FxbXa5-szM-`s z%r|Hh<>S0b2{khSV$fZw`A7ixdnl5;UfLD)GNMa3>$3fwbbc8#<6@rhZYY5huobuQ zYX-S~duD_fZzjK}59&|Q8TbkGf9Dv0LeXvXk})Xdj`%v6r;R3H-n}!d^6*bdYykTH z`u^9BJb77Zyw&j6n|G~GNhSfwZy`l`oz)h+q+vAw3LuMV>Sg}oj=&OUPR*0z@jbkM z9<-wrA{Pdh1siI#K5gth4o`&IyP#JyNP3Qcv^*cIUDnvDJk@C+F6LOdA@Yo0e&*%U z`+<7SZ*Q(1yUS9{A zr&prYdL>nyl$RjhS?Y}M1227P?w3`Cx4L;zOj8lGqfb|PHIeccSQQ%Iwv zu9IOY#TdKc+^2NxH}gR=uGEK+X##S^7Pb7t81g|%*2t54k}4Q_ifau2PT&6GN{7$J z&XESzqM*%)Rg=FK z&n$S&_B1ctao1)eJ^v|tyCHHh0}D$)MY3D;?z?%TIyNe6@D2-uo`~f2_Kt@tWar(peG8wf!d0s~AA9;(+>~ zsQVE4cZXrhiuepK19-F4VHc2F30R~BA=mg;O%=}5-Sh2BsErKF>s*AQikMByRWTn- zRjqMhUDxpE(XZ7Kx}cYHVXCx4x>7*(c8xJXufRZTVFB=Pe%;JBpi?3hiZ~we4gS{1 zJ*L;Ru-x^!30bOU^g`v!;mPlVt(=WyD+BPdCmP3LC16LCxYoNb?5Vd$y7B2(XY+$B zP@IS0M&maT&>Tr`yg!9lU40`@PaPLjn9QTuuInld8Z54K#1}*!@f83x6olm6_rI#c zyFr&N3T#TSxb?uA0j&ds4m`&sYsT%Pb$YWStf4Q~qW)t4oF2ZBWK!moV>##q)j(lt z^5_s>@dfer$Tfa2jP5MWf`>dB&ESm!;D4P1lEb;>Qm8dYfQ<&pAG8A=Q3!>b-p8VK z)0iwGxu6#hp^V)pxA+*wSTp3n9h*AJ0x9=T+Ic@iwLrq=0D-@`t*z~rtVjfSj|OhB zTZJkj*siC@yCp$7uFV77c3`bX7|JRG_KXNV<%BiLcxI4WEUyUyY~YPL9k*&kBcPxK zXv-0o*HxyEnz94a2eqCTfsQ$a=sxte_ooXx{F zsXb%pMv&TLnQ5XHUH+jvbvI56^HV8?@8R+N)o+*76Xi5BUNdR;?4r2fAit)i3T(6M zE-#G5sR^5A^0v(_DC5m8ayY3P?&VW1kbXDlPMNG(WqC`ikPsYTreO_}6vhDen*YK% zUbu194ib9#&aV5y2jKg0Jr37lQxU#%!WX)mOs+VJbkJyUe^*H1NE16pyt(X2`tPUPs*EKg0v=Pf;EGku|aiZ1yD0sy4~NX9p%M;;uHP9@$&!Q$hFgl-QTaB VWMSGC07Hktl{D_;-@gCs{{Yt4e{ui- diff --git a/profiling/results/tims/ecoli_score_histogram_psm.png b/profiling/results/tims/ecoli_score_histogram_psm.png index 6314d7cc8d5cedf090ea87f4e699c565f7533efb..648038cb69c207d8a5363e384f6c010446e82053 100644 GIT binary patch literal 24514 zcmcG$1z1*Xw=MhtFYqFWB1(y%q>|DN1|b3>(v5U?x1w|iNSA?xlz?m^bc?cj@%WU&r^lF+3i;x$?;6GNhp$I% z&(Lnef9UEdpaS9F!z96+&Mm1p<|=4pPDjO^z+aJ-0`^Cd2$6Jz!T6KqENZ5Wlo8^#lE| z%jhCaZBTSiWgkwW_t(B$Pd?U&!&6S zW$Yitud&)KNGI~!MW5d|jJjZe-cih~l^@y>$6<4R=3}(rz#;h3)**q=VTC(stU53y zjGo6*d~++r?-*=6rf5AzZkxRpnT( z>!tPj!sgE&>T1=Zo@`Vl+*=lpMf&rkdrFqWM;(<5%vyEYi>w7+v(G*&+}0~*ULLd{ zXE$^Y@AJ6jv_9V0!)6wEVfg(NDEjr%GI+6T?4xVZ=Wi#=DI#;crCqGetZMl~;qoPV2~OnyB7Sk>IF zEja`xC$8wm(ULOR*Y&_GCq8mqtfvJFjP6H=RE4n{)gq5}#VxFvNW6oTRpY;U1Z{qS)brfT~II^j$fWw+$Z|qN29seTTzQq@$aMSI5FQ?^@4{i*Lp7lOgMb*jI?&Xq? zx9nYyg#_CN*e_oHId1ePkEL(Ijg?m6C&|JOD@w3tR#mZ_Y69^nd9HozX(_{vG44HM zPP`C9C)>jy9nZ&VBBD>=V!4cVRnqUt`C;j1`;UbS_wI+q4Cn8oraZjfs0}dEn^IaN z)O>4^P0M#Xzt!g!ovkLjwj4#sL^jJ1drqsXt9!6K#$|6lagv!2yCc14Wui4ffY_tL z>831;Q)r4{r|(+D&tpRgl;i4%to4eeGtn=dxr(YXr*q-H1y8X7Ukly>JFeFk=6J@8 zf>=B@x0qUnpP$NL_DVz=v7O9jel@=$J>Nrb>93Rq@x3lA9W^Lo>y5J%vJ~49? zl0dX}iBi~g!A-)GJ;Wx(^9dZD@wA*quEfgjAV!3DdrWQ_OKH9{G^fmYo&g5Am=#CZ zF=@t0W-?xlU9orLL;Fge+~-meb*Hu2iSXGn>!l0wO?X_5j?1SMQ@In;$I&tw3dL95 z@K3^gEz0R*mEBVY_Jodc1l&S-Mk^mDYw+=r*#`1yGtd4EcrUvo0$2N(r#NY-a zd`Zg~GWUyZeYkMSdF|-pEIhYhDP@bh3!BVV9W!#@oX=)xKSD@7vpGp+3zER0=JN>t zgns?{G5!rbjY5-(t*)dBqT}q50|&i)(~H64AA3}CpK$2KR6j(1CZQlOv3JhQu<7Yl zY|=dTyc^NiTIl9*%9ViP&oDw3Z*s^CIUNooCQp{l23qKZZRk!UZQEPCFJb zJU-gV+Yvt=_iPHI>*h2&KOM%=^A$WJTC)*4tW4S4ohx+vptE#OuUX4SIuFCNTGxCy zZA?C_&+Pjn7!K3ukol-3O?sG32hEP4w%f;&9th)t*Bl3pImtpqPPhHS6NQ^R>xJ2a zR(|cdiQ68Lh>)CTW-`C;Zi6E>Caa8k_BucBA|LEi9HcY~m+yN`_3uG|(mRV72!Z%M z+nnNQX?iKlpVM&b_Vc|sNKt*vI^p*3y0Vh2_p@F}+`Fje*MaSgL>N0 z?S}+C&7h_emv!`%E>+P!e4$p&uNSo4iL)DOrp^vA5@B9JHu|0AzS^HsuC+ z%6OUW_DZh9ub=(CoBp>ASx$bcwzMQSrqHR6de;kwvbZg&xw)gGWyq{gn`HWwVM%Vm zM#??)yW*h5(d1RTcMX@R`Y{pXcE^4>uYk?3hC2^r&*L)mPRG)2xP2)pNtfNoO_3{>>6M z{6J?n$Tw&ENqNf8tK<2?V`E7|lXx3_Eh-N5k2K2nDW5=6%#<(u+~}#!W6@H3sj1Lq zQHv-~L-u3&ef{p}wKCiG1bX5^x?tS}dTN}D$B{Wx9^D5f+IMGWWm^;4lXBP8ZI3#% zQCIi(C9Oq_y0!m!=db)c$l|P8%Y0(Ds&Vf1H_T2e96#TrMq|zydA2huZoQ_4(@2%~ z(vp3t30`&%jtj1dTQ)W!o_VY?{FSe*KDolV=I7N0Or}2I&;}g}?UWjIYtDTVEMv-j zQIj_`yXNI34i{?Z>gt*~sS?7aE8Qr~-*hJ0&%o(ZEjRqwsQcZ@bVyv$N##W|ESR><5Uy(;U@#JEm zegH=+4L3KDPhIW8C%gQTl1j9QPPW6C)J(Zgg6Ur4b)%gEBgJl%b~RdEZ%1(Bgq|xD zcoKDA*)K#l9w}nQ_B@Gma&vgEVB=N5$A9Je_4f9h@>m&sSE}mn{>d)k$yw4_X*GiI zx1Z3THuy&2-o~%}#5i;%*ktVFT6r}G9fo<+0lg17PybkvEB zAm`+Rs}!=g(Rgi+f*Xt9eD9m<_;J+nNviU~wOV7&XQeux?z2mh(swL#St@(>6eo?3 z8A`0C-iyx;k@7u{&qM_al+6UtN4i)}R9g(0n6QMQ-VU+-MFXPq6oH-7{X#2#bpSzIS_tL46AsSDss0b;ELCc6|rYxN)1V z_0~FDfq`8^h4p<3QHg|6@&pGicXX9KX?!kr6@*vFntI;In`;VFdgD-I6O5N%kMdFPo!=!&Gti+} z96pXabiT$?8++@Ngx&Cp*O|kMw2RAcgc7KY&iK2RL&laS-(SOUb8vZYCzJhQfb6o$ zCv*dhz}bbFjvw0RI*munl@p(m?xd|w2^D(ydOkXRw>&&H)>E!Zt(B=A^v*Pjgxm3g zS8~O@du$@bYm?2ec9-@I>E&fjN4#e$M>B-wMYLN&Hm&>E@6w;IS4m~~ZA~Q>7=09< z-*#vBU;(5+r9C-rK2{Y5D3bovkBaKf6DT?^A~!6hlxx&oN2v;v;i1W3*cj^^hO^pMYs!`1C3)^)E-6U*h^6Z8!_c|G_k%;K(eCU2|J;k%N z%g_LukCf{!7HXS5AIW9{~YlFUdWEaQmNtTZt^-LA**z zy1~V;ft=daaBZV zZj4sDi2!n*9d7MIY76wk?cQnwLf@(#B2xCBR$c_^>F7^IMMWE$jPd^WdXXmlfx*k4 zfCJKoD)uR%;A>w$^~;Wp^7p6xXO$^mCn_2t8PYai=Ctkv3tvs(+Fv!IXhZ-EIgW#V zJ>Lvgh9nG{W}@7Bi9P8U@0L?fn|_;euLmvBQy%9SdcKwQU(T%1zGq1ZsM)XAr`r*2 zImS4`E`PI4f17HX@+Z+3|5O3-kKs|*i?<%1-h@>H5P{QU;L_(O`u)tICysC7lL!t! z#;S_ql3~9(9h6RAs1WSyzVr&04p^0JWFh90W%OcQlA<(sGO?eE_9mGPSzRF@ZA@g1 zVOwlB*lNz^L7DaD8KC*ZKG`NHV6>|Ir16J6L*R zZq9^SQ{zn2%QD<*ddsvN6LBZNKa~NQjR~9XCGkD;(1wPFXX%Cmf6PL*nf@NG?E>{0 zDMwR{lJ-Appc2a|?iEZtoQrOiLg!MF36*g6@36upomSV@T#Dp8pqg!?Y?f6suW>x3 zL*;RQ?%uurLpo)@NUS4L^5(_mei}MD-zB?Fy|fN3?>Zf*qC53@F$luvK2g9~VuUG# z-)J6RlG~amvNs!w_#jqUN=nfhJ{6j;w}EEqd-ljrNd}4BzbJ)6p}Y@Sj%u@6To-a& z<-6D2Wnpqf!%um6h$=&k);A>STgoEK<)v((+P@Kw_BQDg>SIEGcqzu{@~w&%R!P-^tKA&ZBiM@F#bop z4(|PPY?fpLm&-R~CI#0ApNPTkzOvLiZ>Dc+eUoaH-Yi|0ive}+gWYq<`6{8_8Yg@{ z$0r!*@<&hcDLLLX@ALbUf@P>b1x}KpI0y&`!W*`kl-oz|s3bN0MKHxG$e|{yJ^W5+ zv!H&S2Vy3{B4K~m)}3*X+U{)Tk3Xuv0rX^=q$G8KD%5XgK|Xo)_2Zq<>s4j;ho8m9 zU!31~Z)+`8;QaO-)G19~+}J`FRACV2GM{gwL=pufCBm4Rdzm$>URa(^r7QMfnQQUK zw9P;6%hqG{DrpvFN;dd4khE1$FtcQB|K$b&qQkO1-LOSdPbw{zsT~9KI3v>0iPEmh; zRtlI3P15<*uw<^rPwed@a@H}ZIaRnjcG!>m8S5Uw#)X~_^H0=}=o*+ki81Wf;s=#VJk{ju&e8g_6ClUB zzZXb;QFU8MyEt-qdY?kjA4_MatE!Y&K#u3j=R0ZsCG?*Hj|)wjDdfi%lH5$lG_bM# zye?D?c9J3cxvpM>zBw}hSuBs0m{H2vW8XHKpRnsrj}eVMcuTP;4>Rq}FU|(k>D;{`g$$HPWsPWaqGJE2h%ULF-){3A zH}0UdAK`MSQeM8eqKa}sPY({k?Tyk$K_n<;BYJsh(f+Ap+b&C@S-AZ{CRNyV;X28( z2@Ia=-)IH5=Vh!bV0@2E^@}KK*&>Z1#)Pn~zIqI(-9gs)FiqAkRI-rM3(3NTLf0R7 zR39zTtdF}i`@F2Cs(rF{X0@sSUWoYxmZ;iYC`}T^_>H>bs8zfErmo8SK)DgZDGKij;rY+ym9&l7Nxp5uIXrUpaF&o zyW5pb{opAPz5NFdWM`+}5Olw!sGYTO7uchKWDp;M0io*Ss~T2RXsGH|OU#gN_Ednp z(Ot3-B0UUbNA+@Kjia+Dj;CmvRkI(*Z~u6TGL$9n?AW5-0%)$C_l-%rq;a(hShNq0 zyKQMBm32jCmnjHZAvZ{~(aWa%Sey#$dG|y{Ufp_)EPJBB$L|tl{91eCNC3mU6#e27 zLgl71C@rDc+c;8vF>Q1%iMn`OJkDDe6-?frFyw|;oAn0!{s2S1c_$q{#IvduqJ$a7 z7*-hodG_l9ZzVrsMe^CvK{R{N`a#TiG9&rU`h=FIYNf7_MfrZZ(PHt5=PC`MVaNQ( z_otoFzOXm)70tLU7H8(OeyyIA7s&?<^^f2JloItRe=+!kwzKev-&?jX&4s!;R?wH0 zk>_cRfzjUi#-CZlo$qWDDJl%?(w~(WDwba&c{0X>JuP2)Ga=gZzAxT+f5~Jr=18%- z@FJ^b+WiDe=|sLTIiI7u?~E4T3Ns^n(xfx(Mu*0ihEWq5svwf}$Sah0T=3wJc1a2KFx3E-_TYK@_W9d%SYx^&Bd!e$%^o$L zq*>(qQ|l8Ive8U&IZ3`WHx>7h(DkSdfig^L#ql3+y(by>U@JZD1@@5lV743K17iY} z>+^L`!2j$3obQ;d0G1ATO7rpJprOg1A@VQY089pCqkA>a zDdjSx-;4-%X;(@ER~S+F2os5JO^gPRlDbXG;MreJC zhs33RReR%}=|Q`usCoA$S`|Xg-AezaBDk{kn0%dY{9r3)Dnso(o)CXA9dOe~javTe zx;P}{8|pkfV-ovl^(I3faI&UiUsP2I0cxOIo0@4WT5%V);p}=c5_i{w<#!Rio+6yF z>Hp|6m+3?9=4|QfJm`z7BeC53jHX;G_kO-#o3rh%TYk-9{5|dZ07Ep_#i?xug`4xj z@9aTDc*HDAp%_T4agOV*(J+)laeJ0= z&q`F9xmE!^k6;y8mhx4$E+|vQEYUM>vQpmjovV47DXcV9?1ASNn)g}u$x;2ea+p(( zcVl_&C{o<-`uEZXMcj$RD4Vd0ou_C0OU0nMEl3ps8bZ@3vjD;=vazwD=B(}V{Vhn0 z9M{8YPC|nUBQp;$TB19{M+X>p`m7{mEXEu+=rh#B#x;^4H4vco_8j}OpCwazoe3?; zKK?D1Jo?7JOVROyGVHGIJ=Va(!|As|*9(c<>kGINm~`#VC*1ZMp!~M({$WTE!}w$z z0Lh8<0WtdmvnfH=SY_+O-wI2^%GoKl`EYSi>qo!u73$dXZsw~q&a>yIqAcE(WfNtN zvuR@E17t^Nty)MdF$wMk2RZG`vHrrapFmXRF+6YqvKZ;gmb(Ai>K%H6=o8JozcC z!)t|4a=*U(MSrFoCahZ-8^GO%&v#0I7-)9;NdGKv{(2>cI}=hL2O6MD+d$r;_nH)} zJZc(4uCr3I{&+d8){q3@Y)2(i6_e*p(Jnjk+GzfuvZ?whkN%Z2*gsXVr>Ey?&Zsv4 z!+f35W~J8qH8l=UK|yuX1Hk#pb+c4|r{~|Osp5(@ub&Azz4)Xpo$qMMEdx7*EVc22 z7)?$~;vGVmTR)L*P1MbIv6R#b+yZ*u;bR58VTs6qfB?22*lp}LT&twkKSY5xhYk|m;jbOBw6*;F{NOmwNGK0m z-sX`2$Gvm-;ZxdL4W+;f7#9dL32Qt6*xhMAvl;i(OS&%VE@4qE&0t_!>dN>i9(ivL_jF(#eLA z6KFREecc@ps)Y2T? z+TC`5+T)!8;m$X?PkX4wOqkbISacE0gb7qr>DW%+7AbEGBfKPa1Ns=6t zO;EpKO6Ak^<&><(EeET0PnG)i5!%;mkO@guwx1rWxX)YLVZKAcCq%3>elNC3J;mGd z5y7M!1@04ug?m;*mUJ<0_`m}vjDlEuKXc~n7b(B*RA}<|xiFd!khuv6&~%0cta?&U z)hm14zL*egDz^{C7gz8KupLzi2)U+Gro*pVX!s+EBIy81 z(SG9-{`%wnhQc@4&%Kgo^@mdsEsM{tw5P&MfUYA}0=Pp&EK6`)ecsx(3t5}o&CRW_ zvb@MjGH+j8uA$iNTH)Y10I6Oy*$pu^jGt^63V|aLEd{#E=DynQ8+wkwDmrUvP6buk;OjO#luny%u zcfE1rM(3A&c~C%$#>?&e00eWK&LXTZrWY z&L)#GuJ@Pvsj5xkhT_yYes47_anatO!yx6&g<68dV}naBwiV}#r%U@KQLZQ}anWb_ z<|vPHhns#0xL)KUt-#tGP}97%XeW-H5yW-*;yg%GxPNpUpLg76^pgZ3bLM&;w!r9- zl5TC`24A^VWM(X07!Xf|p$E=H`U5YQG)nDXpHVm2?5_)@g`M9#ltq$8@)#H)=s8!J zD4!%tW~E(>NIi&b&VGg}HtT6vbrccvcm6nrp(ZzST$dwe?mWW?|Fu`yN*@6`g-azSP%eq*LDtIdP}f{{p2&g`fzIL zIe#)zG*JA`tBTh%o>g-~1TA*tUwZsG4l{9f;~NwI(en>eg%(4?fHFFXBJAxP-fKNo z0)^(n8~aP2U5*BLyr8jUgYflYa%E<`K$iwTq~cH|f|XHSWzuw@9=;IoURxU}=C zCXD#j`{#x>o;FQ?fP5CtQzCHdeR|}N4wy61XRKLW=qxJyZ*XIBQJc0o%S>^n-Oe?= zw1Lm6?QsC0U52VR8*}PAFagos$H7BnHF$y)V~aD16S_6IEM4Ql{+w0#VgBD5N0#Dj zwp`esatbf9XaqXll7{^n-rw;Vb2`ncdb(@Ejr<~xLC0Iw%HCY;F=eJ&kQA$}965t5 zPHZhca~d}SX%PQcLU!?l(prjW2ZJJyNef2t7?bPyZqwQpkuy;G2ubcE?(wb4ZND{w zmIH<$Dn3!{e-X4#Aw#HC8v|N0)VqU&y8F`nMRJ@FqLrrTF<0ch$3#2SdQHJ#HsS8s z*xK2f1JM4exh&-R6*AwHuyj?8mdv|_vGB_p<+c|^D0d7DkooDYP>@GafZGD}6@R1q zVa_GZO-)F^f~k2!D`kjU##F5D>I#iIcYX5QS(FA4aQgkE__X5bQLf?q#hV1-a%si$ zW;}XEMiN!;KDXM7BTk{rNJUFDo{jz>TeQYIsvTiWaU5GKJ(uaEQzTk-#f(Sa;e#~y zZgy;=2a3N+T;iL8RDr6vG1g!RfQS}vB66tR_7IdyXQzI82)vOHUz(mIg%rwj7fQ4u z(yWPHO`3^cGTs{ExyapI&yfAulV3FW8{j19MWKIruoS_hUKkK2lJL+xJNyCWc2ACn z6xQDIOCKMMkwU(i)wfB23v73fE|RnJKLu@+n>9)5RBfPhZ+A&zBeIi?(WhB@Zg5RN zA?K;@4MyVlWOVc}qvLgrqBoNdg;PzMll8k79>EX~jDHH;@mNOgTFEHN2rZr1HTs<( zs}SJ4hNEA{0@p%*Bh~P?@Be|qR2**H3;)GLYqbwRUGUQGKP*=8JyqVu?Izb9cq++H zFa7V#`Ts9QtpCqlUUWW1gqI~*o6LoD*RH~?ToD7!3%NM25_BUn(4_LlV>*F8~F$oC-jLqFq!)$(u*6)!+NdM%u zYQyVS$!Y6mN#F&#m;6DIgmanj!^e-2Yvf|j64=zkRNR5ZvM6|r=FCAAfgm-Z=bY*_ zNwVh@R-5xBX2w0A!tNMOUGg&-%qNa&k22ipE&FOZ>dkWMTcA5ZROqn_xtX>4C_2CX zsL-^|9*DFw$DL5aMt#@>Mezsb82>KB6%aNHS-$J-qqNy?0iSo51hF zEx5{>`-?%rU+&K~XE;Ntahu!&MqtcwO}0An3UXJkv(->d4Gq@kXBeR;u5Wt?jlLhu zk6vYXlLD-NW4+&{Eno(N_V>5YZ_@nQBB^;J|9q}34nvvPD5#X4O?Kv_vU7hPYzM%i zzIv7XBvhUdIcrgNIlfL^?+eIxnBFmFibk!glC`sJZkwf#j(L=0n)9sAs)Piot`IM- z5*<4@*l;%rh6WW3E4$l(fkK(F&H_U5H)`$4^lELmdzzwrQ?$~h1lCf!eF}+(4@fx- zh(H~!1A9ewT*RZm?Uaf?gPXiSj1P5MGp>3C+$5b!5sFE*OL+-z#RiIDHpCkfyCBsOCT`$lssF zM`7o7X++~mODr38dRO9n)KL-gBc!PQJjIEtgxmW^p0Z6?M=ox*^P>(>{dG2>`M&av z3!(hAv&UJrkHmK*MAy_bUkgknGOT)uRX=2!yIG-QjnY_yBLJLI|X(>Xxy_wl8V%V^oc9yE1bSXxdIh} zc34$ z@M7&}0tgGJF9>?g9#LKv!jF5vy z3u`RQa!sB6i!w1eIXNmrDU&>l=*i-#jeJ~*Gau%5Rm1ol%AOCBUq-5hPk(w8@EICx z#s(l_+~;d;U-af5Bqp83-B?|o>F83#T@spm81J|$3Hp+~#n8(z-|EN@i_8$7BFLbY zpOAHxlcjqhXz{^+IF#6>IQN_wSNo?*bA^NJ`~AjU&r^2E0!mEUmR3bC6I><8zxca8{!glGHgBpswk2F$IZ72T6E9#-d8uU8`sfjE$ zhFJApOT&KTL$pFf2>y?k1KEcrywq$5LUgJUFwz>;jvO+hHDX}*vVm}(S=-d1U5P$h zGsuB!-iz;3k=LjUh`rDQEQKGWrrv{@lO7zGm3!h=ESlA(HXRt}0uSb=qxH9L--l=t zd~O~(vL$-Pk<&>}MP>3z(G^ACt#$8Kn7cF?7s-C2rTJ7<3J5`q40eY^NetEo{(a$NH>Y;CQx)Ydwchw!=Tk2dx zgnp~$CwC9(0|2FPd{K^>6L}7cB3z*XG=if%qeu?Fge8ZZ<)|_OiaK!Rk_f*ec61CA zpwmoD%J>VK15*r!Cm|#?K@E?(y2TRg6{VwOF)ZGmb`ceOSC0j#DHVRB3;6i>Kx@7R z;dQ&`t?IA!(PS{-Mr`OKasL@9NC#(5Tl=C*SHuD!O9(h^(7B3$V%-SzfB6eV84IyW zp*x^0$O!DkHga9e_3^0}d9Af#crm~~cxL)XiuNWGoJX`bO@RYPf9ib-QlN|!8gLY? z@oiFvVqze2w{9Ftn0!2pOtCGY*zc7uQZ4%OVyvHfRQ<_!4XZXuz=_Z4#}!ZEWJ5Oi ziv$k~6eFP9*+{#kX)J;&c)t?)5)nuqKNCYM-SDet(yR)XJSW>wE`035|J6J&%;-K4 zC~XaKI~hA_^n2N-@eq?SE>n;O1Vu(Ro@pc{fe?gqPgLP5(1;_Qx)2On;%RYt>Zz0C z6O78#a4ZGj>R`^GTPFWKCqbguykl6jX~FN)8c}-~6k{@wLkU^8H{A_e{ups*H*$R@ z7R_J2cxc^=$IYCSz*Oc>LsKo0iY!aT00xxM47 zb8s;YeePEyWj)sCiv4?-S)wxNq^DYx2z%6AGDYpw!|>J3?54WoIxRQ%-s?;58-?I}+V6P(EF(4;F=^XB{AlS<04Lp%G9K#A(gSCC2?1zohq^Issa zYf1CNh1&NUm5r2BR&(h$vX*)>xxjbv0YsBZg-WuGN|EULnv*jPY!4u`nJf0@osCJ< zqdOs|3U<49!y_H};=wa76?L}=b13&dU>|{fin~Qgf*!spULxW?{9K(ggO($8003kx zK$(IvDS*K%jQ%&#cnxb7R}de((%TdJ*s2A#7>qpT^8l$~jZ0Syk@e+@5nh<{TFTtm zm^}BWK3G;q)YGW#1&#|+F}K?g7uPG&9pk}wEZ{$$1rZ5M(J3G49~^p~USW8PbGNZ( zya2cPNwRKlMqS$LcWZc9L9gFQVRHQd58%>B88Z-vt?znLHhaFGotxbHNnhff;+UY) z>(>yB_Ul~t`8IvTWv~J-QDb2=S2#*XKQuw&xWm!u>B~azi&z)~j8W87+eE}Hn$O49 zJ%AQ8n0vJpOma>Nj;#5@{0(|u8zix!@JP1u8;dzaJR~N^%ltzd`BjO+G6Q-~Ps8uP1TVQeM4t$@45BCHJ15FwCJxIP6 z?gS-PKUyZ--{2Jrd;%50Oqmr z9#~SBMsqQ5sBwQ1?V&Qv2MgFOJ34HvQ1TQe96&YxEnbls=wIw}()3#+;?n9Iets{w z1H`AWLu<6Ir?3J`dh0c0xn;`1ayv{3oh?#cj$}^I``}2XkaGn#-D!FQ5?^RsXg;G> z`_o)(7jGfQ-=UFPL3wg7N-dW>a&9uW&SHVi2b$Ih1apSZ!6mIHBjy2(B~9;v{~~T@ zzJNyRF>;~^S-4Q2QP+=+giY$c6L&$r(?xB`=(jLbf0`87`G^4GzU%sOj8(I2uV1we zxmtuP&1cgr1#a)xQpz?fdlo?!P;;nkb}}$TF-P^6!L|p+`%ypB^rgu)nNGSqd4>!e zF#4v(rd&dKkzX=cPJ9i>EQ+>}p4RLGRc65XxEN9QB$$&V8Lud!l$fs99_5KFcD|@z z5?H0i^Exl!APYg9fOmdhv?@iQxPCS~z_dGi#N$(a+-u$6h ziUO^K~r}`R!7z&fUM#uOtQxvYOAD7ugv7Y!5TFGRc8nXibXw zJ!z3JX~*YIuil@!g68Bm*Cp|Lo>e~(z3f_A2n0WDYZ9^&{8PO}cTsykhkf&uvp*@v z41r};c0Ey)1}BzT$>OecrJ2ME9Stl6@edd7Y3DrvAJ3J`!?9f!+KscHDu>6^9Vxi2 z6hvAHuZ9!<+Q3m)%K$sW8Lb?qPL}1V^wgyj9rsE$a9zbR-ja;?`*~ICT-QTnh|gN=gmC8Se2fuwFl)%_Y0(2x+XZW>+)7cJD6+y`f3dOS-FB5* zP;t1(1+bt*5_V{*>7}XJmx)k7)Vu@7zebG>VGRT8POJZYsJ)8xr4$kvi2MugyUf775RHZUzS?e7p6V_2 z%%PEmKml7r3$_N^!(#$M!iQVS7hSx@6~Lbw6rGzkp^kA!xrDE5e1e4Si?Zz^1G3O0 zZ^~g#gWR^$7dn94AQHtPWDjf?WltU5u!!fH@#}0aqC?R6BeL)e^vgGa#a?)FqrLMx zWtr{j&6u-c*@Mjbj&pPJvT-}Xq5Cm;6Vj1;R^c`J4oL3~!xy$4;=H@+T39-hL+x9C z+3^T3A6&RzyauHlq_y&2Hv$u6T?&X9I?b$pE%eT35!qm$IlXGDV@8^{UVg$LJs%+t znnfmH_@yYZ_xVC{UkDiWb&d8PSQ%}vV(NEB#DP7b@N8w|4@grbXB!-@GxqWwy#v>O z4xW3cfj|M?>P(GX&QAWl8Nd3P*tIOhAy33-IzlL(@4P4aBvJSwq;*8|xN7$o!n*|y zQ*NfSL{BS6{e}bCI8M7W+<8_cj6~C#lE!)w8{)8$;NeleDYfxlsY$kQY%hhSH2!Cp z&UQ8?9_4pjD8YnDL_)w?v;E_ZC+ZKytOf)v!pHz@;MUerdKs?dZj2nQxdX|D@eZ`W zldL}qP3yVUxGhk%uSo5XUanp~dElkrIfgJG(n(la#|NjOJz#p@WK<&Xq1OktEzq;k z)7(CF-aW?dx#)MRw_bjdx;h_}S3Tb~QGxs#*n;kQHry+W9hAAgy`Hfp=zLiwO&T9e zeZ=7ZhW;C_M&QL$!K8=~w+twrjd7_R-(73#gY@raKpo5qpCMB(IjU{)TDWT15e*Co*(ed`qR)fyn*ihPt z^~M`JpV__ulf)<|V_*`u#iGQO7@RM`r?EzgPtsuVQN`~zh482`<8`p;2YB_P{Q%dhXJ~&37{uz5M%-w~#=_rZU0Snre*tnmH zCQpH)+M!jWT@xI|4r#ana>{V#{`4Ou^Bq6hriCX+**4ciDStuF(bv_{nA!Kw-D^B? zKuTj9TUX6MRcpOgt+vFKdASlh8j~|33jJs@g)Tm3L(?F44wvnt9886iaoo3mks_II zV|U%qRr?=?C0SwT7|_e&_YkHiYWovmQV9}uT;F7%Z+6H=m)r+fmdoPt zK=Ex%?eT$%XUyDY5lDT+=0T0UyRb`vvB@xZ|7p=!<(3kFW_3jg7quKRrhxtNDG1tS>1JBYwRD z9voYz)<2B7IO%Xn;KH>Q*@T6VQn!5yM6E|yX96o(CD!=+^SnZih=2Qh)5f0iZlXk( zZU(VRlnLb}`iH(=Pddj6Z;nNGr=R=Va2oer1P4VhS-&5ekO2LX3li;8F|j^U_#b~u zmPU6O6(x{#n+Y|-*aYy}MjwIx7pNOR9Iu@~G=~d$JC+5iFcqILLH3l039EVf>^G2b zhh-B=G91K};Ajo4Ma$TVA0gCCZ<&vQ8&!V1+YdSb1oe=>1}q3=^+KIhPeNzFjTGsGEcB%wIdjf>#$q%kMrc~yNR>f5X7EUhzix@l%`zb zAZGgUKu~NFYPGmBoq(m*>r^IM;{jXow_$Wk(jQz=>F&I5k8;YV)iZ?whBsDNqoE$u zcNG}!;Df7KyF2ksb^u~?d(;bSRwd^dAHIcZ*JfCHfoq>5sMwe8fqN^HQXvGqVoyI9 zhwIBIAdfWyK`P!eXVuwSoRP!BD!H09INokgcve@hI1nQ?QqgAtkac8-M@GWI00#_{ z;ua7KR1sLT_d_GV34(%_bd#7U?v*#`K&kx7U)Pp zK8OPZlCChVUMKTTlg2v?aqN6L{zh`+?k=`=hs(SFiUER2*<&a6LRP-bGwR!Q#QzI| zP{}mEqm(^$WusWYX}thk!~NMBwPBwfFa=dlFn+Na73MHp`Ur49GKKdqzHw&J- z%myo*v!fviklZ*_2>~4ugr|mQIG`A#FY5I1Xm1WyP%nKqyNtpRcgOQqevfXhwv7p$ zShU*FR1r)ki!4Wrs?EQvt*@33VEU+%RZ(Oi;?N!RFlyU?cdK5>^YE19{d1#$kdQ_& zh5_mgbJB%ngeDtUQ#uvo|EPSg1N|;A@0P;zqypI@z>9xW2>9@pL5`EwmN1;t^xxA@ z!rkN;u@DekZ0BhreI7oZqedP@E0bL0J^mugu9&IZTm1a|Wjn~6hU-UXNZtfXwQ8XY zcH?KRRQZ(!gn0zzO0|LOFTDEuexsla>P$EnJhM1xmY)p)1kj*?BN>s3EEAeT8Wj}& z4UPav`OgYsDG|&X!S@@tu|NKTIyorhgC4a{&>?85lv5j$@MCT}uLIa=uyoiyUO;qF zD#_b&P^aECO}Z=TH^br!&Vz_aT2r9o%F`O(i2VzdCR>GhvZ%etx4j~VqOzJmzX`%H z%abQtO))sPqZ^h2XmSpcpZ>6VEePYvUdqIr-V75lIA~6ZFWM#N?4{~5<}@WGLGFl? zst(<{&S>@-`z2%|ByZsmd4m;Db`T@wfO+?lnD3bkH#m%RHsg zEsSzFI3+A*$pJvz(Iv{}WtlNxrU`a^KC@YjTW>G48thdAHTTW?lJG)|NN6OOK3uDN zqVHE#JpbMy3m413)m?-q{fyt0>TSrFlYG83Dqox$d_Y@GiXgiMe8fs9y0+b7wO0;c zja-PJZ58KZOCld(ebbX$r4GvY8!BSHcww?z|8Qte)G`E<_J$!=>rFpGC`*iV@>p8! zB9M8_d^2#N7X0`hB&~0t_^VDS{Jc)7CwT$=fbTGB+7timfJ5EMqkGV5QJyt}3?){H zJF86_t9ZS*r@#aQ1>OlMd>-QHt~f+MEHYF00WTPE=QJRKmI-=!I=@t*`^a1>w3)Wo z4v)}wz6S&q6jVrvc!NfvnerJlFoWrWEy(OYFe?~(wnlA4SJHkm+DEZY&6m(5h^ zj;vp_4AN%`+EW}DAsw_<8j_2&<;&bnP~WJjXEkHK4~zsL-WpL+F|=tAyqTKn>Rx24 z7FcsYvm-h5tv|LN`v(?sk^ODu{`|}Vjd!`K+A#0>0PY8sU0-)z6Rd>juA*m3zi&bP zGYe|g_Wy_vliZB`4}6${2-c=5QqkKi{TZo1ePh_K6?~MFL;S6HsQK_Nd2oeCK z0y|DfN*;6ie@;{7JDk}O|2S{_=2gAGQsDR)wi_PI-@*EXTbhAdS8~mx{+zLFl@d(v zctwWGS6t)4oZpjuUlS63GcvE7cET}vT6%|-Udgf=FtdLMi8Q0EWBBg;l*5D8F!r_CA8F~{C}%e!{ju%CUzBX2 zSQh0!lIxKSh`6QaL{xvT+Xhoi9Z-(gv;+?OW0|MAr24B9lL|OC`tm!D?N{Yz;XM%b zm(-)_`6ZCUf8CbpL??35vau3CJkVqbe;`kv>O2Wlh6aY2m85}&%g}N9{oC|Ik(WFL(8)n&`8f1EPITFq1Rt!$PZui@_59`w)2EvagdqHP;fnynnzG1>zcQO?5hHsV@t4Iq#6@!IO~BW=^j+feV1u zWXk;?6v>{p$!Q1#`>aqlub`>%Dx|hF;Y=|`DMS zJBL?NeRp#Z@bH1o#7(eb8(t^6R)ztxvW13~Adka;Mrdj%pY;W=OX5JxsjF$WZ#wjP z00X9wD#Fxp)(2VR$rHo1T$V8HDZH=~0hV4Wq%o!SDj~t{esyADLL)CP?;9>DB1>Nh znpnSoRXl`erD+-)lD>b$5r&1k^|%O>#7DZu`Njm{i!r9S_mmf#t6alNEW_zbDl&xx zXKbvJ6S8)uY`PLFoLgs3p%*7DmR{%;kfEKr;GrmU+n|7!Zkz=I$AgMLN`(H+WmJSY z9l7Ni)_icv;mAt!ixW83EvBE(1IOTvBoL|1$#o!A0fs`e{!5EUvw1BGD#bqw%PT}v zD($nH0&1+cAiP;-?n&NEetlsYy5i?oiermdp;7~@XcIoSGb*_f%fgur-8{pn3#E(p zdUNk4D<}0(TCW9>ek@SSZGN^`t!kDGe@*$nEDL)DU{@*UO@Z53*w8{&YeQ}4+6z9$ zUhp=pPgXU9A^yi~_rlYLu}Cm)Jmq^5Hc?O}za-*LK7oz5*z=KO8B@t%w7I_Z&5xA6 zojsu&GfI!@cU8yg&ta2xaEKbA0J6M(`Nem zgzDRtWfJ(HNg*PCiLN)y_a`Uk4JYm{DyzN;D%8Jr2WI(g!G!87};1gQ*Mm*l;Z^Wx5$GN zI{cg^YAQ5LfRTigLq*g)0fH_>h`(Peg+$ix7PNY6k?#ptwZl^U{d4}CUZ@@> z9fI&bj4hxX&Qm}=j=wy`s_Vr#2Qoth1u`u7`nMNXA$`nqd<{23-?6=*iXcL)^pd&^ zSHx(F;I$%<1Pk_G+aRtlC+ooj0gN262KzxhVMS}%G1OjEjHU+o$3^>VE( zW;q(Fh|y2W>b1Bvs^Fs^nlan_iDPK9Q>_(h)#$3lbK@ut9#K19Kv#0 zuC&lQE?O8`b%2Or&^Z%mHc&(fk)7S`+iiBEt_Co^{%JW8RWG0ve(PcU0m<)`AR3?H z4n-BU5q^nt$^W=R@ut{s-kpZ)(EyYxw5#1Uyx{z1PWU6CsKj#gP0|!4LdK)G|3<*N ze6!}O0)KO08_^ua*W|ju*=DIvyDa)<^HnR zWVu-K_AZhZ#Q!D|WeINd8({tm4jam@HRuiuM_W;4jhqPwu~ zZM3S?(*NB4`iIAV5jjDHl_Fw(up_>kZEJ}{ ztHFrRxc5XTWsx_E9ZJO@bv=wtz8UYMQ^{UhEO+O1#NsS`W{!{0W>k~Oz`*!GG)`DE z`%kf~>jhu~)YKTnMI(ahNSt&MX-1)wTQ=b~m89OhnFoYFn*7-bepP|joD$GP(+h9Z zP?$ve4$i-;!A4i;9j8HRB!WY$f4WyD(7}=*VV|HPdIfr~QXkuR1stm1xfI^dQvFg^ zbWRmHPBB)94F8vN>D5%N2Ly=kCqJ~5@u`W}|ElE7)gsjIBQI<)vjAiiNPo2)Yp7Z&<^XHfu z&&=~Y_wWAQ*L8hmxMFyqP5@HDyewQ_{2*e@iOq&}CtSas6Zv51R27j*7Nhr;f;`6? z5-US!*`cy4mv|RCBsA&t^H}s+ASN4k2l{An=^x%6sE6Y3!UCrp*pHIkPaQiYPD9-I zId|OG$o|PvqOq!opcliFDQa1}0taaLDD&f%ewG1aJ|ll5LBG&GccVi~JJej z7Q+fc!iub&)fp{(+oi0+u*QxYR0&f!yq1s?~L}(`z9m`_$Rg- zW%N6*uv%JeT00_^b362vq4PPVEyI85lCJ^v?6g`<$zHD8=N%(z`6ps{mMu!U9zL{^ zUoj|VGgvNweXJY9#C9}+TK)u08E>}BqC)%*767G<4i7Kq4#pGugk3%#3fT!4Yi&GU zT34J!EDFM}&ldi2!{GOg)ScX7j@viZ@w2Z)L?{YaLi_=CaVU2Ud6g@@x7ZNU$ zX=nVQxjh7OuIBc~#RO&r4eLKSxw<-E^L{dHZ4>AqM$X_ugi?trWm$l7L=*1{8lw1-ZuN1$&vb!+Z+ zuLs|Hz>yPqNd&mQ4V&O73&DPf__hHB)=PB}VNcBcFkqS9Msjk=gBuK_r8*qjVeqW_ zxrvzl58)3=s2Hf$H2D--{hrn>jDmK;`W zs;?wSO+~2s9>C!ajmd_>Y6e4qt`WPDsj^@L*fB!yAv_eZU|V=TGvN?l2mmnegEWia z64%FkDQ+u-;C0m;@Z_D-0ncjCuc4r`%g%=d}_ZU1yr^o$8jp{gCI)J8TLL(oFlkqH=4cCUi|r?@p}Vt6~v|JmGSdYxHzcT9Fyu_jR8P zYnd`j*<|eWa`;;W)fY*1pV_8_<~`_E5nO@E>3|5P;HwFzr&lM6f!8NERkaveWPDYY zlB7B?q_|?^-zzxL`xNk!0=wBa@46_@s5dkh8I9K2!1ZONOHZAGpA)!tv+Jw$Tlq2G z*{lkwg_OXR%x$Z4@(AFuS+WL4%Ok3u|43)`|FRdY(ZRhJhW(LLP^or8*v9ws;5!xL zIskp3cKnN3Td>-duLJg;2%8b&K4rS0)I9oqPQza&{mw^{I}# z4C26AS>lAVZ3z2XULb??sQ>*MT1GH_@X>oZGVRuDg+I+42N%6HBuZ|@%EU6Qj;mZIT0f4J^t7E1!*X^#Uk3Nni(o9o#+`*nlbwxFr6Nei#g_F7944&9s81nVXyY zr957^TcubO&gsajrHJy;xPLZ($O-+u(l;IBR>=)Duf?SLIcy0o6Z0)6Pk_PzqS}W} z0>zJ4=5YO8fdd5{IZF+PjJ~SRb^F5=(O&50d#0-S{Ur9DjXj<_D0$@#e2VXXd$#g%mI|jlbO8xt8oyeD&XP3$RSz}lk zny33!!&Okjqeg#xC2DYNYS(Ui8D}|5QjEu1*Gz6wshhMdTA{|pYkXS6It27(*?nD!v!7qA&ugqtT!zo-a>Mn;ouS&qL1x_hI!;!rpXEEJJ zzgef-h9L!K<>s{B=ZyN9cT$kz7q5V8vUzQrfe{-er^_Fz2l{8-gkYxD&9}Qg9(jng z;<5P%+zK9lZi0Wbz(2aq|FEC`AAdIb08rB|^CNoVmPk8Vfq3gywy%c$5#@fukhP?j znhP&4WJ{03_0#^q+XlHTZ52XdRUF=TwDJ>ZA1h}#t*R`Az%evDav~9dPJr%=Qxwx# z-gdK6zQH^tgGOBQSU6}xsvFBR9u9 zYzKa8u5I;gpm&k73)IwJi1mOE<=&mNEYrh^Koqbbbyvoz`5s*HnK_(x=CiPlfAaQz z-?#LZd#LFiO8Riuz!$ksHI2T3o-^w~SuJZ3kGA(;J^#@eM*mlQUc<^YJZg6z(m_tj zf=o-7zRHE-wGtA|ZXhz0= z%j@}B5nI>D@bDV4BNn0#jAEBOJ1(5EAgGF{cn;?mb+R@Z&+tkxj6*Sely9|{Cbxes zt(Qxl-TI}R^=jv>iIU5MVG`*UsMcEHO~ zK|243LU1T>C&}{5I~4US{yWOlih-^8CBb1`-JIOjVpb<|81A9YJi7!lO}g!LnL&dp zuD}NTCJ+mPFy}CUeX|O*o?IJ&9|0g)5;cT~Foqfum7b{IFE*IzG(!huvJsfyAbAqU zaez1i)(~|Ff_rfS^GZmJf&kB^U$2x!8k9kcA=P@}*C9VXM?<17NL8_jrZ?awHY% zAEYiDaxE~k#+7^G*_>Ypxh2PaOTV5I-mX#CH8#hTq;PFVOr&iEcLz8 znqZI##_h4c&8~nm)Cs*ys7=RXA6&kPhR(PVz($mSQJ)ImOLfofv9YV8JGlI14r~$= zozMjw(o8h5iHcqQ3E0X#wqk>~*}njE@#zBQwDs@nM|BjPN4$Wh#a>i|dCC^ph5!gY z_YLw;JZG4G2H&&+5DDwI31mZwA2rxkJJj9JGk0Df$z`9;eTYfu|NL0h^+MD1Z3bi2 z0|l6;-vDwnKQVZuEL6XC@<%cH9;W}{y@bpne`sRB1x@T|v1AXdc AcK`qY literal 17023 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 ZF>$RcwEZ74h``Vxn3&x2yr(bT{~sn4wYC5N diff --git a/profiling/results/tims/ecoli_scores_over_time.png b/profiling/results/tims/ecoli_scores_over_time.png new file mode 100644 index 0000000000000000000000000000000000000000..fc711cacf89920252499cf3939268458d979bce9 GIT binary patch literal 278837 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�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 literal 0 HcmV?d00001 diff --git a/profiling/results/tims/hela_log_score_histogram_peptide.png b/profiling/results/tims/hela_log_score_histogram_peptide.png index fe2d4664997f2bf4a77c9b7cdf495e3658146421..c88c5bf87f088a42611ec214b782cfb2ef1f0dce 100644 GIT binary patch literal 22777 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 GFaHmCn6kzI diff --git a/profiling/results/tims/hela_metrics.toml b/profiling/results/tims/hela_metrics.toml index 55c93b1..a2233a9 100644 --- a/profiling/results/tims/hela_metrics.toml +++ b/profiling/results/tims/hela_metrics.toml @@ -1,5 +1,5 @@ -NumPeptides = 2922 -AvgTargetScore = 23.35560315415351 -TargetQ95Score = 33.983086669309074 -AvgDecoyScore = 20.88152948080362 -DecoyQ95Score = 26.329978361311163 +NumPeptides_q_0.01 = 2442 +AvgTargetScore = 22.473263643744477 +TargetQ95Score = 32.60714890338063 +AvgDecoyScore = 20.481250731007073 +DecoyQ95Score = 25.627900520272156 diff --git a/profiling/results/tims/hela_peptide_qval.png b/profiling/results/tims/hela_peptide_qval.png index 42c910d0215c5f158d0ae5f5bedee07e96c661ce..1f4792635dba9b1e8381e2f91cc933bca6f99f17 100644 GIT binary patch literal 19942 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�)otjA(3`QG`28S|5W_3) zy=}$9C?QmcbFfL?RN|#2W3C9sv=`Gr6jm^x9k-7idt`^Mo)Dvh^rAQKU4i}`8-Nk* zPkJ??=XS;r{0Q_*-sa+?ELyJa9@yvX3maEJ|>=Kk+Hb4I#d)Qs=YK>{X2pddViYcPXP@z#)1jcMosjJ9D;##V|`01H2{HbZ}^CE|3 zXlv`%=4KopwSI3>yq`VXl!g^CsQ9S9hOJJw(DvI946fS{W?_?)_ym`3+!}t4IAh*r zkCs$AD6Z!YF+)EdD>jpKSr}PH^*7c`&V(ra3hEH8bXi$3zN?ffTwk$+X#%v8xb86C zsK*XL`~aL=zn3y(_&vRIF!Ls&h`j^_vZJ)NpK{=?E~(?!F=~K3psb}`2fnrE zfA8zlrOJB;$D*9O9L@=O-gJO}HkuLH-!4#il^F1SZf$y*m!4$4^YwM_38o#GUGx)i zlT%PwGVL$6b#22Qj)667a2SdEML)+T#S3b_W#QPP4hQZ+GLv3%Iafn)0G0Uj|cfY$z_e(;LG!F0{6~-myRxOVl z_UJd?1I*Kb?X2MHC+&*RiPDrw_W@pO$_g_@q)G7vpn})GBYu3kU^yzKpG`vnKwde| z(8-PVKAEf=fiyI?&CXt{W^oBIx$|*DrY0z3 zKS(=TzUN7I49apYJWKNg4_}s89-Mj$4%wU8>*?j5WazYz-Z#D6T>HX{Ze;sEXLXRiQZe_1kt^3Zbt&rY_+3x z`0weZ^nr8wk{~_;{Y6bDd4;iH_vefZu0}4s! zm>Yf@Q#1YQ^bXMJp26sBszo&fbK48my%Q8u_7SAH*zy-Ij<`HQXAu1;c5ri}+b?!k z`UEi2T}inMg~v5LtuI#2&iLu=qs_Z9t7 zS>L_EX0w?Hs*MJzSl$B~2*H4&Z+`Ynm7&(I__0i_S^cBxOJ7@b&_mV|T6c#B6J0>n zyZqt%wLo7qV|~tv{-L zcT>UK=|NifiH&V=Hu2@#K*-`pSqwh8$;lx(r{k)lg-xz-6e!hMaU#h0idb)>iRxUV z)xLkhya47Wl~_!>;Xny^s@(5Vw7orWQCG>A*f9H$S7w1iGcxGrsSuG zUZf#mDThsQi2p|NQXP|t-wSl=MVh)Sw@RXPw4rGjx|mhzD-V=4y4A_jY`z|RdplVK zyB(`zq~n&+WKM_-H1}L)B~HyV1gurDb6hJ#bMSq0I(&L7fP$~_`PFaRld>6K?+#`X zwRr3Hc{@Zw$v-FX;=)NY3C#D4bYtW-c4>9s<9I2?Bd1a`@#G~igaF(vWvM%+^u^xk zoDmZ>uzL_}XIWWY2IdsZYUb}0DaNUoP(kIrM_HHgS|;PKZN!aEKw?Tx4@QGz#*To!44k)qy%aZDSN??C+B1I7 zo9P_=NTiiNQ3VrSvO3`2!=&W$^_K1;D9*4L@|sP%eKsI~shM87mHwX258?kzAXKcS zKf;xL^j%_sMPOMzYTV!<+86IEB+;A`gLhesIC&oSgqm`nO-@>!2*c2aK5dxSvwCXs zxJ%DXzbwghyg>U{xfb5(99KSTSzN@a6a5Rm(MICeKmI-m@~bR>%164Iq0si0lc9rf zfL)x?BDFJ%=Yg(Q7&;W-u<{249S$l~D;;WZeHYbhYBW`2#5xh2xX*V4DM3&t4b7h{c9>nd(V(mK-L94# zBPO680?zVb32QL^{ZF+cW|&J-<*F{Aj35H;|IfBfr9g=YOmHwx;D@vRYm7BOI#wko zA?OBp2C6UVO^v0W%@b&$U)mNS0ObEu=VQ(owQ|rt!0Db0HjS}oKbH>Ih-Go^iFpo) z%EJ5cY#5BZHoj3P3w@Ra z8^Mk!r&kE38X%zC$iuG zCQaknwdl3}FMyu>;JPapp9_F#($ahvMiC4?fX{b-`u@lVM1e|CfDoU?^f0s1{juNM zuf-H+J>Gk+zjK63?|`eRVTSZOSMZBw)jpv`zMvu$y*hXgKrhu|%F=*WJ#J;KHZ#f%1X?)gm2)N6&E|-6*^-fJeXNr^`+O zfcZ~MrB;80c>y~g)f^bW<_!aR*%{|jI1zGa&mImy0I&}Xf1HNsM_lrx8YxBlpe`+& zBzgrVJ>ie08Y+ub%Ef9az7Cu>jS!QmJRv6Y3Hm1@+)IFEZl}A;Jt+XpGU&#}T`0(Q z^d9{&`lbDjGx*lPp@hMjS8k~rVcZ|K5cPew_|lL zJw3O*MU<=;<=`&MyIdTSBCWZ73scXl9%IZ{Sbp+rMkbXQJ-GduNLX508>;lk8Lf6M zeW?ZejAI3wCa{Y=sG>0MP#s>6&0|JN%|n}BfikdOkui|3OhOOe{azY2F@02MFf_=o z*%NS2msC>rfVbZFfl6jG0{8<*}wY7I2ybes6ZMlPs zO_0x?KTAkS4DK8~jU&h`Rx6OMpXGtgCy=O*L&^$G`x$;nW9d8OhhH1fhetKG;by`4 zi~_A1X@P@RzjNUlkl6EdA2tbDPsYo0u;_al_a&tve~eyRqul?=58DPZZww)_KZDGk z7nA1DKcg z@FFZK5zOGohmCo!H((xg*^g!X1PqEOywCc zz7@)3hAs{$k}6&<;TkJQZW9Sj3JHm>L*CTEn^Fd z6^~*nd|7;J!F{W^9OTb=3MJ>od&3i6GMMCSL6k}nf(8=){Afz&^j9W^N`J*#3<@KLFr6KmcGxSl3 z%;%7K5LQ2_Fx|F5jz#y++Fw~1IbYgOHCNSQ^OxU{uK0;CJXabUny0NQH4mue2Dgp( z=ZD1h=gc(+EtwQ7opCXlO(xG@1k?r1{5d<%wy>ByIXoneE&_!a{9-+-XxqBLfZsB3xJiQJ4*;yMu;A37tbx1bW>}sxrUNIm=y9=!zZ!|F5APt%vw;}CNbeP}Gqe0Q;_$yK7xlGi<%4p?$vT%n`@&T~`w`0^f*o!{#k*46-v@iU#fV>Lo>yJnS7Mk%u$@pSI@dCq0Yg6Ub=Qt_2 zdPk>d7YJ+OM&L^!URoQ;cg8l!e0WU+a@1Dk#1;V*v(ZN-O6F%fAt2VMK*jlOzHV)* zeGrs&?{7B()XjfE^7zDPBl(06r6;523kozqo!RmQC?G4I7c%j1FVV&J#zH78 zjd~sbNjpZH3(XUAqS&}6;TAPlg2n+1g7nKEIDn!4E@&LM8w?7Ogaya04oozB0=4jw z@r29UVy#lNCHpbPMA?b|J4<$6joRY41O_Z`rb^=rxrtx5~ic zM^5{dXe=tN`q6KD!x`Yy2c}_D#phs37QSd;g$_Z1BRp^C9iJr}7i?BzYo9`mip;YyENlI!s~wcE*$hBs8=Kh9gFm4ZHxRQ#OL{Sp)W z>VVp-*au5*irg1~SzP!+b3n*3;N#QN^k3z*v7X%C9{Go~=H@bbQy&;pvA}08Ph`+t zyn@8XW=RKpvjN_`O5btfwgJGD%Eo$&?zH#I<}_XoyXQ6slnevszFF;@M(k>l)1vd} z=WL({02eo?lYp{X{Kvm0vhY-X{(v2)qyz!rW6sOZOJ2VKp0f}$D)>my<$p;9n9AFY zjyMi`n4}$_@ZlY=W<=ES?FIWPGZxOXAG?cJ-i^Hd(bT6|v-&eRd+)QXqnrphJ_<2R z^JFU+9?-yjJ*&pdm#4TrBUBq;2!eqz_}SPzk1;Hi<@D*|FXVuw>HWxYSAAyY(F&zp zqe&6-7?9HVI)eYtEOsThZ^y?v(;HV*$_Rp$kx60*LmV#XdGW%bmK4i+FviM7w4F=06*w3y&SgR^C|$@hSbg6hwN z^jiH^d4B=JB;mSf{q7nJ5M)Kl0kA&ziLnVvy4Gg|x`wNtT0$ND!($Y54P}IqUAb<0 zn|eKLm{HXEj|uX7#II=vIqw7`?>3b5;SYqo5ZGv+=cjzF+ik zB@_X>Vp5|WWOP167Xt*hmq(5jX}x-&5Y*>dS7>=Ymhv|tlG+-<_{xwjzy~9&t5jH5 zR;55J*`IRM_FE7nmwQy$Ip1~}V}LC+$+ab!sm|T|@K-uKYr9|`S#^cY{8pCWpwCb{NXs?+A5^?Vr> zIntzQ+m;!|2JOG|b(}5w^rJ{sqXXs}5?7-I!mjZ-nop-m4wL$#I^*VZ)t{hH{@X82 zo~VH^C=0@%UFo!oH#YM4K#SliwXl=bs!PMFggM1v^j7I`A6&psUKJyni!`jDII>A4 z30bMXE>m0Q{_SS8|9^G?fvW}>wngDeAFCU~J5S>*O6r)zDkh@{bA_7iG$)F&cU=R> zhCsg&$FwKCL!Mu6upZFlIy-XVKD7(%IwPkLhAMi1BDVl=^O}%ntODj{r5(<-?Q?Tb zWAI!Rv`0#k5ZzlkFX?7#Et)DNR`Jgm$^!i7`%Sa09%t=9xAKWhDK+!zbv*!MJ#TWZ zDdGmhxL37Xxr45T-xYHBhg#ehGxVwVBQ!|=FKk3H9L#b(exuPe<-b!9YK-QqZ!`rh1|9^LzXtBRc&-1L zeKfo9&}8cTopErp+~1PD(dFBaSz^Sa84+Ht2-tmpVBc`4A_{|smHNs55|m?@L97SaAuqJlMUAXz(Np8?aX~4q9 z7RV`l{gQ>HEMM~WBXC1POdzABXIlTV%6Gs{kUKkihT7X(tf)~q|6wI}Ey35)zsu5j z_<|L+H-_3CM9K`sboNccc?$mDm;jhJdgK105Vx$SQSyY}D-GNmY?l%#GXS$v*rma! z9}@C31GGF+i}%hT&u2D~Qe_WkBgR)2#ht%C*L7O4Wa^xHLUlR^%q|{tC_A{;(Dnvj z^`IA*y3ub)?Qe`IVlL?*Ok~%)HD(_i15iNk&E7frQv`Q8k2Bwk+|J6dx%hGpe~v{P z51BEi`nXPd`U6nio!<}|3ZqZ-W&FznKSnk4{!3l8|9v2{-}I#s5JeT5Wz9R-~ax~{%MQwf3{1}FwMpXruoh*`I<$jH?fNI z|CaQle+$r&LJ5%xv0wLsWCFuMiC_#hy)^6_stj=#L&g2Eo&;#=BG`Nyr04WT-b%C@x6s@sNR} z%kAXa)hS1-fd%I0_#YoP|Key{#A=)GEF{3Ho{V^F4vz9*T{gFj%?<`|9R2lad__*v z;m#BMYF_i=1BFx1g&qJ110|~DQI79<5B{4m&*)JP*m=T)pVp>p?fERMJ_Se|1BT){ z?}io=_yE?_ivJ7vbO-gTm0df~8_+(E0_O)6Q9+Ejk9h!n6xcs&t8kcy23y3H+d&x5 zj#(WAZfyMysp`tnTmVHi|AtM6%gP_itSMf3*R;0$&P@)3Q31rM6LGhjMn2_?*P%`~ zz5GRh_=-(J`A1^m1YEi6t5Yp|T4--cmlc^%@b+cZAw6lHj1-)tl8>1YQPAwA0y-Xs z)BDgn3pf5Iih%UQC-~pTzrGVCfD$+3gzy^8)DCJ;LN!>P)8^C66sMShzNE+FGP`C3 zji%VK!1~gLSusiHKT#9ocUhlNDD&1;Tib;3_QkP__d)w0mH|+)`765K;zxr2CV0Tw zz{~hcG*K3k0+2e2pfoZOMF%<{od*lEt%44e>d5)X@nA6?ncMsO^wcg4{7#oU!fE<; zFmcUgY3$<9Y14`Vbm1RNig{&l1%X#BMKq0dONj+MGlCbuMSBNDp>?8K*#8!?_T?y~ z)rZ>V759^?T?-`Re}F3Wc<5p7ySJ! zQAM;i&ZE9~^o%T(*!^I&^j@EFbg02KT7trh^2>6+`ml(2;8M(F#tI3RHO=(~8I!Iv zI)f^epb}acrMUg6rVAvY!mIgzg#B}is_Qx{m4vF?+h*R#aS+C!93A0%?>_3c9POC( zbn4i0Z-ku#r=-834c!~3vZ*r)qkscb;Tq5b1B-;H3X)@LY*vrbT#EF*_qP{mWKT%Xuab(aAdFKG6IS z|JMwX5ANp)Kn7-Yb|qkqgSBdzZPpGf76#sWyd6+CAeJr(nvJAoF_`J2*WuP)$RrW# zk`$hxoWRRUNYMY^!x08%sL{PM;No~IAFs)kn;uY2agrcVQSE&JQ)QYHVnBAvQcZct zn8hdQK4_|?LNd`=Wis+Wn@ElhOub-LAL`YN?t-PzGPG*-oBP%9w4 zg1I~6b=G(BVC{@7v8FYtmy&l(ybX>;vxlNV1)$TG&sf7hV;i7ypLudDa=KaT?4MYJ zY&nK!+<^aZDdJ*^3=>*d%z2&1M~3kkcrOU67Jh3PZO;%hw;m2 zaXFEo?@}n+Rd?KPo(x#G4cNk2U_Ep8%4Z8P%OCI;L3XLsmPPqk-a@m^?V!-@Rl?RA zCxa!I9qs8Z$E%o^Pj4ipB)See{lqM6_6bLUwFV>}Hb}R>zVp~Is-6%xv_=|^96IGP z&f0d;y}?z)aBRX@LRDLe^UI;+aq2n?*JdKh)RPmsqTarcn!TBd$ZqJ{=f_UEo#Iev^Mj9A*zYf` zM-^D5Kcp_?P+Gs*w8d$XXFM?<2wGC3aBFFgsRuW0BFfo=>Qoe{G)0Xlgr9r)Qf*eI z@-7k_EVGfaOHyB*`#@cJVXoWUyXlnye*$6O3hYB&JOR83cEzBwLlSl+^@4*ZYC!TQ zb#;PUj%z-$2d%fBHNXPT#DE^{i2`PPojPk3m2BpTt3TiG{5T6Y*Vm8JSE^0kLs`SGo&u+AI$TWNmQ6zBH1yRDEqaF@Zrl)OhN8EW=3m8fidL%@3_CS-4mh2m0=HlF~`UE!|%z@E>e3v!(MSNYlX60c^~Q zO3w>bJ4r#wd2O47^0Xa$LWJ!6JMK`xCwz_8-MIQ($i~sql$hzAy;V3X@arIdW`0fK zQ&WEd+Um~%4OD4A#0ki94>hCzD$-K?`q>p19^f?R>d9_xp9fh>*f}k3H?q#+}J)E zd==i5f3Im>-h{gS3xm~#`haXHkEHE!^ zw8+lBIIkEcl%LZJ@5_cQ;dnxl_^RA-cTXw>X|YDigwiJvWZ<nnp91uGjERg1!KzAJ+_Pz`xh@PkU%~vhK`JNtt^2={>p%x7wa0*}g()30sIXPJOt&rxi7V17p45+<0-d}`&V8NL zg0k0tCy+qp(b&{bU@ zYwR7I%8Do|+#UxdN?EHP>M*P8M%FzkMPSv7fbGMFnVBj^*Jk~Dx3^*zDOare>CU(% zHhwn!Su-d1m&$k%qID zhhTH=53zu{qkK%3^Jd%R=4h2O!s>&mE3RyrQwUYpxt-3_(LFfO{g^7_M>vmJ=||&` z+NkXKk1p=@y4tdgH~Vsn4)$cMWSF5o)&D-%DlCRr%2pbJ$1r!`#N7Y;w_T6rM&eQY z@_xZIVK{u!FtIuq^b-HTPt?lckpGFIpLgLP}!b~{cgd@s68nK)8loVeYLjd+S2_aqX52OPo8-4 ziW#hJM8X5LwJSI%QQtpv`-+v{I9P{gIzuC4)3nkDR1Fx+b=+P?m0cHH-wxikWa2pV z|4lzVM6VTvf|W>4yx)I1@cEV!bAad+ z26B-AmRq34XN|P3+3OkU#BhBO4XA7EiR!V z#c1XqxTcxO0y)y((&1M0cpfnUOeIj1* SZ3MVxNK;MccB!h(v;PHiU<1|w literal 20114 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 diff --git a/profiling/results/tims/hela_runtime.toml b/profiling/results/tims/hela_runtime.toml new file mode 100644 index 0000000..4607ce7 --- /dev/null +++ b/profiling/results/tims/hela_runtime.toml @@ -0,0 +1 @@ +runtime = 1483.474233865738 diff --git a/profiling/results/tims/hela_score_histogram_peptide.png b/profiling/results/tims/hela_score_histogram_peptide.png index 85fbfc73307d89002d2ed9aea2ca8ab16448875e..d8c5f4d6a876e3bbf10f7fc6f211135d960def34 100644 GIT binary patch literal 21475 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ⅇ z=IY#_<+RA@OY1tb14q%yGY~)@Hyce{YtOLfVHn7}#?39brvg_pIT)iPhL$HW_X5vj z&{nHziJfQ*5g;ZzGIWF+cRZH*DSJxhSJ37lZiMg>G@iQpX-gS-8B*cgl?chMPovlX2P2kGPu zl&s603)j*$%c5P~|Adyhpr3_vTgyZv^2oO*Z=N==gtD8z|F2KwbMgCcB-viG7So+M znWWpYliM|NsJu-t(RKscQS=JZ$>c)b85@sI&BYHFfNBjkzR>Z?oj+HEwe!eJvWp+$ zS%ihPxd*pQ5uY3O&yXu#-y{RZA{6LT^~{b`2zX`{W*V3!r6Z?8MTd^BBURa_&mpW6 z;}N=C^j%%0nIerd<)Wg1W}i}9B^Voz&5pequRKTT9Y0>U6?hDVI~;yTebU&ehEl}H zDHLqX8A8Kk&;yX{I;!L6+FDTa;m4)od~+E zkwF)gRJcn-c9CwKv(A91`km(SPj4XH?lDS*?D(!t9h%i z`)A!dhuMbz5iRP*_nkqY#>r{^qjowSbHiXi45m;1U*ZR>Vxv6OGLkDWZbLtzdIP-m zcioJ|2lxBvf4|p)EUZKL_(89EcwbfZ2&xkKya(>6?6>>}Q6{`QMIY+w+yH|ZO+bm| zSuC`jR0!avHSqr%+=w1N=2IQ@=U8-~isZK{Pi;}Z?te|?T9orzbDp=4bUna_T-pC0 zK-r5%@~S(pQrg#6E~8Z$H`!yXc)nEnEG?+IRrCsBd1kKahb@yJlO&l@QMs$bv}2$5 zv8bOpm0@ZyNjO5sbJvLuKJ0yexX|G4v#+MWePQcPfIAzTZlz(k9k}g zlGvIg%aQ8DXZ5XAj-gkuIr}czlPq6|5hZMwnOt#)N${J@-29@nL5HH>k?9Hv_^7?gH+wRq3GP&_SJx@^`Cv2zAR-#UolP#5F_A1U%FuTNl(?xw6UXZ= zX(_hAqi*i5+r3RUOq&d4er~3|)`F|tWZV^Pv|2mGfGP#*PE5#r(~ey9$l4%E4UK@M zgPVAw=F9Br>2)2{x5$V>5{~_3_pDlc7>@i8&Zz1}-j-UGS7i;&q@k*%(0sNQ5x8f8 z!`ybF4;HWvs-#hv*;K$i=o#ckR{jI@vWdofl)%-X{#IfU%2)3HOHewj;m$qFG7tx9wUO z>zB9hi*Kw03D!2~EuO6pV5B`y&Tp&m=YwWEfD!YS49rP&M))}WIa|U$aDvMfK?w*O z!2xV;h|hUgd7@87Z-<;YRK;kEitzurce?Mu0YUbSOun%Cms8P0!i83pbLG0APWrrC#d-bD8M9KAKP&u9jiD>SqoJB~zV^_=+yQL5SG zfg3m5&>`zIdk4J)cvUSs;_(mM*K+e5a&_)#<89>cpw7WDNsN3Pba3RoR z_AXdk(@?_@T4mWa$O6vjKHLG}UCZwVSrtckJ-fH~Jhte6E1mMPWf2*6v1aP=l4^w4 z|3)mS*UQhLDJy`ip%*WzW)oT=%;qBpCs${$aH@mR@Q3A>h}`cda!DbTdsx zMK}B87wZ4C$qhRi#A86%H*W1K`ZFmLMuLK#&sSmKpe;td&G+s281PUWnpv@6uvA6I zf>YxF5*)O3i1hPCI7+Q8M}ZHlK{67%Zde0G472ASZZ!Lt)XBtG*KBx#`PZoaLNyED zN6NeUOT2%%3a5##J^&uarla+t$w?c}kF5QJOb(%qjdjS^be^Hzj~uIi%S~F+H64hn zgGL@GGU}U)ilKwbVipt#E3~}0^|oMidfFn1gG8(N)NaV$NpMb|Hr}6Y?nl>gDL)xv zj%}pUI$EgS2jh4L&jFl6V^Nt2hWt14+BH|2H3Xt-eJm_;MMV zM}KXH;N!?bY&=rrnZq8-df@aaK=@U6CXmOskDY;_@cs{64{XYP4GTK|1&^Vv5iGaQ zGAzYxeZ33DYsIhkXDdKE{=Z;7>eYG4EvokWgVTRVRLnlvrR}WG$+h1eLn!@@h$36y zn?X!|x4yCO8#(ElxI2o+5cldqU7r;c%V^Z3nVgw9j?6wwh}Xf)np48tERlo6Z) zQ|Dz8ycjS$(ECVUtw(?5Y5@DbUm;?Pxqt*=M&Ho0#Mxg`r;V(&?ErH01!(m?`om7V zRSB>q19Cq;VStPzn-=0)*(a+fr`r+M2k!);DC{l?;#(Mq%FQ>l>vx1s}2#q>inIDf^jLxy017*SnZQeSvgirR@tq+ zZcf|H?Xh9`gEM+Y#(TwMM_+utMDpN$t2&yKV6Ff&2?D(MSp%q|2||W5_zz@9w2G?Q zMP-#`@5UP~o)Nx$Cw$6PMF31kdsP&WiXl1+)h=i(BP1L4EV|@cISO;o|W0T>Yty zBc`5y4=J~kK4n@O2{H1!aVLosC%vwG6{K?g`n!>w)sdN2zH?F9MRjNMd&H~vSAmEG zPTdr(`LU_DpcI-{HW4hU`HSMNIBJ0Tam{M!O4gpEu-dBo#9El)mmiu0@dZbw;w!or z2ZNMkx+i;gf^}$(n0P1mevS@SV~LOJ24)lo%1l&2*?#Vp&kP!9i+I%=; zT1RO74vTl(1arBwTdc72HSVS+O6$q0qf`jrFxK(cY`;YgQ8)X9k%& zR%GlXw%x^2C#GgoDiUx()#;udPv(abct7NXO{NHlSv)8koZ-#IaW`?--(C>$f#Gp; zE#+ht93y6fAT~NX^=f4 z5&*dJwdeq<((P{Q0&AR!g%Ve?lTRMG4=gbB%LhW&WaCVySE)W23DiOlIA&;_M33N= zM}G8F4?1yAF?oK_AM$d z?UDgm8j0xTYoA3IyFg#1nO`N8HSykMfv z-oha@t{&mmoP5?B{p6Q8QRd*U{DQyWA6zN_VnHOD3D9H#Qub5>7pHoj#aIT<=JCW7 zTnHA-3CV#>);Uc@S}hqDe&#g(uObjj?UjhBsT7mJTGf!Oo3*K6fTsUi76aR7Om`)C zTlBSeZwiQBFYDxf#liq2V~^u}ih=%BGe{r5+U21KzCNZ_0fg@QdNCQ?^0Hb8n83`T zNr09>aX7zuB7ykN)(+<<^Ai=V(LpL5rK&FhB3 z*tDQh3oDFhTb`#uo|YzFTUrgaHQ}qMd=V1w)oHU+o(8*>!*{LZ6x^!U#kR%w(s*=l0*Gtxps_tFT6F1Anp2)4tyTQ&QZ#$&s#@m-o@$nP zQ=LI=cZcQXJFG>+;qpf!vUu6OeZU9rC1_(0&#b>TlTzU=5t*T9qq^&Z-QzB8comdX zV0-vVs0ma>!GepU=BPmtblQ74mG-@~sJ9zT+}wN(4VNgVQ-n8T^tsS@W zEpR(c$3-0h;>n@37e2^P;Du#Ukpq*o8ZD{+myMrSWlqzv5MJJDHzu zHru)yZ@!`?OaDC+oCRa``!B$53rmq1s!(F*JGE>%@pb(K7(_2R&VZV-=UkT(zs+43 zY$jh(%iWtdeM>4%FLB?;;t4hc;Ypob#K;=S3zgTA@NR$Rfk`x*GMw-4}JZ( zHjY=1tn8_?fvhppocciX-Scu?`}l@{;KDE<7v>EiFU1aa-UaA zg2}$SbHC>jR^gOybYu*FDqgR|Zn}a@P5=T$rdHIQVe0H~mmQJP-|RaUJE>G!Fnb?< zt}cFFjL!A{Lu?$}+m0!abG^`VIR{mIVey;qgb0ss1jD*)n>Mxhv7#jl-h{hYr~};` zXH%6VqEk>y0+=%z8u+GM&J(#_Y`D1fbp4daeHuJ=ZI2r?Oj42 zXWOY(hZ7r7aC&N9M^_TbKOr$Ve0Z5BtsurZRUz-w_zL(c)Dphx*f}~D7&}JI{E0Q3 zRc$>lE^ff!PSM`-Ovbb1ZW5R@%yRlwg2o%H+$g z0ggXg2X)HKA_U}=iqNg4I`y~sJwH6CF{ntkq1-bYpw)Yu0d5(`XLmNx6hogxsf`m$^=gqhCY|p^!;IF+-*VVu{IypQy2FpGy5)VHe2>g<4ljNf18AE~ z?$qCm2AhZg`b70=?{e8Q6zJN5D#$bRHb%Og7ka6MWCWvr>}^HqEqY!3ux4NNtVo`u z1IItcq47Q}K}Pu3wexn1zJ$GgZPwC+<`l3q)-jqL2Gr&ENxcgG`de-L|C^WHFA10Y rr^oQGK<*c*@*f!3f4{BXw#3lkI)d2r(`2{-f{{|ZnRVUh|EviBulu+@ diff --git a/profiling/results/tims/hela_score_histogram_psm.png b/profiling/results/tims/hela_score_histogram_psm.png index cff58b3ef25d4778ba32f6b4a9b21e1402d069ce..eec935694f8ac3c5927908f0ba1dcf316552ca73 100644 GIT binary patch literal 22476 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

    kmYF{CP` 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 HiHrXPnD>tR literal 17891 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 diff --git a/profiling/results/tims/hela_scores_over_time.png b/profiling/results/tims/hela_scores_over_time.png new file mode 100644 index 0000000000000000000000000000000000000000..699253ed7b38da1b7655429a17afe5998fc366c8 GIT binary patch literal 227873 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%0DkD53;N0x-RbVjP9GanX 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

    c>NhOVW)?x95+eIK9s7} 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 {@2N?#|{k_68EXajzZPJ%o} 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

    e&df}%X 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 V1V>=Hz5oI~(q9zBt3(Zh{tHzNY|a1x literal 0 HcmV?d00001 diff --git a/profiling/run_profile.py b/profiling/run_profile.py deleted file mode 100644 index 37cf2ab..0000000 --- a/profiling/run_profile.py +++ /dev/null @@ -1,10 +0,0 @@ -from diadem import cli -from diadem.config import DiademConfig - -cli.setup_logger() -cli.diadem_main( - fasta_path="./profiling_data/uniprot_human_sp_canonical_2021-11-19_crap.fasta", - data_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", - config=DiademConfig(run_parallelism=1), - out_prefix="lineprofile_results/results", -) diff --git a/profiling/run_profile_multithread.py b/profiling/run_profile_multithread.py deleted file mode 100644 index f2591e4..0000000 --- a/profiling/run_profile_multithread.py +++ /dev/null @@ -1,10 +0,0 @@ -from diadem import cli -from diadem.config import DiademConfig - -cli.setup_logger(level="INFO") -cli.diadem_main( - fasta_path="./profiling_data/uniprot_human_sp_canonical_2021-11-19_crap.fasta", - data_path="./profiling_data/Chessfest_Plate3_RH4_DMSO_DIA.mzML", - config=DiademConfig(run_parallelism=-4), - out_prefix="lineprofile_results_multithread/results", -) diff --git a/profiling/run_profile_multithread.zsh b/profiling/run_profile_multithread.zsh deleted file mode 100644 index 3a072dd..0000000 --- a/profiling/run_profile_multithread.zsh +++ /dev/null @@ -1,22 +0,0 @@ - -python -m pip install ../. -PYTHONOPTIMIZE=1 python run_profile_multithread.py - -mokapot lineprofile_results_multithread/results.diadem.tsv.pin --test_fdr 0.01 --keep_decoys -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = `Score`, fill = factor(Label))) + geom_density(alpha=0.4) ; ggsave("lineprofile_results_multithread/raw_td_plot.png", plot = g)' -R -e 'library(tidyverse) ; foo = readr::read_tsv("lineprofile_results_multithread/results.diadem.tsv.pin") ; g <- ggplot(foo, aes(x = seq_along(`Score`), y = Score, colour = factor(Label))) + geom_point(alpha=0.4) ; ggsave("lineprofile_results_multithread/iter_score_plot.png", plot = g)' - -# [INFO] - Found 26288 peptides with q<=0.01 -# 2023-02-13 12:43:55.191 | INFO | diadem.search.diadem:diadem_main:295 - Elapsed time: 3487.5772829055786 - -## Experimental branch feature/fix_reference_peak -# Changing scoring to require the reference peak to be included -# [INFO] - Found 6759 peptides with q<=0.01 -# 2023-02-13 18:40:01.719 | INFO | diadem.search.diadem:diadem_main:305 - Elapsed time: 1881.4424259662628 - -# Tuning some parameters, changing threads to 4 -# [INFO] - Found 16790 peptides with q<=0.01 -# 2023-02-21 17:46:55.441 | INFO | diadem.search.diadem:diadem_main:312 - Elapsed time: 6328.449725866318 - -# [INFO] - Found 27119 peptides with q<=0.01 -# 2023-04-13 13:33:17.309 | INFO | diadem.search.diadem:diadem_main:425 - Elapsed time: 8089.781619787216 diff --git a/profiling/run_profile_tims.py b/profiling/run_profile_tims.py deleted file mode 100644 index 4a3ea18..0000000 --- a/profiling/run_profile_tims.py +++ /dev/null @@ -1,24 +0,0 @@ -from diadem import cli -from diadem.config import DiademConfig - -cli.setup_logger() -cli.diadem_main( - fasta_path="./profiling_data/UP000005640_9606.fasta", - # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", # noqa: E501 - data_path="./profiling_data/Hela_25ng_22min_6x3_short_S2-A4_1_118.hdf", - config=DiademConfig( - run_parallelism=1, # Needs to use 1 core for profiling - run_max_peaks=20000, - run_allowed_fails=5000, - g_tolerances=(0.02, 0.02), - g_tolerance_units=("da", "da"), - g_ims_tolerance=0.02, - g_ims_tolerance_unit="abs", - run_min_correlation_score=0.2, - run_min_intensity_ratio=0.01, - peptide_mz_range=(400, 2000), - run_scaling_ratio=0.001, - run_scalin_limits=(0.001, 0.9), - ), - out_prefix="lineprofile_results_tims/results", -) diff --git a/profiling/run_profile_tims_multithread.py b/profiling/run_profile_tims_multithread.py deleted file mode 100644 index cab0d95..0000000 --- a/profiling/run_profile_tims_multithread.py +++ /dev/null @@ -1,24 +0,0 @@ -from diadem import cli -from diadem.config import DiademConfig - -cli.setup_logger() -cli.diadem_main( - fasta_path="./profiling_data/UP000005640_9606.fasta", - # data_path="./profiling_data/20210510_TIMS03_EVO03_PaSk_SA_HeLa_50ng_5_6min_DIA_high_speed_S1-B2_1_25186.hdf", # noqa: E501 - data_path="./profiling_data/Hela_25ng_22min_6x3_short_S2-A4_1_118.hdf", - config=DiademConfig( - run_parallelism=4, # Needs to use 1 core for profiling - run_max_peaks=20000, - run_allowed_fails=5000, - g_tolerances=(0.02, 0.02), - g_tolerance_units=("da", "da"), - g_ims_tolerance=0.02, - g_ims_tolerance_unit="abs", - run_min_correlation_score=0.2, - run_min_intensity_ratio=0.01, - peptide_mz_range=(400, 2000), - run_scaling_ratio=0.001, - run_scalin_limits=(0.01, 0.99), - ), - out_prefix="lineprofile_results_tims_mt/results", -) diff --git a/profiling/src/plot_results.py b/profiling/src/plot_results.py index 114bb29..7ebd6c8 100644 --- a/profiling/src/plot_results.py +++ b/profiling/src/plot_results.py @@ -59,6 +59,8 @@ def main(): ) plt.legend() plt.title(f"PSM Score Histogram\n{prefix}") + plt.xlabel("score") + plt.ylabel("Frequency") plt.savefig(Path(base_dir) / f"{prefix}_score_histogram_psm.png") plt.clf() @@ -80,13 +82,30 @@ def main(): ) plt.legend() plt.title(f"Peptide Score Histogram\n{prefix}") + plt.xlabel("score") + plt.ylabel("Frequency") plt.savefig(Path(base_dir) / f"{prefix}_score_histogram_peptide.png") plt.yscale("log") plt.title(f"Peptide Score Histogram (log scale)\n{prefix}") + plt.xlabel("log(score)") + plt.ylabel("Frequency") plt.savefig(Path(base_dir) / f"{prefix}_log_score_histogram_peptide.png") plt.clf() + plt.scatter( + y=df["Score"], + x=np.arange(len(df)), + c=["red" if x else "blue" for x in df["decoy"]], + s=0.5, + alpha=0.4, + ) + plt.xlabel("Iteration") + plt.ylabel("Score") + plt.title(f"Peptide Score Over Iterations\n{prefix}") + plt.savefig(Path(base_dir) / f"{prefix}_scores_over_time.png") + plt.clf() + pep_parquet = pl.scan_parquet(peptide_matches[0]) qvals = ( pep_parquet.filter(pl.col("is_target") & (pl.col("mokapot q-value") < 0.05)) @@ -100,7 +119,7 @@ def main(): plt.clf() metrics = {} - metrics["NumPeptides"] = len(qvals) + metrics["NumPeptides_q_0.01"] = len(qvals.filter(pl.col("mokapot q-value") < 0.01)) metrics["AvgTargetScore"] = df.filter(pl.col("decoy").is_not())["Score"].mean() metrics["TargetQ95Score"] = df.filter(pl.col("decoy").is_not())["Score"].quantile( 0.95, diff --git a/profiling/src/plot_results_base.py b/profiling/src/plot_results_base.py deleted file mode 100644 index 0eae1da..0000000 --- a/profiling/src/plot_results_base.py +++ /dev/null @@ -1,13 +0,0 @@ -import numpy as np -import pandas as pd -from matplotlib import pyplot as plt - -out = pd.read_csv("results/tims/ecoli.diadem.csv") - -decoys = out[np.invert(out["decoy"])] -targets = out[out["decoy"]] - -plt.hist(decoys["Score"], label="Decoys", color="red", alpha=0.2, bins=100) -plt.hist(targets["Score"], label="Targets", color="blue", alpha=0.2, bins=100) - -plt.legend() diff --git a/profiling/src/run.py b/profiling/src/run.py index ae96bc4..fe84a9a 100644 --- a/profiling/src/run.py +++ b/profiling/src/run.py @@ -1,4 +1,5 @@ import argparse +import time from dataclasses import replace from diadem import cli @@ -20,6 +21,7 @@ config = DiademConfig.from_toml(args.config) config = replace(config, run_parallelism=args.threads) + st = time.time() cli.setup_logger() cli.diadem_main( fasta_path=args.fasta, @@ -27,3 +29,6 @@ config=config, out_prefix=args.output, ) + tt = time.time() - st + with open(args.output + "_runtime.toml", "w") as f: + f.write(f"runtime = {tt}\n") From 77a5dc0b49546afa92995b41458cd262ebb1c7d4 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Fri, 12 May 2023 22:01:26 -0700 Subject: [PATCH 41/41] Updated base interface --- diadem/interfaces.py | 10 ++++------ tests/unit_tests/aggregate/test_confidence.py | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 tests/unit_tests/aggregate/test_confidence.py diff --git a/diadem/interfaces.py b/diadem/interfaces.py index 4bc4e21..1901726 100644 --- a/diadem/interfaces.py +++ b/diadem/interfaces.py @@ -49,13 +49,11 @@ def __init__(self, data: pl.DataFrame | pl.LazyFrame) -> None: except AttributeError: self.data = pl.from_pandas(data).lazy() - self.validate_columns() + self.validate_schema() - def validate_columns(self) -> None: + def validate_schema(self) -> None: """Verify that the required columns are present and the correct dtype.""" - req_dtypes = { - c.name: _check_for_poly_dtype(c.dtype) for c in self.required_columns - } + req_dtypes = {c.name: _check_for_poly_dtype(c.dtype) for c in self.schema} dtype_errors = [] missing_columns = set(req_dtypes.keys()) @@ -90,7 +88,7 @@ def validate_columns(self) -> None: @property @abstractmethod - def required_columns(self) -> Iterable[RequiredColumn]: + def schema(self) -> Iterable[RequiredColumn]: """The required columns for the underlying DataFrame.""" diff --git a/tests/unit_tests/aggregate/test_confidence.py b/tests/unit_tests/aggregate/test_confidence.py new file mode 100644 index 0000000..c6c8b3b --- /dev/null +++ b/tests/unit_tests/aggregate/test_confidence.py @@ -0,0 +1 @@ +"""Tests global FDR estimation."""