diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a8de8a90..752226d7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.1 +current_version = 1.1.2 commit = True tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)(?P\d+))? diff --git a/README.md b/README.md index 7e4da5e9..e1e8910a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ The infrastructure package of AlphaX ecosystem for MS proteomics. It was first p - [AlphaPeptDeep](https://github.com/MannLabs/alphapeptdeep): deep learning framework for proteomics. - [AlphaRaw](https://github.com/MannLabs/alpharaw): raw data reader for different vendors. +- [AlphaDIA](https://github.com/MannLabs/alphadia): DIA search engine. +- [PeptDeep-HLA](https://github.com/MannLabs/peptdeep-hla): personalized HLA-binding peptide prediction. +- [AlphaViz](https://github.com/MannLabs/alphaviz): visualization for MS-based proteomics. +- [AlphaQuant](https://github.com/MannLabs/alphaquant): quantification for MS-based proteomics. ------------------------------------------------------------------------ diff --git a/alphabase/__init__.py b/alphabase/__init__.py index 23f5ac84..cf89ba67 100644 --- a/alphabase/__init__.py +++ b/alphabase/__init__.py @@ -2,7 +2,7 @@ __project__ = "alphabase" -__version__ = "1.1.1" +__version__ = "1.1.2" __license__ = "Apache" __description__ = "An infrastructure Python package of the AlphaX ecosystem" __author__ = "Mann Labs" diff --git a/alphabase/constants/aa.py b/alphabase/constants/aa.py index 9a59f90f..dcd8d0cd 100644 --- a/alphabase/constants/aa.py +++ b/alphabase/constants/aa.py @@ -1,14 +1,14 @@ import os import pandas as pd import numpy as np - -from typing import Union, Tuple +import typing from alphabase.yaml_utils import load_yaml from alphabase.constants.element import ( calc_mass_from_formula, MASS_H2O, parse_formula, + reset_elements ) from alphabase.constants._const import CONST_FILE_FOLDER @@ -19,19 +19,34 @@ AA_Formula:dict = load_yaml( os.path.join(CONST_FILE_FOLDER, 'amino_acid.yaml') ) +#: AA mass array with ASCII code, mass of 'A' is AA_ASCII_MASS[ord('A')] +AA_ASCII_MASS:np.ndarray = np.ones(128)*1e8 + +#: 128-len AA dataframe +AA_DF:pd.DataFrame = pd.DataFrame() + +# AA formula to formula dict of dict. For example: {'K': {'C': n, 'O': m, ...}} +AA_Composition:dict = {} + +def replace_atoms(atom_replace_dict:typing.Dict): + for aa, formula in list(AA_Formula.items()): + atom_comp = dict(parse_formula(formula)) + for atom_from, atom_to in atom_replace_dict.items(): + if atom_from in atom_comp: + atom_comp[atom_to] = atom_comp[atom_from] + del atom_comp[atom_from] + AA_Formula[aa] = "".join([f"{atom}({n})" for atom, n in atom_comp.items()]) def reset_AA_mass()->np.ndarray: """AA mass in np.array with shape (128,)""" - AA_ASCII_MASS = np.ones(128)*1e8 + global AA_ASCII_MASS for aa, chem in AA_Formula.items(): AA_ASCII_MASS[ord(aa)] = calc_mass_from_formula(chem) return AA_ASCII_MASS - -#: AA mass array with ASCII code, mass of 'A' is AA_ASCII_MASS[ord('A')] -AA_ASCII_MASS:np.ndarray = reset_AA_mass() +reset_AA_mass() def reset_AA_df(): - global AA_ASCII_MASS + global AA_DF AA_DF = pd.DataFrame() AA_DF['aa'] = [chr(aa) for aa in range(len(AA_ASCII_MASS))] AA_DF['formula'] = ['']*len(AA_ASCII_MASS) @@ -42,23 +57,31 @@ def reset_AA_df(): formulas.append(formula) AA_DF.loc[aa_idxes, 'formula'] = formulas AA_DF['mass'] = AA_ASCII_MASS - AA_ASCII_MASS = AA_DF.mass.to_numpy() return AA_DF - -#: 128-len AA dataframe -AA_DF:pd.DataFrame = reset_AA_df() - -# AA to formula dict of dict. For example: {'K': {'C': n, 'O': m, ...}} -AA_Composition:dict = {} -for aa, formula, mass in AA_DF.values: - AA_Composition[aa] = dict( - parse_formula(formula) - ) +reset_AA_df() + +def reset_AA_Composition(): + global AA_Composition + AA_Composition = {} + for aa, formula, mass in AA_DF.values: + AA_Composition[aa] = dict( + parse_formula(formula) + ) + return AA_Composition +reset_AA_Composition() + +def reset_AA_atoms(atom_replace_dict:typing.Dict = {}): + reset_elements() + replace_atoms(atom_replace_dict) + reset_AA_mass() + reset_AA_df() + reset_AA_Composition() def update_an_AA(aa:str, formula:str): aa_idx = ord(aa) AA_DF.loc[aa_idx,'formula'] = formula - AA_DF.loc[aa_idx,'mass'] = calc_mass_from_formula(formula) + AA_ASCII_MASS[aa_idx] = calc_mass_from_formula(formula) + AA_DF.loc[aa_idx,'mass'] = AA_ASCII_MASS[aa_idx] AA_Formula[aa] = formula AA_Composition[aa] = dict(parse_formula(formula)) diff --git a/alphabase/constants/atom.py b/alphabase/constants/atom.py index ec3ac4be..5f2b2cf0 100644 --- a/alphabase/constants/atom.py +++ b/alphabase/constants/atom.py @@ -1,6 +1,7 @@ import os import numpy as np import numba +import typing from alphabase.yaml_utils import load_yaml @@ -89,7 +90,25 @@ def truncate_isotope( MASS_H2O:int = None #raise errors if the value is not reset MASS_NH3:int = None +def update_atom_infos(new_atom_info:typing.Dict): + """ + Args: + atom_dict (Dict): Example, replacing N with 15N + {"N": + {"abundance": [0.01,0.99]}, + {"mass": [14.00307400443, 15.00010889888]}, + } + """ + for atom, info in new_atom_info.items(): + CHEM_INFO_DICT[atom] = info + + reset_elements() + def reset_elements(): + + global MASS_C, MASS_H, MASS_O, MASS_N + global MASS_H2O, MASS_NH3 + for elem, items in CHEM_INFO_DICT.items(): isotopes = np.array(items['abundance']) masses = np.array(items['mass']) @@ -120,6 +139,13 @@ def reset_elements(): CHEM_ISOTOPE_DIST[elem] = _isos[start:end] CHEM_MONO_IDX[elem] = _mono_idx + + MASS_C = CHEM_MONO_MASS['C'] + MASS_H = CHEM_MONO_MASS['H'] + MASS_N = CHEM_MONO_MASS['N'] + MASS_O = CHEM_MONO_MASS['O'] + MASS_H2O = CHEM_MONO_MASS['H']*2 + CHEM_MONO_MASS['O'] + MASS_NH3 = CHEM_MONO_MASS['H']*3 + CHEM_MONO_MASS['N'] def load_elem_yaml(yaml_file:str): '''Load built-in or user-defined element yaml file. Default yaml is: @@ -129,8 +155,6 @@ def load_elem_yaml(yaml_file:str): global CHEM_MONO_MASS global CHEM_ISOTOPE_DIST global CHEM_MONO_IDX - global MASS_C, MASS_H, MASS_O, MASS_N - global MASS_H2O, MASS_NH3 CHEM_INFO_DICT = load_yaml(yaml_file) @@ -146,13 +170,6 @@ def load_elem_yaml(yaml_file:str): ) reset_elements() - - MASS_C = CHEM_MONO_MASS['C'] - MASS_H = CHEM_MONO_MASS['H'] - MASS_N = CHEM_MONO_MASS['N'] - MASS_O = CHEM_MONO_MASS['O'] - MASS_H2O = CHEM_MONO_MASS['H']*2 + CHEM_MONO_MASS['O'] - MASS_NH3 = CHEM_MONO_MASS['H']*3 + CHEM_MONO_MASS['N'] load_elem_yaml( os.path.join(CONST_FILE_FOLDER, diff --git a/alphabase/peptide/fragment.py b/alphabase/peptide/fragment.py index d9851c75..cd9a4908 100644 --- a/alphabase/peptide/fragment.py +++ b/alphabase/peptide/fragment.py @@ -588,10 +588,12 @@ def flatten_fragments( input precursor dataframe which contains the frag_start_idx and frag_stop_idx columns fragment_mz_df : pd.DataFrame - input fragment mz dataframe of shape (N, T) which contains N * T fragment mzs + input fragment mz dataframe of shape (N, T) which contains N * T fragment mzs. + Fragments with mz==0 will be excluded. fragment_intensity_df : pd.DataFrame - input fragment mz dataframe of shape (N, T) which contains N * T fragment mzs + input fragment intensity dataframe of shape (N, T) which contains N * T fragment mzs. + Could be empty (len==0) to exclude intensity values. min_fragment_intensity : float, optional minimum intensity which should be retained. Defaults to -1 @@ -758,10 +760,12 @@ def compress_fragment_indices(frag_idx): def remove_unused_fragments( precursor_df: pd.DataFrame, - fragment_df_list: Tuple[pd.DataFrame, ...] + fragment_df_list: Tuple[pd.DataFrame, ...], + frag_start_col:str = 'frag_start_idx', + frag_stop_col:str = 'frag_stop_idx', ) -> Tuple[pd.DataFrame, Tuple[pd.DataFrame, ...]]: """Removes unused fragments of removed precursors, - reannotates the frag_start_idx and frag_stop_idx + reannotates the `frag_start_col` and `frag_stop_col` Parameters ---------- @@ -773,6 +777,14 @@ def remove_unused_fragments( Multiple fragment dataframes can be provided which will all be sliced in the same way. This allows to slice both the fragment_mz_df and fragment_intensity_df. At least one fragment dataframe needs to be provided. + + frag_start_col : str, optional + Fragment start idx column in `precursor_df`, such as "frag_start_idx" and "peak_start_idx". + Defaults to "frag_start_idx". + + frag_stop_col : str, optional + Fragment stop idx column in `precursor_df`, such as "frag_stop_idx" and "peak_stop_idx". + Defaults to "frag_stop_idx". Returns ------- @@ -780,12 +792,12 @@ def remove_unused_fragments( returns the reindexed precursor DataFrame and the sliced fragment DataFrames """ - precursor_df = precursor_df.sort_values(['frag_start_idx'], ascending=True) - frag_idx = precursor_df[['frag_start_idx','frag_stop_idx']].values + precursor_df = precursor_df.sort_values([frag_start_col], ascending=True) + frag_idx = precursor_df[[frag_start_col,frag_stop_col]].values new_frag_idx, fragment_pointer = compress_fragment_indices(frag_idx) - precursor_df[['frag_start_idx','frag_stop_idx']] = new_frag_idx + precursor_df[[frag_start_col,frag_stop_col]] = new_frag_idx precursor_df = precursor_df.sort_index() output_tuple = [] diff --git a/alphabase/peptide/precursor.py b/alphabase/peptide/precursor.py index 2945fb96..94c31577 100644 --- a/alphabase/peptide/precursor.py +++ b/alphabase/peptide/precursor.py @@ -1,6 +1,7 @@ import pandas as pd import numpy as np import numba +import typing import multiprocessing as mp from tqdm import tqdm @@ -486,10 +487,10 @@ def _count_batchify_df(df_group, mp_batch_size): def calc_precursor_isotope_mp( precursor_df:pd.DataFrame, processes:int=8, - mp_batch_size:int=100000, + mp_batch_size:int=10000, process_bar=None, min_right_most_intensity:float=0.2, - min_precursor_num_to_run_mp:int=1000, + min_precursor_num_to_run_mp:int=10000, )->pd.DataFrame: """`calc_precursor_isotope` is not that fast for large dataframes, so here we use multiprocessing for faster isotope pattern calculation. @@ -547,8 +548,9 @@ def calc_precursor_isotope_mp( def calc_precursor_isotope_intensity( precursor_df, max_isotope = 6, - min_right_most_intensity = 0.001 - ): + min_right_most_intensity = 0.001, + normalize:typing.Literal['mono','sum'] = "sum", +)->pd.DataFrame: """Calculate isotope intensity values for precursor_df inplace. Parameters @@ -577,6 +579,8 @@ def calc_precursor_isotope_intensity( precursor_dist = np.zeros((len(precursor_df), max_isotope), dtype=np.float32) + mono_idxes = np.zeros(len(precursor_df),dtype=np.int32) + for i in range(len(precursor_df)): row = precursor_df.iloc[i] @@ -584,10 +588,36 @@ def calc_precursor_isotope_intensity( get_mod_seq_formula(row['sequence'], row['mods']) ) dist[dist <= min_right_most_intensity] = 0. - dist = dist / dist.sum() - precursor_dist[i] = dist[:max_isotope] + + # mono should be always included in the i_x list + # after clipping max_isotope isotopes + mono_left_half_isotope = max_isotope//2 + mono_right_half_isotope = ( + mono_left_half_isotope if max_isotope%2==0 + else (mono_left_half_isotope+1) + ) + if mono < mono_left_half_isotope: + precursor_dist[i] = dist[:max_isotope] + mono_idxes[i] = mono + elif mono + mono_right_half_isotope >= len(dist): + precursor_dist[i] = dist[-max_isotope:] + mono_idxes[i] = max_isotope+mono-len(dist)+1 + else: + precursor_dist[i] = dist[ + mono-mono_left_half_isotope: + mono+mono_right_half_isotope + ] + mono_idxes[i] = mono-mono_left_half_isotope + + if normalize == "sum": + precursor_dist /= np.sum(precursor_dist, axis=1, keepdims=True) + else: + precursor_dist /= precursor_dist[ + np.arange(len(precursor_dist)), mono_idxes + ].reshape(-1,1) precursor_df[col_names] = precursor_dist + precursor_df["mono_isotope_idx"] = mono_idxes return precursor_df @@ -595,10 +625,11 @@ def calc_precursor_isotope_intensity_mp( precursor_df, max_isotope = 6, min_right_most_intensity = 0.001, + normalize:typing.Literal['mono','sum'] = "sum", mp_batch_size = 1000, mp_process_num = 8, - progress_bar = True - ): + progress_bar = True, +)->pd.DataFrame: """Calculate isotope intensity values for precursor_df using multiprocessing. @@ -639,7 +670,8 @@ def calc_precursor_isotope_intensity_mp( partial( calc_precursor_isotope_intensity, max_isotope=max_isotope, - min_right_most_intensity=min_right_most_intensity + min_right_most_intensity=min_right_most_intensity, + normalize=normalize, ), _batchify_df(df_group, mp_batch_size) ) diff --git a/alphabase/spectral_library/base.py b/alphabase/spectral_library/base.py index 839a877d..dc73270d 100644 --- a/alphabase/spectral_library/base.py +++ b/alphabase/spectral_library/base.py @@ -326,7 +326,7 @@ def calc_precursor_isotope_intensity(self, multiprocessing : bool=True, max_isotope = 6, min_right_most_intensity = 0.001, - mp_batch_size = 1000, + mp_batch_size = 10000, mp_process_num = 8 ): """ diff --git a/alphabase/spectral_library/decoy.py b/alphabase/spectral_library/decoy.py index a680d38f..fe490b4e 100644 --- a/alphabase/spectral_library/decoy.py +++ b/alphabase/spectral_library/decoy.py @@ -1,15 +1,92 @@ import copy +from typing import Any import pandas as pd +import multiprocessing as mp +from functools import partial from alphabase.spectral_library.base import SpecLibBase from alphabase.io.hdf import HDF_File +def _batchify_series(series, mp_batch_size): + """Internal funciton for multiprocessing""" + for i in range(0, len(series), mp_batch_size): + yield series.iloc[i:i+mp_batch_size] +class BaseDecoyGenerator(object): + """ + Base class for decoy generator. + A class is used instead of a function to make as it needs to be pickled for multiprocessing. + """ + def __call__(self, series: pd.Series) -> pd.Series: + """ + Main entry of this class, it calls follows methods: + - self._decoy() + """ + + return series.apply(self._decoy) + + def _decoy(self, sequence:str) -> str: + raise NotImplementedError('Subclass should implement this method.') + +class DIANNDecoyGenerator(BaseDecoyGenerator): + def __init__(self, + raw_AAs:str = 'GAVLIFMPWSCTYHKRQENDBJOUXZ', + mutated_AAs:str = 'LLLVVLLLLTSSSSLLNDQEVVVVVV' + ): + + """ + DiaNN-like decoy peptide generator + + Parameters + ---------- + + raw_AAs : str, optional + AAs those DiaNN decoy from. + Defaults to 'GAVLIFMPWSCTYHKRQENDBJOUXZ'. + + mutated_AAs : str, optional + AAs those DiaNN decoy to. + Defaults to 'LLLVVLLLLTSSSSLLNDQEVVVVVV'. + + """ + self.raw_AAs = raw_AAs + self.mutated_AAs = mutated_AAs + + + def _decoy(self, sequence: str) -> str: + return sequence[0]+ \ + self.mutated_AAs[self.raw_AAs.index(sequence[1])]+ \ + sequence[2:-2]+ \ + self.mutated_AAs[self.raw_AAs.index(sequence[-2])]+ \ + sequence[-1] + +class PseudoReverseDecoyGenerator(BaseDecoyGenerator): + def __init__(self, fix_C_term:bool=True): + """ + Pseudo-reverse decoy generator. + + Parameters + ---------- + + fix_C_term : bool, optional + If fix C-term AA when decoy. + Defaults to True. + """ + + self.fix_C_term = fix_C_term + + def _decoy(self, sequence: str) -> str: + if self.fix_C_term: + return (sequence[:-1][::-1] + sequence[-1]) + else: + return sequence[::-1] + class SpecLibDecoy(SpecLibBase): """ Pseudo-reverse peptide decoy generator. """ + def __init__(self, target_lib:SpecLibBase, - fix_C_term = True, + decoy_generator: Any = PseudoReverseDecoyGenerator, **kwargs, ): """ @@ -29,26 +106,48 @@ def __init__(self, """ self.__dict__ = copy.deepcopy(target_lib.__dict__) self.target_lib = target_lib - self.fix_C_term = fix_C_term - def translate_to_decoy(self): + self.generator = decoy_generator( + **kwargs + ) + + def translate_to_decoy( + self, + multiprocessing : bool = True, + mp_batch_size=10000, + mp_process_num: int = 8): """ Main entry of this class, it calls follows methods: - self.decoy_sequence() - - self._decoy_mods() - - self._decoy_meta() - - self._decoy_frags() + + Parameters + ---------- + + multiprocessing : bool, optional + If true use multiprocessing. + Defaults to True. + + mp_batch_size : int, optional + Batch size for multiprocessing. + Defaults to 10000. + + mp_process_num : int, optional + Number of processes for multiprocessing. + Defaults to 8. + """ - self.decoy_sequence() - self._decoy_mods() - self._decoy_meta() - self._decoy_frags() + self.decoy_sequence( + multiprocessing=multiprocessing, + mp_batch_size=mp_batch_size, + mp_process_num=mp_process_num + ) def append_to_target_lib(self): """ A decoy method should define how to append itself to target_lib. Sub-classes should override this method when necessary. """ + self._remove_target_seqs() self._precursor_df['decoy'] = 1 self.target_lib._precursor_df['decoy'] = 0 self.target_lib._precursor_df = pd.concat(( @@ -57,24 +156,51 @@ def append_to_target_lib(self): ), ignore_index=True) self.target_lib.refine_df() - def decoy_sequence(self): + def decoy_sequence( + self, + multiprocessing: bool = True, + mp_batch_size=10000, + mp_process_num: int = 8 + ): """ Generate decoy sequences from `self.target_lib`. - Sub-classes should override this method when necessary. + Sub-classes should override the `_decoy_seq` method when necessary. + + Parameters + ---------- + + multiprocessing : bool, optional + If true use multiprocessing. + Defaults to True. + + mp_batch_size : int, optional + Batch size for multiprocessing. + Defaults to 10000. + + mp_process_num : int, optional + Number of processes for multiprocessing. + Defaults to 8. """ - self._decoy_seq() - self._remove_target_seqs() - def append_decoy_sequence(self): - pass + if not multiprocessing or self._precursor_df.shape[0] < mp_batch_size: + self._precursor_df['sequence'] = self.generator(self._precursor_df['sequence']) + self._remove_target_seqs() + return + + sequence_batches = list(_batchify_series( + self._precursor_df['sequence'], mp_batch_size + )) - def _decoy_seq(self): - ( - self._precursor_df.sequence - ) = self._precursor_df.sequence.apply( - lambda x: (x[:-1][::-1]+x[-1]) - if self.fix_C_term else x[::-1] - ) + series_list = [] + with mp.get_context("spawn").Pool(mp_process_num) as p: + processing = p.imap( + self.generator, + sequence_batches + ) + for df in processing: + series_list.append(df) + self._precursor_df['sequence'] = pd.concat(series_list) + self._remove_target_seqs() def _remove_target_seqs(self): target_seqs = set( @@ -86,110 +212,6 @@ def _remove_target_seqs(self): ].index, inplace=True ) - def _decoy_meta(self): - """ - Decoy for CCS/RT or other meta data - """ - pass - - def _decoy_mods(self): - """ - Decoy for modifications and modification sites - """ - pass - - def _decoy_frags(self): - """ - Decoy for fragment masses and intensities - """ - self._decoy_fragment_mz() - self._decoy_fragment_intensity() - - def _decoy_fragment_mz(self): - pass - - def _decoy_fragment_intensity(self): - pass - - def _get_hdf_to_save(self, - hdf_file, - delete_existing=False - ): - _hdf = HDF_File( - hdf_file, - read_only=False, - truncate=True, - delete_existing=delete_existing - ) - return _hdf.library.decoy - - def _get_hdf_to_load(self, - hdf_file, - ): - _hdf = HDF_File( - hdf_file, - ) - return _hdf.library.decoy - - def save_hdf(self, hdf_file): - _hdf = HDF_File( - hdf_file, - read_only=False, - truncate=True, - delete_existing=False - ) - _hdf.library.decoy = { - 'precursor_df': self._precursor_df, - 'fragment_mz_df': self._fragment_mz_df, - 'fragment_intensity_df': self._fragment_intensity_df, - } - - def load_hdf(self, hdf_file): - _hdf = HDF_File( - hdf_file, - ) - _hdf_lib = _hdf.library - self._precursor_df = _hdf_lib.decoy.precursor_df.values - self._fragment_mz_df = _hdf_lib.decoy.fragment_mz_df.values - self._fragment_intensity_df = _hdf_lib.decoy.fragment_intensity_df.values - -class SpecLibDecoyDiaNN(SpecLibDecoy): - def __init__(self, - target_lib:SpecLibBase, - raw_AAs:str = 'GAVLIFMPWSCTYHKRQENDBJOUXZ', - mutated_AAs:str = 'LLLVVLLLLTSSSSLLNDQEVVVVVV', #DiaNN - **kwargs, - ): - """ - DiaNN-like decoy peptide generator - - Parameters - ---------- - target_lib : SpecLibBase - Target library object - - raw_AAs : str, optional - AAs those DiaNN decoy from. - Defaults to 'GAVLIFMPWSCTYHKRQENDBJOUXZ'. - - mutated_AAs : str, optional - AAs those DiaNN decoy to. - Defaults to 'LLLVVLLLLTSSSSLLNDQEVVVVVV'. - - """ - super().__init__(target_lib) - self.raw_AAs = raw_AAs - self.mutated_AAs = mutated_AAs - - def _decoy_seq(self): - ( - self._precursor_df.sequence - ) = self._precursor_df.sequence.apply( - lambda x: - x[0]+self.mutated_AAs[self.raw_AAs.index(x[1])]+ - x[2:-2]+self.mutated_AAs[self.raw_AAs.index(x[-2])]+x[-1] - ) - class SpecLibDecoyProvider(object): def __init__(self): self.decoy_dict = {} @@ -198,8 +220,10 @@ def register(self, name:str, decoy_class:SpecLibDecoy): """Register a new decoy class""" self.decoy_dict[name.lower()] = decoy_class - def get_decoy_lib(self, name:str, - target_lib:SpecLibBase, **kwargs + def get_decoy_lib(self, + name:str, + target_lib:SpecLibBase, + **kwargs )->SpecLibDecoy: """Get an object of a subclass of `SpecLibDecoy` based on registered name. @@ -217,11 +241,15 @@ def get_decoy_lib(self, name:str, SpecLibDecoy Decoy library object """ - if name is None: return None + if not name: return None name = name.lower() + if name == "none" or name == "null": + return None if name in self.decoy_dict: - return self.decoy_dict[name]( - target_lib, **kwargs + return SpecLibDecoy( + target_lib, + decoy_generator = self.decoy_dict[name], + **kwargs ) else: raise ValueError(f'Decoy method {name} not found.') @@ -232,5 +260,5 @@ def get_decoy_lib(self, name:str, register and get different types of decoy methods. """ -decoy_lib_provider.register('pseudo_reverse', SpecLibDecoy) -decoy_lib_provider.register('diann', SpecLibDecoyDiaNN) +decoy_lib_provider.register('pseudo_reverse', PseudoReverseDecoyGenerator) +decoy_lib_provider.register('diann', DIANNDecoyGenerator) diff --git a/docs/_static/diagrams/loader_classes.drawio b/docs/_static/diagrams/loader_classes.drawio new file mode 100644 index 00000000..50da33f2 --- /dev/null +++ b/docs/_static/diagrams/loader_classes.drawio @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/diagrams/loader_classes.drawio.svg b/docs/_static/diagrams/loader_classes.drawio.svg new file mode 100644 index 00000000..c38e37a5 --- /dev/null +++ b/docs/_static/diagrams/loader_classes.drawio.svg @@ -0,0 +1,4 @@ + + + +SpecLibBaseprecursor_dffragment_mz_dffragment_intensity_dfcharged_frag_typesmin_precursor_mzmax_precursor_mzdecoySpecLibDecoytarget_libfix_C_termSpecLibDecoyDiaNNraw_AAsmutated_AAsSpecLibFlatprecursor_dffragment_dfmin_fragment_intensitykeep_top_k_fragmentscustom_fragment_df_columnsPSMReaderBasepsm_dfcolumn_mappingmodification_mappingkeep_fdrkeep_decoy_min_max_rt_normMaxQuantReaderfixed_C57mod_seq_columnAlphaPeptReaderhdf_datasetMSFraggerPepXMLpFindReaderSpectronautReadercsv_sepSwathReaderDiannReaderSpectronautReportReadercsv_sepprecursor_columnLibraryReaderBase_frag_type_columns_frag_number_columns_frag_charge_columns_frag_loss_type_columns_frag_inten_columns \ No newline at end of file diff --git a/docs/_static/diagrams/loader_classes_new.drawio b/docs/_static/diagrams/loader_classes_new.drawio new file mode 100644 index 00000000..b10e9737 --- /dev/null +++ b/docs/_static/diagrams/loader_classes_new.drawio @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/diagrams/loader_classes_new.drawio.svg b/docs/_static/diagrams/loader_classes_new.drawio.svg new file mode 100644 index 00000000..30165387 --- /dev/null +++ b/docs/_static/diagrams/loader_classes_new.drawio.svg @@ -0,0 +1,4 @@ + + + +SpecLibBaseprecursor_dffragment_mz_dffragment_intensity_dfcharged_frag_typesmin_precursor_mzmax_precursor_mzdecoySpecLibDecoytarget_libfix_C_termSpecLibDecoyDiaNNraw_AAsmutated_AAsSpecLibFlatprecursor_dffragment_dfmin_fragment_intensitykeep_top_k_fragmentscustom_fragment_df_columnsPSMReaderBasepsm_dfcolumn_mappingmodification_mappingkeep_fdrkeep_decoy_min_max_rt_normCSVReaderBasefixed_C57mod_seq_columncsv_sepAlphaPeptReaderhdf_datasetMSFraggerPepXMLpFindReaderSpectronautReaderDiannReaderSpectronautReportReadercsv_sepprecursor_columnLibraryReaderBase_frag_type_columns_frag_number_columns_frag_charge_columns_frag_loss_type_columns_frag_inten_columnsAlphaDIAReader \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 9b962b73..182e6555 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ copyright = '2022, Mann Labs, MPIB' author = 'Mann Labs, MPIB' -release = "1.1.1" +release = "1.1.2" # -- General configuration --------------------------------------------------- diff --git a/nbdev_nbs/constants/aa.ipynb b/nbdev_nbs/constants/aa.ipynb index 703e355b..6dbf3502 100644 --- a/nbdev_nbs/constants/aa.ipynb +++ b/nbdev_nbs/constants/aa.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -258,7 +258,7 @@ "90 Z C(1000000) 1.200000e+07" ] }, - "execution_count": null, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -293,7 +293,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -314,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -339,7 +339,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -359,7 +359,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -382,7 +382,7 @@ " 453.26996726, 396.24850354, 259.18959168, 146.1055277 ]])}" ] }, - "execution_count": null, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -404,7 +404,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -424,7 +424,7 @@ " 1.28094963e+02]])" ] }, - "execution_count": null, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -436,7 +436,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -445,6 +445,20 @@ "assert AA_Composition['Z']['C'] == 10" ] }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "replace_atoms({'N':'15N'})\n", + "assert '15N' in AA_Formula['A']\n", + "assert '15N' in AA_Formula['K']\n", + "replace_atoms({\"15N\":'N'})\n", + "assert '15N' not in AA_Formula['A']\n", + "assert '15N' not in AA_Formula['K']" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/nbdev_nbs/peptide/precursor.ipynb b/nbdev_nbs/peptide/precursor.ipynb index 2d78858d..244f5465 100644 --- a/nbdev_nbs/peptide/precursor.ipynb +++ b/nbdev_nbs/peptide/precursor.ipynb @@ -420,6 +420,226 @@ "assert get_mod_seq_charge_hash(\"AGHCEWQMKAADER\",'Acetyl@Protein N-term;Carbamidomethyl@C;Oxidation@M','0;4;8',2) == precursor_df.mod_seq_charge_hash.values[0]" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# sum normalization\n", + "sum_norm_intens = np.array([[0.504251,0.290763,0.139951,0.048122,0.013660,0.003253],\n", + " [0.504251,0.290763,0.139951,0.048122,0.013660,0.003253],\n", + " [0.360538,0.320501,0.190923,0.085047,0.030905,0.009528],\n", + " [0.360538,0.320501,0.190923,0.085047,0.030905,0.009528]]\n", + ")\n", + "\n", + "# mono normalization\n", + "mono_norm_intens = np.array([[1., 0.5766, 0.2775, 0.0954, 0.0270, 0.0064],\n", + " [1., 0.5766, 0.2775, 0.0954, 0.0270, 0.0064],\n", + " [1., 0.8889, 0.5295, 0.2358, 0.0857, 0.0264],\n", + " [1., 0.8889, 0.5295, 0.2358, 0.0857, 0.0264]], \n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sequencemodsmod_sitesnAAchargeprecursor_mzisotope_m1_intensityisotope_apex_intensityisotope_apex_offsetisotope_right_most_intensityisotope_right_most_offsetisotope_m1_mzisotope_apex_mzisotope_right_most_mzmod_seq_hashmod_seq_charge_hash
0AGHCEWQMKAADERAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;8142873.8697710.8889521.000.2358893874.371421873.869771875.3747211323284730455794676713232847304557946769
1AGHCEWQMKAADERAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;8142873.8697710.8889521.000.2358893874.371421873.869771875.3747211323284730455794676713232847304557946769
2AGHCEWQMK92545.2338620.5766231.000.2775422545.735512545.233862546.23716292111825455857905369211182545585790538
3AGHCEWQMK92545.2338620.5766231.000.2775422545.735512545.233862546.23716292111825455857905369211182545585790538
\n", + "
" + ], + "text/plain": [ + " sequence mods \\\n", + "0 AGHCEWQMKAADER Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", + "1 AGHCEWQMKAADER Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", + "2 AGHCEWQMK \n", + "3 AGHCEWQMK \n", + "\n", + " mod_sites nAA charge precursor_mz isotope_m1_intensity \\\n", + "0 0;4;8 14 2 873.869771 0.888952 \n", + "1 0;4;8 14 2 873.869771 0.888952 \n", + "2 9 2 545.233862 0.576623 \n", + "3 9 2 545.233862 0.576623 \n", + "\n", + " isotope_apex_intensity isotope_apex_offset isotope_right_most_intensity \\\n", + "0 1.0 0 0.235889 \n", + "1 1.0 0 0.235889 \n", + "2 1.0 0 0.277542 \n", + "3 1.0 0 0.277542 \n", + "\n", + " isotope_right_most_offset isotope_m1_mz isotope_apex_mz \\\n", + "0 3 874.371421 873.869771 \n", + "1 3 874.371421 873.869771 \n", + "2 2 545.735512 545.233862 \n", + "3 2 545.735512 545.233862 \n", + "\n", + " isotope_right_most_mz mod_seq_hash mod_seq_charge_hash \n", + "0 875.374721 13232847304557946767 13232847304557946769 \n", + "1 875.374721 13232847304557946767 13232847304557946769 \n", + "2 546.237162 9211182545585790536 9211182545585790538 \n", + "3 546.237162 9211182545585790536 9211182545585790538 " + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precursor_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "repeat = 2\n", + "peptides = ['AGHCEWQMK']*repeat\n", + "mods = ['']*repeat\n", + "sites = ['']*repeat\n", + "peptides += ['AGHCEWQMKAADER']*repeat\n", + "mods += ['Acetyl@Protein N-term;Carbamidomethyl@C;Oxidation@M']*repeat\n", + "sites += ['0;4;8']*repeat\n", + "\n", + "precursor_df = pd.DataFrame({\n", + " 'sequence': peptides,\n", + " 'mods': mods,\n", + " 'mod_sites': sites\n", + "})\n", + "precursor_df['nAA'] = precursor_df['sequence'].str.len()\n", + "precursor_df['charge'] = 2\n", + "\n", + "precursor_df = calc_precursor_isotope_intensity(precursor_df,normalize=\"mono\")\n", + "\n", + "assert all(col in precursor_df.columns for col in ['i_0','i_1','i_2','i_3','i_4','i_5'])\n", + "\n", + "assert np.allclose(\n", + " precursor_df[['i_0','i_1','i_2','i_3','i_4','i_5']].values,\n", + " mono_norm_intens,\n", + " 0.01\n", + ")" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -435,12 +655,12 @@ "outputs": [], "source": [ "repeat = 2\n", - "peptides = ['AGHCEWQMKAADER']*repeat\n", - "mods = ['Acetyl@Protein N-term;Carbamidomethyl@C;Oxidation@M']*repeat\n", - "sites = ['0;4;8']*repeat\n", - "peptides += ['AGHCEWQMK']*repeat\n", - "mods += ['']*repeat\n", - "sites += ['']*repeat\n", + "peptides = ['AGHCEWQMK']*repeat\n", + "mods = ['']*repeat\n", + "sites = ['']*repeat\n", + "peptides += ['AGHCEWQMKAADER']*repeat\n", + "mods += ['Acetyl@Protein N-term;Carbamidomethyl@C;Oxidation@M']*repeat\n", + "sites += ['0;4;8']*repeat\n", "\n", "precursor_df = pd.DataFrame({\n", " 'sequence': peptides,\n", @@ -460,25 +680,28 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 2/2 [00:02<00:00, 1.07s/it]\n" + "100%|██████████| 2/2 [00:02<00:00, 1.20s/it]\n" ] } ], "source": [ - "precursor_df = calc_precursor_isotope_intensity_mp(precursor_df)\n", + "precursor_df = calc_precursor_isotope_intensity_mp(precursor_df,normalize=\"sum\")\n", "\n", "assert all(col in precursor_df.columns for col in ['i_0','i_1','i_2','i_3','i_4','i_5'])\n", "\n", "assert np.allclose(\n", " precursor_df[['i_0','i_1','i_2','i_3','i_4','i_5']].values,\n", - " np.array([[0.504251,0.290763,0.139951,0.048122,0.013660,0.003253],\n", - " [0.504251,0.290763,0.139951,0.048122,0.013660,0.003253],\n", - " [0.360538,0.320501,0.190923,0.085047,0.030905,0.009528],\n", - " [0.360538,0.320501,0.190923,0.085047,0.030905,0.009528]]\n", - " ),\n", + " sum_norm_intens,\n", " 0.01\n", ")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/nbdev_nbs/protein/test_lcp.ipynb b/nbdev_nbs/protein/test_lcp.ipynb index 93d47aa8..f5428247 100644 --- a/nbdev_nbs/protein/test_lcp.ipynb +++ b/nbdev_nbs/protein/test_lcp.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "outputs": [], "source": [ - "from alphabase.protein.lcp_digest import *" + "# from alphabase.protein.lcp_digest import *" ] }, { @@ -15,16 +15,16 @@ "metadata": {}, "outputs": [], "source": [ - "cat_prots = \"$ABCABCD$ABCDE$ABCE$BCDEF$\"\n", - "pos_starts, pos_ends = get_substring_indices(cat_prots, 2, 100)\n", - "substr_set = set()\n", - "for start,end in zip(pos_starts, pos_ends):\n", - " substr_set.add(cat_prots[start:end])\n", - "assert len(substr_set)==len(pos_starts) #not redundant\n", - "for i in range(len(cat_prots)):\n", - " for j in range(i+2,len(cat_prots)):\n", - " if '$' in cat_prots[i:j]: break\n", - " assert cat_prots[i:j] in substr_set, f\"{cat_prots[i:j]} not found\" #not missing" + "# cat_prots = \"$ABCABCD$ABCDE$ABCE$BCDEF$\"\n", + "# pos_starts, pos_ends = get_substring_indices(cat_prots, 2, 100)\n", + "# substr_set = set()\n", + "# for start,end in zip(pos_starts, pos_ends):\n", + "# substr_set.add(cat_prots[start:end])\n", + "# assert len(substr_set)==len(pos_starts) #not redundant\n", + "# for i in range(len(cat_prots)):\n", + "# for j in range(i+2,len(cat_prots)):\n", + "# if '$' in cat_prots[i:j]: break\n", + "# assert cat_prots[i:j] in substr_set, f\"{cat_prots[i:j]} not found\" #not missing" ] }, { diff --git a/nbdev_nbs/spectral_library/decoy_library.ipynb b/nbdev_nbs/spectral_library/decoy_library.ipynb index b0765cfd..46304ebc 100644 --- a/nbdev_nbs/spectral_library/decoy_library.ipynb +++ b/nbdev_nbs/spectral_library/decoy_library.ipynb @@ -36,19 +36,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'pseudo_reverse': alphabase.spectral_library.decoy.SpecLibDecoy,\n", - " 'diann': alphabase.spectral_library.decoy.SpecLibDecoyDiaNN}" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "decoy_lib_provider.decoy_dict" ] @@ -69,111 +57,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sequencemodsmod_sitesnAAcharge
0AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892
1AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892
2AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892
3AGHCEWQMKAADER142
4AGHCEWQMKAADER142
5AGHCEWQMKAADER142
\n", - "
" - ], - "text/plain": [ - " sequence mods \\\n", - "0 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "1 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "2 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "3 AGHCEWQMKAADER \n", - "4 AGHCEWQMKAADER \n", - "5 AGHCEWQMKAADER \n", - "\n", - " mod_sites nAA charge \n", - "0 0;4;8 9 2 \n", - "1 0;4;8 9 2 \n", - "2 0;4;8 9 2 \n", - "3 14 2 \n", - "4 14 2 \n", - "5 14 2 " - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "#| hide\n", "repeat = 3\n", @@ -198,140 +82,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sequencemodsmod_sitesnAAchargeprecursor_mzmod_seq_hashmod_seq_charge_hash
0AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892602.747333-5783464648586361190-5783464648586361188
1AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892602.747333-5783464648586361190-5783464648586361188
2AGHCEWQMKAcetyl@Protein N-term;Carbamidomethyl@C;Oxidat...0;4;892602.747333-5783464648586361190-5783464648586361188
3AGHCEWQMKAADER142816.356299-1606275412423975023-1606275412423975021
4AGHCEWQMKAADER142816.356299-1606275412423975023-1606275412423975021
5AGHCEWQMKAADER142816.356299-1606275412423975023-1606275412423975021
\n", - "
" - ], - "text/plain": [ - " sequence mods \\\n", - "0 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "1 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "2 AGHCEWQMK Acetyl@Protein N-term;Carbamidomethyl@C;Oxidat... \n", - "3 AGHCEWQMKAADER \n", - "4 AGHCEWQMKAADER \n", - "5 AGHCEWQMKAADER \n", - "\n", - " mod_sites nAA charge precursor_mz mod_seq_hash \\\n", - "0 0;4;8 9 2 602.747333 -5783464648586361190 \n", - "1 0;4;8 9 2 602.747333 -5783464648586361190 \n", - "2 0;4;8 9 2 602.747333 -5783464648586361190 \n", - "3 14 2 816.356299 -1606275412423975023 \n", - "4 14 2 816.356299 -1606275412423975023 \n", - "5 14 2 816.356299 -1606275412423975023 \n", - "\n", - " mod_seq_charge_hash \n", - "0 -5783464648586361188 \n", - "1 -5783464648586361188 \n", - "2 -5783464648586361188 \n", - "3 -1606275412423975021 \n", - "4 -1606275412423975021 \n", - "5 -1606275412423975021 " - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "#| hide\n", "target_lib = SpecLibBase(['b_z1','b_z2','y_z1','y_z2'])\n", @@ -351,11 +102,8 @@ "metadata": {}, "outputs": [], "source": [ - "#| hide\n", - "decoy_lib = decoy_lib_provider.get_decoy_lib('pseudo_reverse', target_lib)\n", - "decoy_lib.translate_to_decoy()\n", - "decoy_lib.calc_precursor_mz()\n", - "assert np.allclose(decoy_lib.precursor_df.precursor_mz, target_lib.precursor_df.precursor_mz)" + "decoy_lib = decoy_lib_provider.get_decoy_lib('diann', target_lib.copy())\n", + "decoy_lib.translate_to_decoy()" ] }, { @@ -364,13 +112,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| hide\n", - "decoy_lib = decoy_lib_provider.get_decoy_lib('diann', target_lib, fix_C_term=False)\n", - "decoy_lib.translate_to_decoy()\n", - "if not os.path.isdir('sandbox'):\n", - " os.makedirs('sandbox')\n", - "decoy_lib.save_hdf('sandbox/decoy_lib.hdf')\n", - "assert len(decoy_lib.precursor_df) > 0" + "decoy_lib.precursor_df" ] }, { @@ -380,9 +122,22 @@ "outputs": [], "source": [ "#| hide\n", - "_hdf = HDF_File('sandbox/decoy_lib.hdf')\n", - "assert len(_hdf.library.precursor_df.values) > 0\n", - "assert len(_hdf.library.fragment_mz_df.values) == 0" + "# call once with multiprocessing and once without\n", + "for mp_batch_size in [2, 10000]:\n", + "\n", + " decoy_lib = decoy_lib_provider.get_decoy_lib('pseudo_reverse', target_lib.copy())\n", + " decoy_lib.translate_to_decoy(mp_batch_size=mp_batch_size)\n", + " decoy_lib.calc_precursor_mz()\n", + " assert np.allclose(decoy_lib.precursor_df.precursor_mz, target_lib.precursor_df.precursor_mz)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "decoy_lib.precursor_df" ] }, { @@ -392,8 +147,14 @@ "outputs": [], "source": [ "#| hide\n", - "assert len(_hdf.library.decoy.precursor_df.values) > 0\n", - "assert len(_hdf.library.decoy.fragment_mz_df.values) == 0" + "# call once with multiprocessing and once without\n", + "for mp_batch_size in [2, 10000]:\n", + " decoy_lib = decoy_lib_provider.get_decoy_lib('diann', target_lib)\n", + " decoy_lib.translate_to_decoy(mp_batch_size=mp_batch_size)\n", + " if not os.path.isdir('sandbox'):\n", + " os.makedirs('sandbox')\n", + " decoy_lib.save_hdf('sandbox/decoy_lib.hdf')\n", + " assert len(decoy_lib.precursor_df) > 0" ] }, { @@ -403,9 +164,10 @@ "outputs": [], "source": [ "#| hide\n", - "test_lib = SpecLibDecoy(target_lib)\n", - "test_lib.load_hdf('sandbox/decoy_lib.hdf')\n", - "assert len(test_lib._precursor_df) > 0" + "speclib = SpecLibBase()\n", + "speclib.load_hdf('sandbox/decoy_lib.hdf')\n", + "assert len(speclib.precursor_df.values) > 0\n", + "assert len(speclib.fragment_mz_df.values) == 0" ] }, { diff --git a/nbs_tests/test_isotope_calc.ipynb b/nbs_tests/test_isotope_calc.ipynb new file mode 100644 index 00000000..8dc12724 --- /dev/null +++ b/nbs_tests/test_isotope_calc.ipynb @@ -0,0 +1,150 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n" + ] + }, + { + "data": { + "text/plain": [ + "Text(797.4950942977803, 0.3439403069019318, 'mono')" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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AltbW1OGU69bQ4+VOdd9twTOc+vgPLceznXr4Dzf/UpLS2FnZ9fs7991MenjrTvlWTUpKSmws7OTNi8vL1OGQURERE3Q3MtcbjZu3DgoFAqkpqYalL/55pvo27cvrKysYG9v36h+mxR67pRn1SQmJqKkpETaTp8+bcowiIiIqJFa6iuZAODLL7/Enj174OHhUWtfZWUlhg8fjvHjxze673fEbUAjRozA0KFD0atXL8TExOCbb77B/v37kZmZabS+SqWCra2twUZEREQtr6W+kuns2bOYNGkS1q5di/bt29dq5/XXX8fUqVPRq1evRvfdpNDT1s+qISIiorbTUstc9Ho9Ro0ahWnTpuH+++9vmc7DxNDTls+qISIiorbVUstc3nnnHbRr1w4vvvhi83f6JiYvdddoNIiLi0NISAjCwsKQmppa61k1np6eSElJAXD9WTUDBgzAwoULMXjwYKxfvx4HDhyQnipcVlaG119/HU888QTc3Nxw8uRJvPLKKwbPqiEiIqJ7V1paGg4ePAiFQtGi5zE59LTFs2qIiIio7bXUMpeioiJ4e3tLP1dXV+Oll15Camoq8vLymq3/jXqowcSJE6WnCt/K2OLj4cOHY/jw4UbrW1paYtu2bY3pBhEREbWim5e5xMTEALixzKWuXFCzzGXKlClS2a3LXH755RfY2NhIP0dFRWHUqFHSp0jNhU9yIiIiogZr7mUuAODv729wJ3b79u3h5uaG7t27S2X5+fm4dOkS8vPzUV1djdzcXABA165dDQJTfRh6iIiIqE4t+ZVMpaWlDe5HUlISVq5cKf3cu3dvAMCuXbswcODABrXB0ENEREQmac5lLsYYW8fzySef4JNPPmlwG8bcEQ8nJCIiImppDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLDD1EREQkCww9REREJAsMPURERCQLjQo9S5cu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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "from alphabase.peptide.precursor import (\n", + " calc_precursor_isotope_intensity, calc_precursor_mz\n", + ")\n", + "from alphabase.constants.aa import reset_AA_atoms\n", + "from alphabase.constants.atom import CHEM_INFO_DICT\n", + "CHEM_INFO_DICT['12C'] = CHEM_INFO_DICT['C'] #backup\n", + "CHEM_INFO_DICT['C'] = CHEM_INFO_DICT['13C']\n", + "\n", + "reset_AA_atoms()\n", + "\n", + "precursor_df = pd.DataFrame(dict(\n", + " sequence=[\"ISGLIYEETCISGLIYEETR\"],\n", + " mods=\"\",\n", + " mod_sites=\"\",\n", + " charge=3\n", + "))\n", + "precursor_df = calc_precursor_isotope_intensity(precursor_df)\n", + "precursor_df = calc_precursor_mz(precursor_df)\n", + "mono_idx = precursor_df.mono_isotope_idx[0]\n", + "\n", + "masses = precursor_df.precursor_mz[0]+1.0033*np.arange(-mono_idx, 6-mono_idx)/precursor_df.charge[0]\n", + "import matplotlib.pyplot as plt\n", + "plt.vlines(\n", + " masses, \n", + " np.zeros(6), \n", + " precursor_df[\"i_0,i_1,i_2,i_3,i_4,i_5\".split(',')].values[0,:]\n", + ")\n", + "plt.title(f\"{precursor_df.sequence[0]}({precursor_df.charge[0]}+)\")\n", + "for x, y in zip(\n", + " masses, \n", + " precursor_df[\"i_0,i_1,i_2,i_3,i_4,i_5\".split(',')].values[0,:]\n", + "):\n", + " plt.text(x, y, f\"{y:.3f}\")\n", + "print(mono_idx)\n", + "x = precursor_df.precursor_mz[0]\n", + "y = precursor_df[\"i_\"+str(mono_idx)][0]\n", + "plt.text(x, y+0.02, f\"mono\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3821.8765415544003, 3832.9062415544004)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGzCAYAAAAMr0ziAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7RklEQVR4nO3de1hVZf7//9cGZSMoiJIclMRTHkolj2GamiTWlNqUY06jRo12GLOGvqaWiqWNh8zsYFo5VvaxdGomnco0o3CmJE2tzEOlpeMRPKSikpLw/v3Rj51bNgrKYYHPx3WtS7jXve593/dG9ot7r7W2y8xMAAAADuZX3h0AAAA4FwILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILiuTVV1+Vy+XS9u3by7srlcoXX3yhTp06KTg4WC6XS1999VV5d+mc7rvvPl133XXl3Q2PUaNGqWPHjiXaJmMsGQcPHlRwcLCWLFlSou2WRd83bdqkKlWqaMOGDaX6OCgGA4rglVdeMUm2bdu2Umn/iSeesHfeeadU2naqnJwcq1+/vjVt2tRefPFFe/311+2nn34qtP7EiRPtpptusjp16pgkS0lJ8VnvX//6l/Xs2dOioqIsICDA6tata7fccot98803Puv//PPPNn36dOvQoYOFhISY2+22Jk2a2F/+8hf77rvvvOr++OOPVrVqVfv44489ZTt27LDx48db+/btrWbNmla7dm3r2rWrLV++3OfjHTp0yIYMGWLh4eEWFBRk3bp1s7Vr1/qsu3jxYrvyyivN7XZbTEyMjRs3zn755RevOnv37jW3222LFy8ucLwk+8tf/uKz7fyf6S+++OKcY8zOzrY777zTLr/8cgsJCbHg4GBr1aqVzZgxw3Jycsp9jJJs2rRpFzTG0noehw8fbm3atPHZhpnZp59+an379rU6depYQECA1a9f34YOHWr/+9//fNb31fcz/fe///XMy/79+wvs37Vrl/Xr189CQ0OtRo0a1rt3b/vhhx8K1Ovdu7fdfPPNhT4OyhaBBUVy6tQp+/nnny0vL69U2g8ODrbBgweXSttOtXnzZpNkL7/8cpHqS7LIyEhLTEw8a2B57LHHrH///jZ58mSbM2eOTZw40Ro2bGjVqlWzr776yqvu/v37rW3btibJbrzxRpsxY4bNmTPHRowYYTExMVa1alWv+g888IBddtllXmXPPfecVatWzQYMGGDPP/+8zZgxw9q0aWOSbO7cuV51c3NzrVOnThYcHGzjx4+3559/3lq0aGE1atSw77//3qvukiVLzOVyWffu3e2ll16y+++/3/z8/Oyee+4pMOY//OEP1qVLF59zVtzA4muMBw8etI4dO9qIESNs5syZNmvWLBs4cKC5XC4bMGBAuY9RkkVERNjx48fPe4yl9Txu2rTJJFlqamqBvj/77LPmcrmsUaNGNmHCBJszZ4499NBDFhoaaqGhofbZZ58VOMZX38/sW1xcnAUHB/sMLEePHrUmTZpYnTp1bMqUKTZ9+nSLiYmxevXq2YEDB7zqLlmyxCTZ1q1bC308lB0CCxzhYgwsK1asMEn21ltvFal+/urW/v37zxpYfMnIyLAqVarY3Xff7VX+u9/9zvz8/Oztt98ucMyJEyfsoYce8nyfk5Nj4eHhNmbMGK96GzZsKPCicOLECWvWrJnVq1fPq3zhwoUFxrxv3z6rWbNmgRf+Fi1aWOvWrb1WGx599FFzuVy2efNmr7pvv/22uVyuAn8lFzewFDbGwgwbNswk2d69e8t1jHFxcSbJnnrqqfMeY2k9j2ZmV1xxhQ0cONCr7NNPPzU/Pz/r0qVLgaC1detWi4iIsKioKK9Vx6I8P7NmzbLatWvbAw884DOwTJkyxSTZ6tWrPWWbN282f39/Gz16tFfdnJwcCwsLs7Fjxxb6eCg7BBYUia+3hL744gvr2bOn1a5d2wIDAy02NtaSkpK8jjt27JglJydbvXr1LCAgwC677DJ78sknvVZq8v9CPH07PbysW7fOevXqZTVq1LDg4GC79tprLT093Wf/VqxYYUOHDrVatWpZjRo1bODAgT7fZlmyZIl17tzZgoKCrHr16nbDDTfYhg0bvOrs3bvX7rjjDqtbt64FBARYZGSk9e7du0hvi6WmpnraDw0Ntd69e9umTZs8+wcPHlxgzF27dj1nu2bnF1jy8vIsJCTE+vfv7yn7/PPPTZINGTKkSG18/PHHJsnS0tKKVD85OdkkWVZWlqesX79+FhERYbm5uV51hw4dakFBQXbixAkzM9u4caNJspkzZ3rV2717t0myCRMmeJUfPnzYXC6XTZ8+3au8uIGluGOcNm2aSfIKF+U1xmuvvdYiIiIsOzu7RMd4Ic9jvr/+9a9Ws2ZNr//3iYmJ5u/vbz/++KPPx33ttddMkk2aNKnIfT948KDVrl3bZs6caSkpKT4DS/v27a19+/YFju3Zs6c1atSoQPnNN99srVq18vl4KFucdIvzsm/fPvXs2VPbt2/XqFGj9Nxzz+n222/X559/7qljZurdu7eefvpp9erVS9OnT1fTpk01YsQIJScne+q9/vrrcrvd6tKli15//XW9/vrruvvuuyVJGzduVJcuXfT111/r4Ycf1tixY7Vt2zZ169ZNq1atKtCvYcOGafPmzRo/frwGDRqk+fPnq2/fvjIzr8f73e9+p+rVq2vKlCkaO3asNm3apM6dO3udVHzLLbfonXfeUVJSkl544QUNHz5cR48e1Y4dO846Nx999JESExO1b98+jR8/XsnJyVq5cqWuvvpqT/t33323HnnkEUnS8OHD9frrr+vRRx8t9vNwNocPH9b+/fv1zTff6M9//rOysrLUo0cPz/5///vfkqSBAwcWqb2VK1fK5XLpyiuvLFL9jIwMBQUFKSgoyFP25Zdfqk2bNvLz8/7V06FDB2VnZ+v777/31JOkdu3aedWLjo5WvXr1PPvzhYaGqlGjRvrss88K9OPEiRM6cOBAge3YsWPFHmNOTo4OHDignTt36p133tG0adNUv359NW7cuFzHKEnjx49XZmamZs2a5XN/Ucd4pgt5HvO1bdtWhw8f1saNGyVJ2dnZSk1NVZcuXdSgQQOfj9u/f3+53W699957Re772LFjFRkZ6fn9caa8vDytX7++wJzn9/2HH37Q0aNHC/R9w4YNysrK8tkmylB5JyZUDGeusLzzzjs+3xs/3aJFi0ySTZw40av81ltvNZfL5fW+cGFvCfXt29cCAgK8lsH37NljNWrUsGuuuaZA/9q2bet1EuTUqVNNkudkxaNHj1rNmjULrCpkZGRYaGiop/zQoUMmyZ588slzzExBcXFxVqdOHTt48KCn7OuvvzY/Pz8bNGiQp+yTTz4p1ltC+Yq6wtK0aVPP6k316tVtzJgxXn8R33zzzSbJDh06VKTH/dOf/mS1a9cuUt0tW7ZYYGBggbcBgoOD7c477yxQ//333zdJtnTpUjMze/LJJ02S7dixo0Dd9u3b21VXXVWgvGfPnta8eXOvsvzxn207/Wf4XGN88803vY5t166drV+/vtzHmL+K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Ofn222/N39/fRo4cWaDvN998s7Vs2fKs84GyQWBBkZwZWJ5++mlr0qSJPfzww/biiy/atGnTrGnTphYSEuK5r0Jubq51797dXC6XDR061GbOnGl9+vQxSfbggw96tX/DDTdYcHCwPfXUU/bmm2/a559/bma/3swqODjY6tata0888YRNmTLFGjZsaG6321Pn9P61bNnSunTpYs8995wNGzbM/Pz8rHPnzl6/KOfPn29+fn52xRVX2MSJE+3FF1+0Rx991OLi4jy/+L/88kurVauW3XPPPfbss8/aCy+8YNddd51J8nmTtdMtX77cqlSpYs2aNbMnn3zSHn/8cbvkkkssLCzM654TxQ0s8+bNswkTJtjo0aNNknXv3t0mTJhgEyZMsO3bt3vq1alTxwYMGGBTpkyxl156yUaMGGG1atWywMDAAncO3bdvn8XFxZnL5bLevXvbM888Y3PmzLGRI0da/fr1LSAgwFP35MmTVrt27QI31/rXv/5lkqxJkyb2+uuvF9gyMjI8dU+dOmVXXXWVVa9e3R577DGbOXOmXX755VajRo0CV2e8++675nK57Nprr7WXXnrJhg8fbn5+fj7vG/P222+bfNyRtLiBpbAxPv3009a0aVMbOXKk5+c9/+fhpptu8qrrlDF27drV55VQZf08mv1647g//elPBcqnT59ukqxx48Y2ceJE+/vf/24jRoywmjVrWkhIiH366ade9Qvruy+F3YclKyvLGjVqZHXq1LGpU6fa008/bTExMRYdHe35gyVfTk6O1apVq8g3EkTpIrCgSM4MLOvWrbMBAwbYpZdeam632+rUqWM33nijrVmzxuu4o0eP2l//+leLjo62qlWrWpMmTQrcOM7s179wrrnmGqtWrZrPG8clJiZa9erVLSgoyLp3724rV6702b/8G8eFhYVZ9erV7fbbb/e6vDjfJ598YomJiRYaGmqBgYHWqFEju+OOOzz9P3DggP3lL3+xZs2aWXBwsIWGhlrHjh3tH//4R5Hm66OPPrKrr77aqlWrZiEhIXbTTTcV+Iu5uIHl9BegM7dPPvnEUy8lJcXatWtnYWFhVqVKFYuOjrbbbrutwOW3+bKzs23atGnWvn17q169ugUEBFiTJk3s/vvvL/DiOHz4cGvcuLFXWf4LQ1H6Zvbr6txdd91ltWvXtqCgIOvatWuhl8e/8847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4xFYAACA4xFYAACA4xFYAACA451XYJk5c6ZiY2MVGBiojh07nvXs7Y0bN+qWW25RbGysXC6XZsyYUaDO+PHj5XK5vLZmzZqdT9cAAEAlVOzAsnDhQiUnJyslJUXr1q1T69atlZiY6PnMhjNlZ2erYcOGmjx5siIjIwtt9/LLL9fevXs926efflrcrgEAgEqq2IFl+vTpGjJkiJKSktSiRQvNnj1bQUFBmjt3rs/67du315NPPqnbbrtNbre70HarVKmiyMhIzxYeHl7crgEAgEqqWIElJydHa9euVUJCwm8N+PkpISFB6enpF9SRLVu2KDo6Wg0bNtTtt9+uHTt2FFr35MmTysrK8toAAEDlVazAcuDAAeXm5no++jtfRESEMjIyzrsTHTt21KuvvqqlS5dq1qxZ2rZtm7p06aKjR4/6rD9p0iSFhoZ6ttM/MhwAAFQ+jrhK6Prrr1e/fv3UqlUrJSYmasmSJTp8+LD+8Y9/+Kw/evRoHTlyxLOd/gmcAACg8inWZwmFh4fL399fmZmZXuWZmZlnPaG2uGrWrKnLLrtMW7du9bnf7Xaf9XwYAABQuRRrhSUgIEBt27ZVamqqpywvL0+pqamKj48vsU4dO3ZMP/zwg6KiokqsTQAAUHEV+9Oak5OTNXjwYLVr104dOnTQjBkzdPz4cSUlJUmSBg0apLp162rSpEmSfj1Rd9OmTZ6vd+/era+++krVq1dX48aNJUn/7//9P910002qX7++9uzZo5SUFPn7+2vAgAElNU4AAFCBFTuw9O/fX/v379e4ceOUkZGhuLg4LV261HMi7o4dO+Tn99vCzZ49e3TllVd6vp82bZqmTZumrl27ej4SfNeuXRowYIAOHjyoSy65RJ07d9bnn3+uSy655AKHBwAAKoNiBxZJGjZsmIYNG+ZzX34IyRcbGyszO2t7CxYsOJ9uAACAi4QjrhICAAA4GwILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwPAILAABwvPMKLDNnzlRsbKwCAwPVsWNHrV69utC6Gzdu1C233KLY2Fi5XC7NmDHjgtsEAAAXl2IHloULFyo5OVkpKSlat26dWrdurcTERO3bt89n/ezsbDVs2FCTJ09WZGRkibQJAAAuLsUOLNOnT9eQIUOUlJSkFi1aaPbs2QoKCtLcuXN91m/fvr2efPJJ3XbbbXK73SXSJgAAuLgUK7Dk5ORo7dq1SkhI+K0BPz8lJCQoPT39vDpwPm2ePHlSWVlZXhsAAKi8ihVYDhw4oNzcXEVERHiVR0REKCMj47w6cD5tTpo0SaGhoZ4tJibmvB4bAABUDBXyKqHRo0fryJEjnm3nzp3l3SUAAFCKqhSncnh4uPz9/ZWZmelVnpmZWegJtaXRptvtLvR8GAAAUPkUa4UlICBAbdu2VWpqqqcsLy9Pqampio+PP68OlEabAACgcinWCoskJScna/DgwWrXrp06dOigGTNm6Pjx40pKSpIkDRo0SHXr1tWkSZMk/XpS7aZNmzxf7969W1999ZWqV6+uxo0bF6lNAABwcSt2YOnfv7/279+vcePGKSMjQ3FxcVq6dKnnpNkdO3bIz++3hZs9e/boyiuv9Hw/bdo0TZs2TV27dlVaWlqR2gQAABe3YgcWSRo2bJiGDRvmc19+CMkXGxsrM7ugNgEAwMWtQl4lBAAALi4EFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFsBhsnNOKXbU+4od9b6yc06Vd3cAwBEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPHOK7DMnDlTsbGxCgwMVMeOHbV69eqz1n/rrbfUrFkzBQYGqmXLllqyZInX/jvuuEMul8tr69Wr1/l0DQAAVELFDiwLFy5UcnKyUlJStG7dOrVu3VqJiYnat2+fz/orV67UgAEDdNddd+nLL79U37591bdvX23YsMGrXq9evbR3717P9uabb57fiAAAQKVT7MAyffp0DRkyRElJSWrRooVmz56toKAgzZ0712f9Z555Rr169dKIESPUvHlzTZgwQW3atNHzzz/vVc/tdisyMtKzhYWFnd+IAABApVOswJKTk6O1a9cqISHhtwb8/JSQkKD09HSfx6Snp3vVl6TExMQC9dPS0lSnTh01bdpU9957rw4ePFhoP06ePKmsrCyvDQAAVF7FCiwHDhxQbm6uIiIivMojIiKUkZHh85iMjIxz1u/Vq5fmzZun1NRUTZkyRStWrND111+v3Nxcn21OmjRJoaGhni0mJqY4wwAAABVMlfLugCTddtttnq9btmypVq1aqVGjRkpLS1OPHj0K1B89erSSk5M932dlZRFaAACoxIq1whIeHi5/f39lZmZ6lWdmZioyMtLnMZGRkcWqL0kNGzZUeHi4tm7d6nO/2+1WSEiI1wYAACqvYgWWgIAAtW3bVqmpqZ6yvLw8paamKj4+3ucx8fHxXvUlafny5YXWl6Rdu3bp4MGDioqKKk73AABAJVXsq4SSk5P18ssv67XXXtPmzZt177336vjx40pKSpIkDRo0SKNHj/bUf+CBB7R06VI99dRT+vbbbzV+/HitWbNGw4YNkyQdO3ZMI0aM0Oeff67t27crNTVVffr0UePGjZWYmFhCwwQAABVZsc9h6d+/v/bv369x48YpIyNDcXFxWrp0qefE2h07dsjP77cc1KlTJ73xxhsaM2aMHnnkETVp0kSLFi3SFVdcIUny9/fX+vXr9dprr+nw4cOKjo5Wz549NWHCBLnd7hIaJgAAqMjO66TbYcOGeVZIzpSWllagrF+/furXr5/P+tWqVdOyZcvOpxsAAOAiwWcJAQAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAAAAxyOwAABwmuycU4od9b5iR72v7JxT5d0d/P8ILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILKjUuDwRACoHAgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAHA8AgsAAJVAds4pxY56X7Gj3ld2zqny7k6JI7AAAADHI7AAAADHI7CgyCr7ciMAwLkILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPEILAAAwPHOK7DMnDlTsbGxCgwMVMeOHbV69eqz1n/rrbfUrFkzBQYGqmXLllqyZInXfjPTuHHjFBUVpWrVqikhIUFbtmw5n65VCNyADQCA4il2YFm4cKGSk5OVkpKidevWqXXr1kpMTNS+fft81l+5cqUGDBigu+66S19++aX69u2rvn37asOGDZ46U6dO1bPPPqvZs2dr1apVCg4OVmJiok6cOHH+IwMAAJVGsQPL9OnTNWTIECUlJalFixaaPXu2goKCNHfuXJ/1n3nmGfXq1UsjRoxQ8+bNNWHCBLVp00bPP/+8pF9XV2bMmKExY8aoT58+atWqlebNm6c9e/Zo0aJFPts8efKksrKyvDYAAFB5uczMilo5JydHQUFBevvtt9W3b19P+eDBg3X48GEtXry4wDGXXnqpkpOT9eCDD3rKUlJStGjRIn399df68ccf1ahRI3355ZeKi4vz1Onatavi4uL0zDPPFGhz/PjxeuyxxwqUHzlyRCEhIUUdDgAAKEdZWVkKDQ0t0ut3sVZYDhw4oNzcXEVERHiVR0REKCMjw+cxGRkZZ62f/29x2hw9erSOHDni2Xbu3FmcYQAAgAqmSnl34Hy43W653e7y7gYAACgjxVphCQ8Pl7+/vzIzM73KMzMzFRkZ6fOYyMjIs9bP/7c4bQIAgItLsQJLQECA2rZtq9TUVE9ZXl6eUlNTFR8f7/OY+Ph4r/qStHz5ck/9Bg0aKDIy0qtOVlaWVq1aVWibAADg4lLst4SSk5M1ePBgtWvXTh06dNCMGTN0/PhxJSUlSZIGDRqkunXratKkSZKkBx54QF27dtVTTz2l3/3ud1qwYIHWrFmjl156SZLkcrn04IMPauLEiWrSpIkaNGigsWPHKjo62uvEXgAAcPEqdmDp37+/9u/fr3HjxikjI0NxcXFaunSp56TZHTt2yM/vt4WbTp066Y033tCYMWP0yCOPqEmTJlq0aJGuuOIKT52HH35Yx48f19ChQ3X48GF17txZS5cuVWBgYAkMEQAAVHTFuqzZqYpzWRQAAHCGUrusGQAAoDwQWAAAgOMRWAAAgOMRWAAAgOMRWAAAgOMRWAAAgOMRWAAAgOMRWAAAgONVyE9rPlP+ve+ysrLKuScAAKCo8l+3i3IP20oRWI4ePSpJiomJKeeeAACA4jp69KhCQ0PPWqdS3Jo/Ly9Pe/bsUY0aNeRyuTzlWVlZiomJ0c6dO7llfylinssG81w2mOeywTyXDafPs5np6NGjio6O9vocQl8qxQqLn5+f6tWrV+j+kJAQRz5RlQ3zXDaY57LBPJcN5rlsOHmez7Wyko+TbgEAgOMRWAAAgONV6sDidruVkpIit9td3l2p1JjnssE8lw3muWwwz2WjMs1zpTjpFgAAVG6VeoUFAABUDgQWAADgeAQWAADgeAQWAADgeAQWAADgeI4PLLNmzVKrVq08d+mLj4/XBx984NmfkZGhgQMHKjIyUsHBwWrTpo3++c9/evZv375dd911lxo0aKBq1aqpUaNGSklJUU5OjqdOWlqa+vTpo6ioKAUHBysuLk7z588v03GWp7KY49Nt3bpVNWrUUM2aNUt7aI5SVvNsZpo2bZouu+wyud1u1a1bV0888USZjbO8ldU8L1u2TFdddZVq1KihSy65RLfccou2b99eVsMsdxc6z5LUu3dvXXrppQoMDFRUVJQGDhyoPXv2eNVZv369unTposDAQMXExGjq1KllMj6nKIt5rjCvgeZw//73v+3999+377//3r777jt75JFHrGrVqrZhwwYzM7vuuuusffv2tmrVKvvhhx9swoQJ5ufnZ+vWrTMzsw8++MDuuOMOW7Zsmf3www+2ePFiq1Onjj300EOex3jiiSdszJgx9tlnn9nWrVttxowZ5ufnZ++++265jLmslcUc58vJybF27drZ9ddfb6GhoWU5zHJXVvN8//33W9OmTW3x4sX2448/2po1a+zDDz8s8/GWl7KY5x9//NHcbreNHj3atm7damvXrrVrrrnGrrzyynIZc3m40Hk2M5s+fbqlp6fb9u3b7bPPPrP4+HiLj4/37D9y5IhFRETY7bffbhs2bLA333zTqlWrZi+++GKZj7e8lMU8V5TXQMcHFl/CwsJszpw5ZmYWHBxs8+bN89pfq1Yte/nllws9furUqdagQYOzPsYNN9xgSUlJF97ZCqq05vjhhx+2P/3pT/bKK69cdIHFl5Ke502bNlmVKlXs22+/LZ0OV1AlPc9vvfWWValSxXJzcz1l//73v83lcllOTk4J977iuNB5Xrx4sdccvvDCCxYWFmYnT5701Bk5cqQ1bdq0FHpfcZT0PPvixNdAx78ldLrc3FwtWLBAx48fV3x8vCSpU6dOWrhwoX766Sfl5eVpwYIFOnHihLp161ZoO0eOHFGtWrXO+lhFqVMZleYcf/zxx3rrrbc0c+bM0hxChVBa8/zuu++qYcOGeu+999SgQQPFxsbqz3/+s3766afSHpIjldY8t23bVn5+fnrllVeUm5urI0eO6PXXX1dCQoKqVq1a2sNynJKY559++knz589Xp06dPHOYnp6ua665RgEBAZ56iYmJ+u6773To0KFSH5fTlNY8++LI18DyTkxFsX79egsODjZ/f38LDQ21999/37Pv0KFD1rNnT5NkVapUsZCQEFu2bFmhbW3ZssVCQkLspZdeKrTOwoULLSAgwLPkdjEo7Tk+cOCAxcTE2IoVK8zMLtoVltKe57vvvtvcbrd17NjR/vOf/9gnn3xicXFx1r1791Idl9OUxe+MtLQ0q1Onjvn7+5ski4+Pt0OHDpXWkBypJOb54YcftqCgIJNkV111lR04cMCz77rrrrOhQ4d61d+4caNJsk2bNpXewBymtOf5TE59DawQgeXkyZO2ZcsWW7NmjY0aNcrCw8Nt48aNZmY2bNgw69Chg3300Uf21Vdf2fjx4y00NNTWr19foJ1du3ZZo0aN7K677ir0sT7++GMLCgqy1157rdTG40SlPcc333yzjRw50vP9xRpYSnuehwwZYpLsu+++85StXbvWJF1UbxOV9jzv3bvXmjRpYiNGjLB169bZihUrrGvXrtajRw/Ly8srkzE6QUnM8/79++27776zDz/80K6++mq74YYbPHNIYPlVac/z6Zz8GlghAsuZevToYUOHDrWtW7eapAIpsEePHnb33Xd7le3evduaNGliAwcO9Hrf+XRpaWkWHBx8UZ3QVZiSnuPQ0FDz9/f3bH5+fibJ/P397e9//3upj8epSnqex40bZ1WqVPEqy87ONkkX1Ym3ZyrpeR4zZoy1a9fOq2znzp0mydLT00tnEBXA+czz6fLncOXKlWZmNnDgQOvTp49XnY8//tgk2U8//VTi/a8oSnqe8zn9NbBKmb33VILy8vJ08uRJZWdnS5L8/LxPxfH391deXp7n+927d6t79+5q27atXnnllQL1pV8v67rxxhs1ZcoUDR06tHQHUAGU9Bynp6crNzfX8/3ixYs1ZcoUrVy5UnXr1i3FkThbSc/z1VdfrVOnTumHH35Qo0aNJEnff/+9JKl+/fqlORRHK+l5zs7O9tlG/mNdrIo7z76Ol6STJ09KkuLj4/Xoo4/ql19+8ZxvsXz5cjVt2lRhYWGlMYQKoaTnWaogr4HlnZjOZdSoUbZixQrbtm2brV+/3kaNGmUul8s+/PBDy8nJscaNG1uXLl1s1apVtnXrVps2bZq5XC7Pe3y7du2yxo0bW48ePWzXrl22d+9ez5Yvfwls9OjRXvsPHjxYXsMuU2Uxx2e6GN8SKot5zs3NtTZt2tg111xj69atszVr1ljHjh3tuuuuK69hl7mymOfU1FRzuVz22GOP2ffff29r1661xMREq1+/vmVnZ5fX0MvUhc7z559/bs8995x9+eWXtn37dktNTbVOnTpZo0aN7MSJE2ZmdvjwYYuIiLCBAwfahg0bbMGCBRYUFOTYFYDSUBbzXFFeAx0fWO68806rX7++BQQE2CWXXGI9evTwWtr+/vvv7fe//73VqVPHgoKCrFWrVl6XeL3yyismyeeWb/DgwT73d+3atSyHWm7KYo7PdDEGlrKa5927d9vvf/97q169ukVERNgdd9zhuF88pams5vnNN9+0K6+80oKDg+2SSy6x3r172+bNm8tsnOXtQud5/fr11r17d6tVq5a53W6LjY21e+65x3bt2uX1OF9//bV17tzZ3G631a1b1yZPnlxmY3SCspjnivIa6DIzK+1VHAAAgAtRoe7DAgAALk4EFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4HgEFgAA4Hj/H4n+OYNLn4YBAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from alphabase.constants.isotope import IsotopeDistribution\n", + "from alphabase.constants.atom import calc_mass_from_formula, parse_formula\n", + "\n", + "iso_dist = IsotopeDistribution()\n", + "composition = '13C(200)H(300)N(20)O(40)'\n", + "formula = parse_formula(composition)\n", + "isotope_intens,mono = iso_dist.calc_formula_distribution(formula)\n", + "isotope_masses = calc_mass_from_formula(composition)+np.arange(len(isotope_intens))*1.0033\n", + "\n", + "\n", + "plt.vlines(\n", + " isotope_masses, \n", + " np.zeros(len(isotope_intens)), \n", + " isotope_intens\n", + ")\n", + "plt.title(f\"isotopes of {composition}\")\n", + "\n", + "plt.text(isotope_masses[mono], isotope_intens[mono], 'mono')\n", + "\n", + "plt.xlim(isotope_masses[0]-1, isotope_masses[-1]+1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/release/one_click_linux_gui/control b/release/one_click_linux_gui/control index 949d5008..9c803273 100644 --- a/release/one_click_linux_gui/control +++ b/release/one_click_linux_gui/control @@ -1,5 +1,5 @@ Package: AlphaBase -Version: 1.1.1 +Version: 1.1.2 Architecture: all Maintainer: Mann Labs Description: AlphaBase diff --git a/release/one_click_linux_gui/create_installer_linux.sh b/release/one_click_linux_gui/create_installer_linux.sh index 5ba14151..5c4d8a59 100644 --- a/release/one_click_linux_gui/create_installer_linux.sh +++ b/release/one_click_linux_gui/create_installer_linux.sh @@ -17,7 +17,7 @@ python setup.py sdist bdist_wheel # Setting up the local package cd release/one_click_linux_gui # Make sure you include the required extra packages and always use the stable or very-stable options! -pip install "../../dist/alphabase-1.1.1-py3-none-any.whl[stable]" +pip install "../../dist/alphabase-1.1.2-py3-none-any.whl[stable]" # Creating the stand-alone pyinstaller folder pip install pyinstaller diff --git a/release/one_click_macos_gui/Info.plist b/release/one_click_macos_gui/Info.plist index fa16383b..69dfab28 100644 --- a/release/one_click_macos_gui/Info.plist +++ b/release/one_click_macos_gui/Info.plist @@ -9,9 +9,9 @@ CFBundleIconFile alpha_logo.icns CFBundleIdentifier - alphabase.1.1.1 + alphabase.1.1.2 CFBundleShortVersionString - 1.1.1 + 1.1.2 CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/release/one_click_macos_gui/create_installer_macos.sh b/release/one_click_macos_gui/create_installer_macos.sh index 81c55d34..97e38164 100644 --- a/release/one_click_macos_gui/create_installer_macos.sh +++ b/release/one_click_macos_gui/create_installer_macos.sh @@ -20,7 +20,7 @@ python setup.py sdist bdist_wheel # Setting up the local package cd release/one_click_macos_gui -pip install "../../dist/alphabase-1.1.1-py3-none-any.whl[stable]" +pip install "../../dist/alphabase-1.1.2-py3-none-any.whl[stable]" # Creating the stand-alone pyinstaller folder pip install pyinstaller @@ -40,5 +40,5 @@ cp ../../LICENSE.txt Resources/LICENSE.txt cp ../logos/alpha_logo.png Resources/alpha_logo.png chmod 777 scripts/* -pkgbuild --root dist/alphabase --identifier de.mpg.biochem.alphabase.app --version 1.1.1 --install-location /Applications/AlphaBase.app --scripts scripts AlphaBase.pkg +pkgbuild --root dist/alphabase --identifier de.mpg.biochem.alphabase.app --version 1.1.2 --install-location /Applications/AlphaBase.app --scripts scripts AlphaBase.pkg productbuild --distribution distribution.xml --resources Resources --package-path AlphaBase.pkg dist/alphabase_gui_installer_macos.pkg diff --git a/release/one_click_macos_gui/distribution.xml b/release/one_click_macos_gui/distribution.xml index f5cc449e..870d4d36 100644 --- a/release/one_click_macos_gui/distribution.xml +++ b/release/one_click_macos_gui/distribution.xml @@ -1,6 +1,6 @@ - AlphaBase 1.1.1 + AlphaBase 1.1.2 diff --git a/release/one_click_windows_gui/alphabase_innoinstaller.iss b/release/one_click_windows_gui/alphabase_innoinstaller.iss index 3bc18cf6..d574bc02 100644 --- a/release/one_click_windows_gui/alphabase_innoinstaller.iss +++ b/release/one_click_windows_gui/alphabase_innoinstaller.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "AlphaBase" -#define MyAppVersion "1.1.1" +#define MyAppVersion "1.1.2" #define MyAppPublisher "Max Planck Institute of Biochemistry and the University of Copenhagen, Mann Labs" #define MyAppURL "https://github.com/MannLabs/alphabase" #define MyAppExeName "alphabase_gui.exe" diff --git a/release/one_click_windows_gui/create_installer_windows.sh b/release/one_click_windows_gui/create_installer_windows.sh index 18e2afdb..a02d55fb 100644 --- a/release/one_click_windows_gui/create_installer_windows.sh +++ b/release/one_click_windows_gui/create_installer_windows.sh @@ -17,7 +17,7 @@ python setup.py sdist bdist_wheel # Setting up the local package cd release/one_click_windows_gui # Make sure you include the required extra packages and always use the stable or very-stable options! -pip install "../../dist/alphabase-1.1.1-py3-none-any.whl[stable]" +pip install "../../dist/alphabase-1.1.2-py3-none-any.whl[stable]" # Creating the stand-alone pyinstaller folder pip install pyinstaller diff --git a/settings.ini b/settings.ini index 0f14c20f..f4484ac7 100644 --- a/settings.ini +++ b/settings.ini @@ -4,7 +4,7 @@ ### Python library ### repo = alphabase lib_name = alphabase -version = 1.1.1 +version = 1.1.2 min_python = 3.7 license = apache2