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
14 changes: 13 additions & 1 deletion alphabase/constants/const_files/pg_reader.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ fragpipe:
"uniprot_ids": "Protein ID"
"genes": "Gene Names"
"description": "Description"

measurement_regex:
"raw": "Intensity$"
"razor": "Razor Intensity$"
Expand All @@ -106,3 +105,16 @@ fragpipe:
"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
2 changes: 2 additions & 0 deletions alphabase/pg_reader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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 @@ -14,4 +15,5 @@
"MaxQuantPGReader",
"SpectronautPGReader",
"FragPipePGReader",
"MZTabPGReader",
]
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:
29 changes: 29 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,32 @@ def example_fragpipe_tsv(tmp_path) -> Path:
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.
9 changes: 9 additions & 0 deletions tests/integration/test_pg_reader_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
DiannPGReader,
FragPipePGReader,
MaxQuantPGReader,
MZTabPGReader,
SpectronautPGReader,
pg_reader_provider,
)
Expand Down Expand Up @@ -57,3 +58,11 @@ def test_reader_provider(self) -> None:
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)
23 changes: 23 additions & 0 deletions tests/integration/test_pg_readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
DiannPGReader,
FragPipePGReader,
MaxQuantPGReader,
MZTabPGReader,
SpectronautPGReader,
)
from alphabase.pg_reader.keys import PGCols
Expand Down Expand Up @@ -208,3 +209,25 @@ def test_import_real_file(self, example_fragpipe_tsv: str) -> None:
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)
Loading