File size: 3,026 Bytes
6dd9839
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python3
"""Render HOMER *.motif PWMs to PNG logos (website display colors)."""
from __future__ import annotations

import argparse
import re
from pathlib import Path

import logomaker
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# Same palette as create_motif_logo.ipynb (website / paper figures)
COLOR_SCHEME = {
    "A": "#65a455",
    "C": "#2e45a4",
    "G": "#fda562",
    "U": "#d54f3f",
}


def parse_homer_motif(path: Path) -> list[tuple[str, np.ndarray]]:
    """Parse HOMER .motif file → [(label, Lx4 ACGU matrix), ...]."""
    text = path.read_text(encoding="utf-8", errors="replace")
    motifs: list[tuple[str, np.ndarray]] = []
    blocks = [b.strip() for b in text.split(">") if b.strip()]
    for bi, block in enumerate(blocks):
        lines = [ln.strip() for ln in block.splitlines() if ln.strip()]
        if not lines:
            continue
        header = lines[0].split("\t")
        label = header[1] if len(header) > 1 else f"{path.stem}_{bi + 1}"
        rows: list[list[float]] = []
        for ln in lines[1:]:
            parts = re.split(r"\s+", ln)
            if len(parts) != 4:
                continue
            try:
                rows.append([float(x) for x in parts])
            except ValueError:
                continue
        if rows:
            motifs.append((label, np.asarray(rows, dtype=np.float64)))
    return motifs


def plot_logo(matrix: np.ndarray, output_path: Path, dpi: int = 200) -> None:
    df = pd.DataFrame(matrix, columns=["A", "C", "G", "U"])
    df = df.div(df.sum(axis=1).replace(0, np.nan), axis=0).fillna(0.25)
    fig, ax = plt.subplots(figsize=(4, 2))
    logomaker.Logo(
        df,
        ax=ax,
        color_scheme=COLOR_SCHEME,
        baseline_width=0,
        show_spines=False,
    )
    ax.axis("off")
    output_path.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(output_path, dpi=dpi, bbox_inches="tight", format="png")
    plt.close(fig)


def render_homer_dir(homer_dir: Path, logo_dir: Path, dpi: int = 200) -> list[Path]:
    out: list[Path] = []
    logo_dir.mkdir(parents=True, exist_ok=True)
    for motif_path in sorted(homer_dir.glob("*.motif")):
        for i, (label, mat) in enumerate(parse_homer_motif(motif_path), start=1):
            safe = re.sub(r"[^\w.\-]+", "_", label)
            png = logo_dir / f"{motif_path.stem}_logo{i}_{safe}.png"
            plot_logo(mat, png, dpi=dpi)
            out.append(png)
            print(f"saved {png.name}")
    return out


def main() -> None:
    ap = argparse.ArgumentParser(description="HOMER .motif → PNG logos")
    ap.add_argument("homer_dir", type=Path, help="Directory of *.motif files")
    ap.add_argument("logo_dir", type=Path, help="Output directory for PNGs")
    ap.add_argument("--dpi", type=int, default=200)
    args = ap.parse_args()
    paths = render_homer_dir(args.homer_dir, args.logo_dir, dpi=args.dpi)
    print(f"wrote {len(paths)} logos -> {args.logo_dir}")


if __name__ == "__main__":
    main()