Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions alphabase/constants/const_files/pg_reader.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,34 @@ spectronaut:
# 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
4 changes: 4 additions & 0 deletions alphabase/pg_reader/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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

Expand All @@ -12,4 +14,6 @@
"AlphaPeptPGReader",
"MaxQuantPGReader",
"SpectronautPGReader",
"FragPipePGReader",
"MZTabPGReader",
]
47 changes: 47 additions & 0 deletions alphabase/pg_reader/fragpipe_pg_reader.py
Original file line number Diff line number Diff line change
@@ -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
Comment thread
lucas-diedrich marked this conversation as resolved.
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)
116 changes: 116 additions & 0 deletions alphabase/pg_reader/mztab_pg_reader.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Reader
:maxdepth: 2

modules_psm_reader
modules_pg_reader


I/O
Expand Down
17 changes: 17 additions & 0 deletions docs/modules_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
alphabase.pg_reader
===========================

All pg_readers can be accessed by
:obj:`pg_reader_provider <alphabase.pg_reader.pg_reader.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
7 changes: 7 additions & 0 deletions docs/pg_reader/alphadia_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.alphadia_pg_reader
======================================

.. automodule:: alphabase.pg_reader.alphadia_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/alphapept_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.alphapept_pg_reader
=======================================

.. automodule:: alphabase.pg_reader.alphapept_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/diann_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.diann_pg_reader
===================================

.. automodule:: alphabase.pg_reader.diann_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/fragpipe_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.fragpipe_pg_reader
======================================

.. automodule:: alphabase.pg_reader.fragpipe_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/maxquant_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.maxquant_pg_reader
======================================

.. automodule:: alphabase.pg_reader.maxquant_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/mztab_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.mztab_pg_reader
======================================

.. automodule:: alphabase.pg_reader.mztab_pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/pg_base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.base
========================

.. automodule:: alphabase.pg_reader.pg_reader
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/pg_reader/spectronaut_pg_reader.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
alphabase.pg_reader.spectronaut_reader
======================================

.. automodule:: alphabase.pg_reader.spectronaut_reader
:members:
:undoc-members:
:show-inheritance:
54 changes: 54 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,57 @@ def example_spectronaut_parquet(tmp_path) -> Path:
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
Binary file not shown.
Binary file not shown.
18 changes: 18 additions & 0 deletions tests/integration/test_pg_reader_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
AlphaDiaPGReader,
AlphaPeptPGReader,
DiannPGReader,
FragPipePGReader,
MaxQuantPGReader,
MZTabPGReader,
SpectronautPGReader,
pg_reader_provider,
)
Expand Down Expand Up @@ -48,3 +50,19 @@ def test_reader_provider(self) -> None:
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)
Loading
Loading