diff --git a/alphabase/constants/const_files/pg_reader.yaml b/alphabase/constants/const_files/pg_reader.yaml index ec0313c9..9d86db1e 100644 --- a/alphabase/constants/const_files/pg_reader.yaml +++ b/alphabase/constants/const_files/pg_reader.yaml @@ -72,3 +72,49 @@ maxquant: "raw": "^Intensity(?!\\s[LHM]\\s).+$" # Raw intensity-based quantification "lfq": "^LFQ intensity(?!\\s[LHM]\\s).+$" # LFQ intensity-based quantification "ibaq": "^iBAQ(?!\\s[LHM]\\s).+$" # Intensity-Based Absolute Quantification + + +# Based on Spectronaut 20.0 Run Pivot Report (Page 80/81) +# https://biognosys.com/content/uploads/2025/06/Spectronaut-20-Manual.pdf +spectronaut: + reader_type: "spectronaut" + column_mapping: + "proteins": ["PG.ProteinNames", "PG_ProteinGroups"] + "genes": "PG.Genes" + "uniprot_ids": "PG.UniProtIds" + measurement_regex: + # Spectronaut marks sample columns with "[number]..." + # At the beginning (^) of the string, match open square bracket "\\[", any number of digits "[0-9]+", closed square bracket "\\]" + # Square brackets are special characters -> need to be escaped with "\[". The backslash itself needs to be escaped -> "\\[" + "default": "^\\[[0-9]+\\]" + + +# https://fragpipe.nesvilab.org/docs/tutorial_fragpipe_outputs.html#proteintsv +fragpipe: + reader_type: "fragpipe" + column_mapping: + "proteins": "Entry Name" + "uniprot_ids": "Protein ID" + "genes": "Gene Names" + "description": "Description" + measurement_regex: + "raw": "Intensity$" + "razor": "Razor Intensity$" + "unique": "Unique Intensity$" + "total": "Total Intensity$" + "lfq": "MaxLFQ Intensity$" + "lfq_unique": "MaxLFQ Unique Intensity$" + "lfq_total": "MaxLFQ Total Intensity$" + + +# mzTab +# version 2.0.0 (2019-03) +mztab: + reader_type: "mztab" + column_mapping: + "uniprot_ids": "accession" + "description": "description" + "source_db": "database" + measurement_regex: + "assay": "^protein_abundance_assay\\[[0-9]+\\]" # The protein's abundance as measured in the given assay through whatever technique was employed + "study_variable": "^protein_abundance_study_variable\\[[0-9]+\\]" # The protein's abundance as measured in the given study variable (condition) through whatever technique was employed diff --git a/alphabase/pg_reader/__init__.py b/alphabase/pg_reader/__init__.py index 4425d89d..c16e3971 100644 --- a/alphabase/pg_reader/__init__.py +++ b/alphabase/pg_reader/__init__.py @@ -1,8 +1,11 @@ from .alphadia_pg_reader import AlphaDiaPGReader from .alphapept_pg_reader import AlphaPeptPGReader from .diann_pg_reader import DiannPGReader +from .fragpipe_pg_reader import FragPipePGReader from .maxquant_pg_reader import MaxQuantPGReader +from .mztab_pg_reader import MZTabPGReader from .pg_reader import pg_reader_provider +from .spectronaut_reader import SpectronautPGReader __all__ = [ "pg_reader_provider", @@ -10,4 +13,7 @@ "DiannPGReader", "AlphaPeptPGReader", "MaxQuantPGReader", + "SpectronautPGReader", + "FragPipePGReader", + "MZTabPGReader", ] diff --git a/alphabase/pg_reader/alphapept_pg_reader.py b/alphabase/pg_reader/alphapept_pg_reader.py index 705cb254..c915e1e7 100644 --- a/alphabase/pg_reader/alphapept_pg_reader.py +++ b/alphabase/pg_reader/alphapept_pg_reader.py @@ -110,7 +110,7 @@ def __init__( Parameters ---------- column_mapping - Dictionary mapping alphabase column names (keys) to AlphaPep column names (values). + Dictionary mapping alphabase column names (keys) to AlphaPept column names (values). If `None`, uses default mapping from configuration file. measurement_regex Pattern to select quantity columns diff --git a/alphabase/pg_reader/fragpipe_pg_reader.py b/alphabase/pg_reader/fragpipe_pg_reader.py new file mode 100644 index 00000000..454a49a3 --- /dev/null +++ b/alphabase/pg_reader/fragpipe_pg_reader.py @@ -0,0 +1,47 @@ +"""FragPipe protein group reader.""" + +from typing import Literal, Optional, Union + +from .pg_reader import PGReaderBase, pg_reader_provider + + +class FragPipePGReader(PGReaderBase): + """Reader for `protein.tsv` reports from FragPipe. + + Example: + ------- + Per default, the reader will return the raw intensities from the `razor` method. Additional protein features are stored + in the dataframe index, samples are stored as columns. + + .. code-block:: python + + # Get raw intensities + reader = FragPipePGReader() + results = reader.import_file(download_path) + + + References: + ---------- + - FragPipe Documentation https://fragpipe.nesvilab.org/docs/tutorial_fragpipe_outputs.html#proteintsv + + """ + + _reader_type: str = "fragpipe" + + def __init__( # noqa: D107 inherited from base class + self, + *, + column_mapping: Optional[dict[str, str]] = None, + measurement_regex: Union[ + Literal[ + "raw", "razor", "unique", "total", "lfq", "lfq_unique", "lfq_total" + ], + None, + ] = "razor", + ): + super().__init__( + column_mapping=column_mapping, measurement_regex=measurement_regex + ) + + +pg_reader_provider.register_reader("fragpipe", reader_class=FragPipePGReader) diff --git a/alphabase/pg_reader/maxquant_pg_reader.py b/alphabase/pg_reader/maxquant_pg_reader.py index c0da66e2..93a7d80e 100644 --- a/alphabase/pg_reader/maxquant_pg_reader.py +++ b/alphabase/pg_reader/maxquant_pg_reader.py @@ -46,16 +46,7 @@ class MaxQuantPGReader(PGReaderBase): results.columns > Index([...], dtype='object', length=312) - - To read the LFQ values, pass the pre-configured key `lfq` to the reader, which represents a regular expression - that automatically extracts the `LFQ` columns from the protein group table. - - .. code-block:: python - - # Get raw intensities - reader = MaxQuantPGReader(measurement_regex="lfq") - results = reader.import_file(download_path) - + You can get other intensity types by passing a specific pattern to the `measurment_regex` parameter during class initialization. To checkout all preconfigured regular expressions that enable you to retrieve different intensity modalities, use the `get_preconfigured_regex` method: @@ -76,14 +67,6 @@ class MaxQuantPGReader(PGReaderBase): reader = MaxQuantPGReader(measurement_regex="^Intensity H .+") - If desired, remove the test data - - .. code-block:: python - - # Clean up - os.rmdir(download_dir) - - References ---------- - MaxQuant Documentation (Cox Lab, 2024-06-27): https://cox-labs.github.io/coxdocs/output_tables.html#protein-groups, @@ -93,12 +76,30 @@ class MaxQuantPGReader(PGReaderBase): _reader_type = "maxquant" - def __init__( # noqa: D107 inherited from base class + def __init__( self, *, column_mapping: Optional[dict[str, str]] = None, - measurement_regex: Union[str, Literal["raw", "lfq", "ibaq"], None] = "raw", # noqa: PYI051 raw and lfq are special casees and not equivalent to string + measurement_regex: Union[str, Literal["raw", "lfq", "ibaq"], None] = "raw", # noqa: PYI051 raw and lfq are special cases and not equivalent to string ): + """Initialize MaxQuant protein group matrix reader. + + Parameters + ---------- + column_mapping + Dictionary mapping alphabase column names (keys) to MaxQuant column names (values). + If `None`, uses default mapping from configuration file. + measurement_regex + Pattern to select quantity columns + + - "raw" (default): Raw intensities + - "lfq": LFQ-corrected intensities + - "ibaq": Intensity-Based Absolute Quantification-corrected intensities + - custom: Any valid regular expression + + See class documentation for usage examples and `get_preconfigured_regex()` for available patterns. + + """ super().__init__( column_mapping=column_mapping, measurement_regex=measurement_regex ) diff --git a/alphabase/pg_reader/mztab_pg_reader.py b/alphabase/pg_reader/mztab_pg_reader.py new file mode 100644 index 00000000..3219c25d --- /dev/null +++ b/alphabase/pg_reader/mztab_pg_reader.py @@ -0,0 +1,116 @@ +"""FragPipe protein group reader.""" + +from pathlib import Path +from typing import Literal, Optional, Union + +import pandas as pd + +from .pg_reader import PGReaderBase, pg_reader_provider + + +class MZTabPGReader(PGReaderBase): + """Reader for MZTab search engine output. + + MZTab is a standardized tab-delimited format for reporting proteomics and metabolomics results. + The format organizes data into distinct sections: metadata (MTD), protein groups (PRH/PRT), + peptides (PEH/PEP), PSMs (PSH/PSM), and small molecules (SMH/SML), with each section identified + by specific three-letter prefixes. This reader extracts protein-level quantification data from + the PRT lines, which contain protein abundances across samples or study variables. + + Example: + ------- + Per default, the reader will return the raw intensities from the `razor` method. Additional protein features are stored + in the dataframe index, samples are stored as columns. + + .. code-block:: python + + from alphabase.pg_reader import MZTabPGReader + + # Get raw intensities + reader = MZTabPGReader() + results = reader.import_file(path) + + + References: + ---------- + - Griss, J. et al. The mzTab Data Exchange Format: Communicating Mass-spectrometry-based Proteomics and Metabolomics Experimental Results to a Wider Audience*. Molecular & Cellular Proteomics 13, 2765-2775 (2014). + - Official MZTab Repository: https://github.com/HUPO-PSI/mzTab.git + - Official documentation: https://hupo-psi.github.io/mzTab/ + + """ + + _reader_type: str = "mztab" + + _PROTEIN_ROW_INDICATOR: str = "PRT" + _PROTEIN_HEADER_INDICATOR: str = "PRH" + _SEPARATOR: str = "\t" + + def __init__( # noqa: D107 inherited from base class + self, + *, + column_mapping: Optional[dict[str, str]] = None, + measurement_regex: Union[ + str, Literal["assay", "study_variable"], None # noqa: PYI051 raw and lfq are special cases and not equivalent to string + ] = "assay", + ): + super().__init__( + column_mapping=column_mapping, measurement_regex=measurement_regex + ) + + def _load_file(self, file_path: str) -> pd.DataFrame: + """Load MZTab file and extract protein data section. + + Parameters + ---------- + file_path : str + Path to MZTab file + + Returns + ------- + pd.DataFrame + DataFrame containing protein data from MZTab file + + Notes + ----- + Protein lines are indicated with a leading `PRT`. The protein metadata header is + indicated with a leading `PRH`. The file is tab separated. + + Raises + ------ + ValueError + If no protein data or metadata is found in the file + + """ + file_path = Path(file_path) + protein_header = None + protein_rows = [] + + with file_path.open() as f: + for line in f: + line_stripped = line.strip() + + if line_stripped.startswith(self._PROTEIN_HEADER_INDICATOR): + # Protein header line - remove 'PRH' prefix and parse columns + header_content = line_stripped[3:].strip() + protein_header = header_content.split(self._SEPARATOR) + + elif line_stripped.startswith(self._PROTEIN_ROW_INDICATOR): + # Protein data line - remove 'PRT' prefix and parse data + row_content = line_stripped[3:].strip() + protein_rows.append(row_content.split(self._SEPARATOR)) + + # Validate that we found protein data + if protein_header is None: + raise ValueError( + f"No protein header ({self._PROTEIN_HEADER_INDICATOR}) found in MZTab file" + ) + + if not protein_rows: + raise ValueError( + f"No protein data rows ({self._PROTEIN_ROW_INDICATOR}) found in MZTab file" + ) + + return pd.DataFrame(protein_rows, columns=protein_header) + + +pg_reader_provider.register_reader("mztab", reader_class=MZTabPGReader) diff --git a/alphabase/pg_reader/pg_reader.py b/alphabase/pg_reader/pg_reader.py index 4d3ee86f..a09eb1f5 100644 --- a/alphabase/pg_reader/pg_reader.py +++ b/alphabase/pg_reader/pg_reader.py @@ -168,18 +168,20 @@ def _load_file(self, file_path: str) -> pd.DataFrame: """ if Path(file_path).suffix == ".hdf": return pd.read_hdf(file_path) + if Path(file_path).suffix == ".parquet": + return pd.read_parquet(file_path) sep = _get_delimiter(file_path) return pd.read_csv(file_path, sep=sep, keep_default_na=False) def _pre_process(self, df: pd.DataFrame) -> pd.DataFrame: - """Preprocess dataframe before standardizing columns and return an updated copy.""" + """Preprocess dataframe before standardizing columns.""" return df def _translate_columns( self, df: pd.DataFrame, column_mapping: dict[str, str] ) -> pd.DataFrame: - """Translate standardized columns in dataframe from other search engines to AlphaBase format and return an updated copy.""" + """Translate standardized columns in dataframe from other search engines to AlphaBase format.""" return df.rename(columns=column_mapping) def _filter_measurement( @@ -188,7 +190,7 @@ def _filter_measurement( regex: str, extra_columns: Optional[Iterable[str]] = None, ) -> pd.DataFrame: - """Subset :class:`pd.DataFrame` to columns matching a regex plus optionally extra columns and return an updated copy. + """Subset :class:`pd.DataFrame` to columns matching a regex plus optionally extra columns. Parameters ---------- @@ -217,13 +219,13 @@ def _filter_measurement( return df[regex_columns + extra_columns] def _post_process(self, df: pd.DataFrame) -> pd.DataFrame: - """Process dataframe after standardizing columns and return an updated copy.""" + """Process dataframe after standardizing columns.""" return df def _get_measurement_regex(self, regex: Optional[str]) -> Union[str, None]: """Get the correct named measurement regex from the reader configuration. - The function tries to match the provided `regex` to the keys in `measurement_regex`. This + The function tries to match the provided `regex` to the keys in `measurement_regex` in the reader configuration. This enables users to provide tangible names for the columns they want instead of abstract regular expressions. If a match is found, it returns the associated value (the actual regex). If this not possible, the function assumes that a regular expression was passed and diff --git a/alphabase/pg_reader/spectronaut_reader.py b/alphabase/pg_reader/spectronaut_reader.py new file mode 100644 index 00000000..bdee9088 --- /dev/null +++ b/alphabase/pg_reader/spectronaut_reader.py @@ -0,0 +1,108 @@ +"""Spectronaut Protein Group Reader.""" + +import re +from typing import Any, Literal, Optional, Union + +import numpy as np +import pandas as pd + +from .pg_reader import PGReaderBase, pg_reader_provider + + +class SpectronautPGReader(PGReaderBase): + """Reader for pivot reports from the Spectronaut search engine. + + Examples + -------- + Get example data + + .. code-block:: python + + import os + import tempfile + from alphabase.tools.data_downloader import DataShareDownloader + from alphabase.pg_reader import SpectronautPGReader + + + # Download to temporary directory + # Data provided by Pham et al, 2024 (https://doi.org/10.1093/bioinformatics/btae432), + # originally generated by Bekker-Jensen et al, 2020 (https://doi.org/10.1038/s41467-020-14609-1) + URL = "https://datashare.biochem.mpg.de/s/ot008eF6wwSISvk" + download_dir = tempfile.mkdtemp() + + download_path = DataShareDownloader(url=URL, output_dir=download_dir).download() + + + The reader will return the values that are stored in the spectronaut report. Note that the nature of + these values depends on the selection made by the user during the data export in Spectronaut and might + represent Protein Quantities or Peptide Quantities. + + .. code-block:: python + + reader = SpectronautPGReader() + results = reader.import_file(download_path) + results.index.names + > FrozenList(['proteins', 'genes']) + + Spectronaut allows users to export a multitude of feature-level metadata. You can retrieve additional + columns from the report by using the `add_column_mapping` method + + .. code-block:: python + + reader = SpectronautPGReader() + reader.add_column_mapping({"organism": "PG.Organisms"}) + results = reader.import_file(spectronaut_path) + results.index.names + > FrozenList(['proteins', 'genes', 'organism']) + + """ + + _reader_type: str = "spectronaut" + + _to_nan_values: tuple[Any] = ("Filtered",) + + def __init__( + self, + *, + column_mapping: Optional[dict[str, str]] = None, + measurement_regex: Union[str, Literal["default"], None] = "default", # noqa: PYI051 default are special cases and not equivalent to string + ): + """Initialize Spectronaut protein group matrix reader. + + Parameters + ---------- + column_mapping + Dictionary mapping alphabase column names (keys) to Spectronaut column names (values). + If `None`, uses default mapping from configuration file. + measurement_regex + Pattern to select quantity columns + + - "default" (default): Intensities exported by the user in the spectronaut report. Might depend on the specific report. + - custom: Any valid regular expression + + See class documentation for usage examples and `get_preconfigured_regex()` for available patterns. + + """ + super().__init__( + column_mapping=column_mapping, measurement_regex=measurement_regex + ) + + def _post_process(self, df: pd.DataFrame) -> pd.DataFrame: + """Process Spectronaut protein group table after standardization. + + Notes + ----- + Spectronaut reports might contain "Filtered" as values. Replace these values with NAN + and assure that floating point values are returned + + """ + # Only modify the intensity columns, as defined by the `measurement_regex` + pattern = re.compile(self.measurement_regex) + regex_columns = [col for col in df.columns if re.search(pattern, col)] + df[regex_columns] = df[regex_columns].replace(self._to_nan_values, np.nan) + df[regex_columns] = df[regex_columns].astype(float) + + return df + + +pg_reader_provider.register_reader("spectronaut", reader_class=SpectronautPGReader) diff --git a/docs/api.rst b/docs/api.rst index 7a4c8218..72a33557 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -36,6 +36,7 @@ Reader :maxdepth: 2 modules_psm_reader + modules_pg_reader I/O diff --git a/docs/modules_pg_reader.rst b/docs/modules_pg_reader.rst new file mode 100644 index 00000000..cc27886d --- /dev/null +++ b/docs/modules_pg_reader.rst @@ -0,0 +1,17 @@ +alphabase.pg_reader +=========================== + +All pg_readers can be accessed by +:obj:`pg_reader_provider `. + +.. toctree:: + :maxdepth: 1 + + pg_reader/pg_base + pg_reader/alphadia_pg_reader + pg_reader/alphapept_pg_reader + pg_reader/diann_pg_reader + pg_reader/fragpipe_pg_reader + pg_reader/maxquant_pg_reader + pg_reader/mztab_pg_reader + pg_reader/spectronaut_pg_reader diff --git a/docs/pg_reader/alphadia_pg_reader.rst b/docs/pg_reader/alphadia_pg_reader.rst new file mode 100644 index 00000000..c4c79426 --- /dev/null +++ b/docs/pg_reader/alphadia_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.alphadia_pg_reader +====================================== + +.. automodule:: alphabase.pg_reader.alphadia_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/alphapept_pg_reader.rst b/docs/pg_reader/alphapept_pg_reader.rst new file mode 100644 index 00000000..e1d08593 --- /dev/null +++ b/docs/pg_reader/alphapept_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.alphapept_pg_reader +======================================= + +.. automodule:: alphabase.pg_reader.alphapept_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/diann_pg_reader.rst b/docs/pg_reader/diann_pg_reader.rst new file mode 100644 index 00000000..15a7d0e8 --- /dev/null +++ b/docs/pg_reader/diann_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.diann_pg_reader +=================================== + +.. automodule:: alphabase.pg_reader.diann_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/fragpipe_pg_reader.rst b/docs/pg_reader/fragpipe_pg_reader.rst new file mode 100644 index 00000000..fff83518 --- /dev/null +++ b/docs/pg_reader/fragpipe_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.fragpipe_pg_reader +====================================== + +.. automodule:: alphabase.pg_reader.fragpipe_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/maxquant_pg_reader.rst b/docs/pg_reader/maxquant_pg_reader.rst new file mode 100644 index 00000000..5744f171 --- /dev/null +++ b/docs/pg_reader/maxquant_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.maxquant_pg_reader +====================================== + +.. automodule:: alphabase.pg_reader.maxquant_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/mztab_pg_reader.rst b/docs/pg_reader/mztab_pg_reader.rst new file mode 100644 index 00000000..69475764 --- /dev/null +++ b/docs/pg_reader/mztab_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.mztab_pg_reader +====================================== + +.. automodule:: alphabase.pg_reader.mztab_pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/pg_base.rst b/docs/pg_reader/pg_base.rst new file mode 100644 index 00000000..a18e1710 --- /dev/null +++ b/docs/pg_reader/pg_base.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.base +======================== + +.. automodule:: alphabase.pg_reader.pg_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/pg_reader/spectronaut_pg_reader.rst b/docs/pg_reader/spectronaut_pg_reader.rst new file mode 100644 index 00000000..5bc39fca --- /dev/null +++ b/docs/pg_reader/spectronaut_pg_reader.rst @@ -0,0 +1,7 @@ +alphabase.pg_reader.spectronaut_reader +====================================== + +.. automodule:: alphabase.pg_reader.spectronaut_reader + :members: + :undoc-members: + :show-inheritance: diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 4f6880ce..ce29948e 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -162,3 +162,88 @@ def example_maxquant_tsv(tmp_path) -> tuple[Path, pd.DataFrame]: reference = get_local_reference_data(test_case_name=TEST_FILE_NAME) return file_path, reference + + +@pytest.fixture(scope="function") +def example_spectronaut_tsv(tmp_path) -> Path: + """Get and parse real spectronaut protein group report matrix (pivot report).""" + TEST_FILE_NAME = "pg_spectronaut_18.0.tsv" + TEST_DATA = """PG.Genes PG.Organisms PG.ProteinNames PTM.CollapseKey PTM.FlankingRegion PTM.ModificationTitle PTM.Multiplicity PTM.ProteinId PTM.SiteAA PTM.SiteLocation [1] 20180815_QE3_nLC3_AH_DIA_Honly_ind_01.raw.PTM.Quantity [2] 20180815_QE3_nLC3_AH_DIA_Honly_ind_02.raw.PTM.Quantity [3] 20180815_QE3_nLC3_AH_DIA_Honly_ind_03.raw.PTM.Quantity [4] 20180815_QE3_nLC3_AH_DIA_Yonly_ind_01.raw.PTM.Quantity [5] 20180815_QE3_nLC3_AH_DIA_Yonly_ind_02.raw.PTM.Quantity [6] 20180815_QE3_nLC3_AH_DIA_Yonly_ind_03.raw.PTM.Quantity [7] 20180816_QE3_nLC3_AH_DIA_H100_Y100_01.raw.PTM.Quantity [8] 20180816_QE3_nLC3_AH_DIA_H100_Y100_02.raw.PTM.Quantity [9] 20180816_QE3_nLC3_AH_DIA_H100_Y100_03.raw.PTM.Quantity [10] 20180816_QE3_nLC3_AH_DIA_H100_Y100_04.raw.PTM.Quantity [11] 20180816_QE3_nLC3_AH_DIA_H100_Y100_05.raw.PTM.Quantity [12] 20180816_QE3_nLC3_AH_DIA_H100_Y100_06.raw.PTM.Quantity [13] 20180816_QE3_nLC3_AH_DIA_H100_Y150_01.raw.PTM.Quantity [14] 20180816_QE3_nLC3_AH_DIA_H100_Y150_02.raw.PTM.Quantity [15] 20180816_QE3_nLC3_AH_DIA_H100_Y150_03.raw.PTM.Quantity [16] 20180816_QE3_nLC3_AH_DIA_H100_Y150_04.raw.PTM.Quantity [17] 20180816_QE3_nLC3_AH_DIA_H100_Y150_05.raw.PTM.Quantity [18] 20180816_QE3_nLC3_AH_DIA_H100_Y150_06.raw.PTM.Quantity [19] 20180816_QE3_nLC3_AH_DIA_H100_Y200_01.raw.PTM.Quantity [20] 20180816_QE3_nLC3_AH_DIA_H100_Y200_02.raw.PTM.Quantity [21] 20180816_QE3_nLC3_AH_DIA_H100_Y200_03.raw.PTM.Quantity [22] 20180816_QE3_nLC3_AH_DIA_H100_Y200_04.raw.PTM.Quantity [23] 20180816_QE3_nLC3_AH_DIA_H100_Y200_05.raw.PTM.Quantity [24] 20180816_QE3_nLC3_AH_DIA_H100_Y200_06.raw.PTM.Quantity [25] 20180816_QE3_nLC3_AH_DIA_H100_Y25_01.raw.PTM.Quantity [26] 20180816_QE3_nLC3_AH_DIA_H100_Y25_02.raw.PTM.Quantity [27] 20180816_QE3_nLC3_AH_DIA_H100_Y25_03.raw.PTM.Quantity [28] 20180816_QE3_nLC3_AH_DIA_H100_Y25_04.raw.PTM.Quantity [29] 20180816_QE3_nLC3_AH_DIA_H100_Y25_05.raw.PTM.Quantity [30] 20180816_QE3_nLC3_AH_DIA_H100_Y25_06.raw.PTM.Quantity [31] 20180816_QE3_nLC3_AH_DIA_H100_Y50_01.raw.PTM.Quantity [32] 20180816_QE3_nLC3_AH_DIA_H100_Y50_02.raw.PTM.Quantity [33] 20180816_QE3_nLC3_AH_DIA_H100_Y50_03.raw.PTM.Quantity [34] 20180816_QE3_nLC3_AH_DIA_H100_Y50_04.raw.PTM.Quantity [35] 20180816_QE3_nLC3_AH_DIA_H100_Y50_05.raw.PTM.Quantity [36] 20180816_QE3_nLC3_AH_DIA_H100_Y50_06.raw.PTM.Quantity +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN A0A075B6N1_S86_M3 IAEGYSVSREKKESF Phospho (STY) 3 A0A075B6N1 S 86 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN A0A075B6N1_S84_M3 GDIAEGYSVSREKKE Phospho (STY) 3 A0A075B6N1 S 84 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN A0A075B6N1_Y83_M3 KGDIAEGYSVSREKK Phospho (STY) 3 A0A075B6N1 Y 83 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN P0DSE2_S86_M3 IAEGYSVSREKKESF Phospho (STY) 3 P0DSE2 S 86 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN P0DSE2_S84_M3 GDIAEGYSVSREKKE Phospho (STY) 3 P0DSE2 S 84 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +TRBV19;TRB Homo sapiens TVB19_HUMAN;TRBR1_HUMAN P0DSE2_Y83_M3 KGDIAEGYSVSREKK Phospho (STY) 3 P0DSE2 Y 83 Filtered Filtered Filtered Filtered Filtered Filtered 89374.65625 Filtered 90181.578125 96197.0703125 89868.4375 88778.9453125 75351.0234375 80948.8515625 80663.4296875 84789.296875 79565.1875 76369.9921875 76147.9609375 75591.015625 75025.4609375 73621.09375 70005.9140625 71209.375 111330.484375 89729.921875 69968.8359375 103632.6015625 90488.9296875 113429.859375 96970.2734375 61069.171875 99673.2734375 109199.875 112307.4765625 112374.84375 +RAMACL;RAMAC Homo sapiens RMACL_HUMAN;RAMAC_HUMAN A0A3B3IU46_S36_M1 YLKRPPESPPIVEEW Phospho (STY) 1 A0A3B3IU46 S 36 Filtered Filtered Filtered Filtered Filtered Filtered 1200.7196044921875 5300.68896484375 4638.056640625 4712.28076171875 3450.970703125 5380.7333984375 7731.48486328125 6493.57861328125 5718.142578125 6750.22802734375 6919.2783203125 6276.05029296875 5876.96875 8595.048828125 4630.2802734375 3050.944580078125 Filtered 7158.28369140625 Filtered 4104.6337890625 4239.0849609375 3748.393798828125 2717.6083984375 5581.71435546875 1382.2950439453125 7370.5205078125 5632.4931640625 5181.73046875 4633.93701171875 4836.95556640625 +RAMACL;RAMAC Homo sapiens RMACL_HUMAN;RAMAC_HUMAN Q9BTL3_S36_M1 YLKRPPESPPIVEEW Phospho (STY) 1 Q9BTL3 S 36 Filtered Filtered Filtered Filtered Filtered Filtered 1200.7196044921875 5300.68896484375 4638.056640625 4712.28076171875 3450.970703125 5380.7333984375 7731.48486328125 6493.57861328125 5718.142578125 6750.22802734375 6919.2783203125 6276.05029296875 5876.96875 8595.048828125 4630.2802734375 3050.944580078125 Filtered 7158.28369140625 Filtered 4104.6337890625 4239.0849609375 3748.393798828125 2717.6083984375 5581.71435546875 1382.2950439453125 7370.5205078125 5632.4931640625 5181.73046875 4633.93701171875 4836.95556640625 +""" + file_path = write_test_data( + data=TEST_DATA, directory=tmp_path, test_case_name=TEST_FILE_NAME + ) + reference = get_local_reference_data(test_case_name=TEST_FILE_NAME) + + return file_path, reference + + +@pytest.fixture(scope="function") +def example_spectronaut_parquet(tmp_path) -> Path: + """Get and parse real spectronaut protein group report matrix (pivot report) in parquet format.""" + URL = "https://datashare.biochem.mpg.de/s/W5ZgzVymP2qDSca" + REF_URL = "https://datashare.biochem.mpg.de/s/nhxU8NZXQt35BWw" + + return get_remote_data_with_ref(url=URL, ref_url=REF_URL, directory=tmp_path) + + +@pytest.fixture(scope="function") +def example_fragpipe_tsv(tmp_path) -> Path: + """Get and parse real FragPipe protein group report matrix (protein.tsv).""" + TEST_FILE_NAME = "pg_fragpipe" + TEST_DATA = """Protein Group SubGroup Protein Protein ID Entry Name Gene Names Protein Length Coverage Organism Protein Existence Description Protein Probability Top Peptide Probability Unique Stripped Peptides Summarized Total Spectral Count Summarized Unique Spectral Count S1 Razor Intensity S2 Razor Intensity S3 Razor Intensity S4 Razor Intensity S5 Razor Intensity S6 Razor Intensity S7 Razor Intensity S8 Razor Intensity S9 Razor Intensity S10 Razor Intensity S11 Razor Intensity S12 Razor Intensity S13 Razor Intensity S14 Razor Intensity S15 Razor Intensity S16 Razor Intensity S17 Razor Intensity S18 Razor Intensity S19 Razor Intensity S20 Razor Intensity +679 a sp|P02790|HEMO_HUMAN P02790 HEMO_HUMAN HPX 462 82.9 Homo sapiens OX=9606 1:Experimental evidence at protein level Hemopexin 1.0 0.9990000000000001 95 25026 25025 2216637.5 2295583.8 1240315.4 106460.28 1019385.2 2596973.0 3091005.2 2327599.5 2323380.0 3109355.8 2113776.8 2301295.2 2451093.5 142603.97 946154.75 3126271.8 2970801.5 2399545.8 3020956.8 3691187.2 +680 a sp|P02792|FRIL_HUMAN P02792 FRIL_HUMAN FTL 175 40.6 Homo sapiens OX=9606 1:Experimental evidence at protein level Ferritin light chain 1.0 0.9990000000000001 18 69 67 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 +681 a sp|P02794|FRIH_HUMAN P02794 FRIH_HUMAN FTH1 183 53.6 Homo sapiens OX=9606 1:Experimental evidence at protein level Ferritin heavy chain 1.0 0.9990000000000001 15 15 15 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 +682 a sp|P03951|FA11_HUMAN P03951 FA11_HUMAN F11 625 20.2 Homo sapiens OX=9606 1:Experimental evidence at protein level Coagulation factor XI 1.0 0.9990000000000001 11 18 18 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 +683 a sp|P03952|KLKB1_HUMAN P03952 KLKB1_HUMAN KLKB1 638 41.7 Homo sapiens OX=9606 1:Experimental evidence at protein level Plasma kallikrein 1.0 0.9990000000000001 23 1022 1022 0.0 85066.71 0.0 0.0 74640.38 118894.164 111398.06 59677.086 45627.31200000001 36386.727 38690.133 70755.9 70384.055 102087.5 106722.82 102985.125 99397.76 45197.56 54068.883 45319.242 +684 a sp|P04003|C4BPA_HUMAN P04003 C4BPA_HUMAN C4BPA 597 40.2 Homo sapiens OX=9606 1:Experimental evidence at protein level C4b-binding protein alpha chain 1.0 0.9990000000000001 26 1645 1645 0.0 0.0 0.0 0.0 0.0 112257.234 30634.523 112197.33 107021.34 95892.05 100655.766 77396.234 78481.19 0.0 0.0 0.0 55184.43 25498.191000000006 43999.35 32183.307 +685 a sp|P04004|VTNC_HUMAN P04004 VTNC_HUMAN VTN 478 51.0 Homo sapiens OX=9606 1:Experimental evidence at protein level Vitronectin 1.0 0.9990000000000001 41 10829 10812 426109.1 531158.4 280231.38 972440.7 925719.9 1446606.2 841194.25 850832.94 911400.2 461015.6 349032.28 877507.94 1113970.9 980389.94 1374961.0 1188514.5 870155.6 1299377.1 1360895.5 614073.56 +686 a sp|P04040|CATA_HUMAN P04040 CATA_HUMAN CAT 527 43.8 Homo sapiens 1:Experimental evidence at protein level Catalase 1.0 0.9990000000000001 18 23 23 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 +687 a sp|P04070|PROC_HUMAN P04070 PROC_HUMAN PROC 461 43.4 Homo sapiens OX=9606 1:Experimental evidence at protein level Vitamin K-dependent protein C 1.0 0.9990000000000001 15 65 65 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 39016.97 0.0 0.0 0.0 +688 a sp|P04114|APOB_HUMAN P04114 APOB_HUMAN APOB 4563 75.5 Homo sapiens OX=9606 1:Experimental evidence at protein level Apolipoprotein B-100 1.0 0.9990000000000001 573 103725 103716 603013.44 923688.06 591751.25 491850.03 397211.94 506224.94 319749.8 263752.28 390402.94 443494.1 498007.12 230024.48 265819.75 687710.9 762252.1 422017.75 1020169.75 196046.12 318726.28 480642.72 +""" + + file_path = write_test_data( + data=TEST_DATA, directory=tmp_path, test_case_name=TEST_FILE_NAME + ) + reference = get_local_reference_data(test_case_name=TEST_FILE_NAME) + + return file_path, reference + + +@pytest.fixture(scope="function") +def example_mztab(tmp_path) -> Path: + """Get and parse real MZTab report""" + URL = "https://datashare.biochem.mpg.de/s/ayieQHU9zjY89cl" + REF_URL = "https://datashare.biochem.mpg.de/s/o7K2FEAmpmLUglS" + + return get_remote_data_with_ref(url=URL, ref_url=REF_URL, directory=tmp_path) + + +@pytest.fixture(scope="function") +def example_mztab_minimal(tmp_path) -> Path: + """Get and parse minimal MZTab report for local testing""" + TEST_FILE_NAME = "pg_mztab_minimal" + TEST_DATA = """COM Only variable modifications can be reported when the original source is a PRIDE XML file + +PRH accession description taxid species database database_version search_engine best_search_engine_score[1] search_engine_score[1]_ms_run[1] num_psms_ms_run[1] num_peptides_distinct_ms_run[1] num_peptides_unique_ms_run[1] ambiguity_members modifications protein_coverage protein_abundance_assay[1] protein_abundance_assay[2] protein_abundance_assay[3] protein_abundance_assay[4] +PRT 223462890 Spna2 protein [Mus musculus] 10090 Mus musculus (Mouse) NCBInr_2010_10 nr_101020.fasta [MS, MS:1001207, Mascot, ] 6539.67 6539.67 157 92 null null null 0 1 0.853 0.864 0.791 +PRT 19855078 RecName: Full=Sodium/potassium-transporting ATPase subunit alpha-3; Short=Na(+)/K(+) ATPase alpha-3 subunit; AltName: Full=Na(+)/K(+) ATPase alpha(III) subunit; AltName: Full=Sodium pump subunit alpha-3 10090 Mus musculus (Mouse) NCBInr_2010_10 nr_101020.fasta [MS, MS:1001207, Mascot, ] 6331.91 6331.91 144 49 null null 32-MOD:00425,525-MOD:00425,606-MOD:00425,725-MOD:00425,739-MOD:00425,940-MOD:00425 0 null null null null +PRT 21450277 sodium/potassium-transporting ATPase subunit alpha-1 precursor [Mus musculus] 10090 Mus musculus (Mouse) NCBInr_2010_10 nr_101020.fasta [MS, MS:1001207, Mascot, ] 4577.11 4577.11 112 39 null null 42-MOD:00425,616-MOD:00425,749-MOD:00425,950-MOD:00425 0 1 0.776 0.819 0.687 +PRT 6978545 sodium/potassium-transporting ATPase subunit alpha-2 precursor [Rattus norvegicus] 10090 Mus musculus (Mouse) NCBInr_2010_10 nr_101020.fasta [MS, MS:1001207, Mascot, ] 4342.81 4342.81 108 42 null null 40-MOD:00425,613-MOD:00425,746-MOD:00425,947-MOD:00425 0 1 0.784 0.848 0.693 + """ + file_path = write_test_data( + data=TEST_DATA, directory=tmp_path, test_case_name=TEST_FILE_NAME + ) + reference = get_local_reference_data(test_case_name=TEST_FILE_NAME) + + return file_path, reference diff --git a/tests/integration/reference_data/reference_pg_fragpipe.parquet b/tests/integration/reference_data/reference_pg_fragpipe.parquet new file mode 100644 index 00000000..e7751798 Binary files /dev/null and b/tests/integration/reference_data/reference_pg_fragpipe.parquet differ diff --git a/tests/integration/reference_data/reference_pg_mztab_minimal.parquet b/tests/integration/reference_data/reference_pg_mztab_minimal.parquet new file mode 100644 index 00000000..2bd11b55 Binary files /dev/null and b/tests/integration/reference_data/reference_pg_mztab_minimal.parquet differ diff --git a/tests/integration/reference_data/reference_pg_spectronaut_18.0.tsv.parquet b/tests/integration/reference_data/reference_pg_spectronaut_18.0.tsv.parquet new file mode 100644 index 00000000..7afad5c6 Binary files /dev/null and b/tests/integration/reference_data/reference_pg_spectronaut_18.0.tsv.parquet differ diff --git a/tests/integration/test_pg_reader_provider.py b/tests/integration/test_pg_reader_provider.py index 015ab7b6..5d5d1f7a 100644 --- a/tests/integration/test_pg_reader_provider.py +++ b/tests/integration/test_pg_reader_provider.py @@ -4,7 +4,10 @@ AlphaDiaPGReader, AlphaPeptPGReader, DiannPGReader, + FragPipePGReader, MaxQuantPGReader, + MZTabPGReader, + SpectronautPGReader, pg_reader_provider, ) @@ -39,3 +42,27 @@ def test_reader_provider(self) -> None: reader = pg_reader_provider.get_reader("maxquant") assert isinstance(reader, MaxQuantPGReader) + + +class TestSpectronautPGReaderProvider: + def test_reader_provider(self) -> None: + """Test whether reader provider initializes spectronaut protein group reader correctly.""" + reader = pg_reader_provider.get_reader("spectronaut") + + assert isinstance(reader, SpectronautPGReader) + + +class TestFragPipePGReaderProvider: + def test_reader_provider(self) -> None: + """Test whether reader provider initializes FragPipe protein group reader correctly.""" + reader = pg_reader_provider.get_reader("fragpipe") + + assert isinstance(reader, FragPipePGReader) + + +class TestMZTabPGReaderProvider: + def test_reader_provider(self) -> None: + """Test whether reader provider initializes MZTab protein group reader correctly.""" + reader = pg_reader_provider.get_reader("mztab") + + assert isinstance(reader, MZTabPGReader) diff --git a/tests/integration/test_pg_readers.py b/tests/integration/test_pg_readers.py index 87a17982..13875838 100644 --- a/tests/integration/test_pg_readers.py +++ b/tests/integration/test_pg_readers.py @@ -7,7 +7,10 @@ AlphaDiaPGReader, AlphaPeptPGReader, DiannPGReader, + FragPipePGReader, MaxQuantPGReader, + MZTabPGReader, + SpectronautPGReader, ) from alphabase.pg_reader.keys import PGCols @@ -172,3 +175,59 @@ def test_measurement_regex( PGCols.GENES, PGCols.DECOY_INDICATOR, ] + + +class TestSpectronautPGReader: + def test_import_real_file_tsv(self, example_spectronaut_tsv: str) -> None: + """Test import of real spectronaut file""" + file_path, reference = example_spectronaut_tsv + + reader = SpectronautPGReader() + + result_df = reader.import_file(file_path=file_path) + + pd.testing.assert_frame_equal(result_df, reference) + + def test_import_real_file_parqet(self, example_spectronaut_parquet: str) -> None: + """Test import of real spectronaut file""" + file_path, reference = example_spectronaut_parquet + + reader = SpectronautPGReader() + + result_df = reader.import_file(file_path=file_path) + + pd.testing.assert_frame_equal(result_df, reference) + + +class TestFragPipePGReader: + def test_import_real_file(self, example_fragpipe_tsv: str) -> None: + """Test import of real FragPipe file""" + file_path, reference = example_fragpipe_tsv + + reader = FragPipePGReader() + + result_df = reader.import_file(file_path=file_path) + + pd.testing.assert_frame_equal(result_df, reference) + + +class TestMZTabPGReader: + def test_import_real_file(self, example_mztab: str) -> None: + """Test import of real MZTab file""" + file_path, reference = example_mztab + + reader = MZTabPGReader() + + result_df = reader.import_file(file_path=file_path) + + pd.testing.assert_frame_equal(result_df, reference) + + def test_import_minimal_example(self, example_mztab_minimal: str) -> None: + """Test import of minimal example MZTab file""" + file_path, reference = example_mztab_minimal + + reader = MZTabPGReader() + + result_df = reader.import_file(file_path=file_path) + + pd.testing.assert_frame_equal(result_df, reference)