Skip to content

crispr_analysis_utils.guide_annotation.promoters

crispr_analysis_utils.guide_annotation.promoters

Transcription start sites, from a BED file or from the store itself.

PromoterError

Bases: RuntimeError

The promoter file is unusable, or shares no gene with the store.

Source code in src/crispr_analysis_utils/guide_annotation/promoters.py
14
15
class PromoterError(RuntimeError):
    """The promoter file is unusable, or shares no gene with the store."""

read_promoters

read_promoters(path: str | PathLike[str]) -> pd.DataFrame

Read a TSS BED file.

Parameters:

  • path (str | PathLike[str]) –

    A BED of transcription start sites, one row per site: a gene with several promoters needs one row each. Six columns at least; a seventh is read as the gene id, which is what lets a TSS be tied back to the store, and an eighth as the transcript id, which is what fills promoter_enst. A first line beginning with # is a header and is skipped.

Returns:

  • DataFrame –

    Chromosome, Start, End, Strand, gene_symbol, ensg and enst, the last two empty when the file has no seventh or eighth column.

Raises:

  • PromoterError –

    The file is missing, empty, or has fewer than six columns.

Source code in src/crispr_analysis_utils/guide_annotation/promoters.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def read_promoters(path: str | os.PathLike[str]) -> pd.DataFrame:
    """Read a TSS BED file.

    Parameters
    ----------
    path
        A BED of transcription start sites, one row per site: a gene with
        several promoters needs one row each. Six columns at least; a seventh
        is read as the gene id, which is what lets a TSS be tied back to the
        store, and an eighth as the transcript id, which is what fills
        ``promoter_enst``. A first line beginning with ``#`` is a header and
        is skipped.

    Returns
    -------
    pandas.DataFrame
        ``Chromosome``, ``Start``, ``End``, ``Strand``, ``gene_symbol``,
        ``ensg`` and ``enst``, the last two empty when the file has no
        seventh or eighth column.

    Raises
    ------
    PromoterError
        The file is missing, empty, or has fewer than six columns.
    """
    path = Path(path)
    if not path.is_file():
        raise PromoterError(f"The promoter file {path} does not exist.")

    with open(path, encoding="utf-8") as handle:
        first = handle.readline()
    try:
        frame = pd.read_csv(
            path,
            sep="\t",
            header=None,
            skiprows=1 if first.startswith("#") else 0,
            dtype={0: str},
        )
    except pd.errors.EmptyDataError as error:
        raise PromoterError(f"{path} holds no transcription start site.") from error
    if frame.shape[1] < len(_BED6):
        raise PromoterError(
            f"{path} has {frame.shape[1]} columns; a TSS BED needs at least"
            f" {len(_BED6)}: {', '.join(_BED6)}."
        )
    if frame.empty:
        raise PromoterError(f"{path} holds no transcription start site.")

    names = (
        list(_BED6)
        + ["ensg", "enst"]
        + [f"extra_{i}" for i in range(frame.shape[1] - 8)]
    )
    frame.columns = names[: frame.shape[1]]
    for column in ("ensg", "enst"):
        if column not in frame:
            frame[column] = ""
    return frame.assign(
        ensg=_unversioned(frame["ensg"]), enst=_unversioned(frame["enst"])
    )[["Chromosome", "Start", "End", "Strand", "gene_symbol", "ensg", "enst"]]

promoters_from_store

promoters_from_store(store, *, reference_only: bool = False) -> pd.DataFrame

Derive the TSS of every transcript from the store.

A gene has as many promoters as it has distinct transcript starts, and a guide at an alternative promoter is invisible to a one-TSS-per-gene file, so every transcript contributes a site by default.

Parameters:

  • store –

    A loaded Store.

  • reference_only (bool, default: False ) –

    Take only each gene's reference transcript, one TSS per gene. This is the older behaviour and loses alternative promoters.

Returns:

  • DataFrame –

    The same columns read_promoters returns, each TSS one base wide, deduplicated so two transcripts sharing a start give one row per transcript but not two identical ones.

Source code in src/crispr_analysis_utils/guide_annotation/promoters.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def promoters_from_store(store, *, reference_only: bool = False) -> pd.DataFrame:
    """Derive the TSS of every transcript from the store.

    A gene has as many promoters as it has distinct transcript starts, and a
    guide at an alternative promoter is invisible to a one-TSS-per-gene file,
    so every transcript contributes a site by default.

    Parameters
    ----------
    store
        A loaded `Store`.
    reference_only
        Take only each gene's reference transcript, one TSS per gene. This is
        the older behaviour and loses alternative promoters.

    Returns
    -------
    pandas.DataFrame
        The same columns `read_promoters` returns, each TSS one base wide,
        deduplicated so two transcripts sharing a start give one row per
        transcript but not two identical ones.
    """
    rows = store.rows("transcript", reference_only=reference_only)
    if rows.empty:
        rows = store.rows("gene")
    forward = rows["Strand"] == "+"
    start = rows["Start"].where(forward, rows["End"] - 1)
    frame = pd.DataFrame(
        {
            "Chromosome": rows["Chromosome"].to_numpy(),
            "Start": start.to_numpy(),
            "End": start.to_numpy() + 1,
            "Strand": rows["Strand"].to_numpy(),
            "gene_symbol": rows["gene_symbol"].to_numpy(),
            "ensg": rows["ensg"].to_numpy(),
            "enst": rows["enst"].to_numpy() if "enst" in rows else "",
        }
    )
    return frame.drop_duplicates().reset_index(drop=True)