Skip to content

mecfs_bio.build_system.task.ppp_ldsc.gene_coords

Gene coordinates per Olink protein, read from the Sun et al. 2023 supplementary table ST3.

The cis-excluded variant set needs to know where each protein's gene sits, so that variants near it can be dropped and the remaining heritability or genetic correlation reflects trans effects only. ST3 supplies those hg38 coordinates.

This module takes ST3 already extracted into a dataframe (ExtractSheetFromExelFileTask) rather than reading the spreadsheet itself, which keeps the spreadsheet's quirks -- the two title rows above the header, the mixed numeric/text columns -- at the point where ST3 is wired in, and leaves the parsing here as a plain dataframe-to-dictionary function.

Functions:

  • parse_chrom

    Chromosome label -> int (X -> 23); None for unparseable/non-standard labels.

  • read_gene_coords

    Map Olink ID (OID) -> hg38 gene coordinates from the extracted ST3 sheet.

Attributes:

ST3_GENE_CHROM_COL module-attribute

ST3_GENE_CHROM_COL = 'Gene CHROM'

ST3_GENE_END_COL module-attribute

ST3_GENE_END_COL = 'Gene end'

ST3_GENE_START_COL module-attribute

ST3_GENE_START_COL = 'Gene start'
ST3_OLINK_ID_COL = 'Olink ID'

logger module-attribute

logger = structlog.get_logger()

parse_chrom

parse_chrom(value: str | None) -> int | None

Chromosome label -> int (X -> 23); None for unparseable/non-standard labels.

NOTE: - There are a few "proteins" in PPP that are actually multi-protein complexes. - These multi-protein complexes do not correspond to a unique gene on a single chromosome. - In the Excel sheet, these proteins have entries in the chromosome column like chrom1;chrom2 - We return None for these proteins.

Source code in mecfs_bio/build_system/task/ppp_ldsc/gene_coords.py
def parse_chrom(value: str | None) -> int | None:
    """Chromosome label -> int (X -> 23); None for unparseable/non-standard labels.

    NOTE:
        - There are a few "proteins" in PPP that are actually multi-protein complexes.
        - These multi-protein complexes do not correspond to a unique gene on a single chromosome.
        - In the Excel sheet, these proteins have entries in the chromosome column like chrom1;chrom2
        - We return None for these proteins.

    """
    if value is None:
        return None
    text = str(value).strip()
    if text.isdigit():
        return int(text)
    if text.upper() == "X":
        return _X_CHROMOSOME
    return None

read_gene_coords

read_gene_coords(
    st3: DataFrame,
) -> dict[Oid, GenomicInterval]

Map Olink ID (OID) -> hg38 gene coordinates from the extracted ST3 sheet.

Proteins whose chromosome or coordinates are non-standard are skipped: they simply have no cis window, and callers treat them accordingly.

Source code in mecfs_bio/build_system/task/ppp_ldsc/gene_coords.py
def read_gene_coords(st3: pl.DataFrame) -> dict[Oid, GenomicInterval]:
    """Map Olink ID (OID) -> hg38 gene coordinates from the extracted ST3 sheet.

    Proteins whose chromosome or coordinates are non-standard are skipped: they simply have no
    cis window, and callers treat them accordingly."""
    for column in (
        ST3_OLINK_ID_COL,
        ST3_GENE_CHROM_COL,
        ST3_GENE_START_COL,
        ST3_GENE_END_COL,
    ):
        assert column in st3.columns, (
            f"extracted ST3 sheet has no '{column}' column; found {st3.columns}"
        )

    coords: dict[Oid, GenomicInterval] = {}
    for row in st3.select(
        pl.col(ST3_OLINK_ID_COL).cast(pl.String),
        pl.col(ST3_GENE_CHROM_COL).cast(pl.String),
        pl.col(ST3_GENE_START_COL).cast(pl.String),
        pl.col(ST3_GENE_END_COL).cast(pl.String),
    ).iter_rows(named=True):
        oid = row[ST3_OLINK_ID_COL]
        chrom = parse_chrom(row[ST3_GENE_CHROM_COL])
        start = _parse_position(row[ST3_GENE_START_COL])
        end = _parse_position(row[ST3_GENE_END_COL])
        if oid is None or chrom is None or start is None or end is None:
            continue
        coords[Oid(oid)] = GenomicInterval(chrom=chrom, start=start, end=end)

    logger.debug(f"Read gene coordinates for {len(coords)} of {st3.height} ST3 rows")
    return coords