File size: 2,766 Bytes
925ee3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Network graph visualization."""

from __future__ import annotations

import matplotlib.pyplot as plt
import numpy as np
from anndata import AnnData

from ._utils import setup_axes, save_or_show


def network_graph(
    adata: AnnData,
    n_edges: int = 50,
    figsize: tuple[float, float] = (8, 8),
    save: str | None = None,
    show: bool = True,
    ax: plt.Axes | None = None,
) -> plt.Figure | None:
    """Plot the inferred regulatory network as a graph.

    Requires ``adata.uns['pt_network']`` (from ``scptr.tl.infer_network``).

    Parameters
    ----------
    adata
        Annotated data matrix.
    n_edges
        Number of top edges to display.
    figsize
        Figure size.
    save
        Path to save.
    show
        Whether to display.
    ax
        Pre-existing axes.
    """
    if "pt_network" not in adata.uns:
        raise KeyError("Run scptr.tl.infer_network() first.")

    edges_df = adata.uns["pt_network"]
    if len(edges_df) == 0:
        fig, ax = setup_axes(ax, figsize=figsize)
        ax.text(0.5, 0.5, "No edges found", ha="center", va="center",
                transform=ax.transAxes)
        save_or_show(fig, save, show)
        return fig if not show else None

    edges_df = edges_df.head(n_edges)

    # Collect unique nodes
    nodes = list(set(edges_df["regulator"].tolist() + edges_df["target"].tolist()))
    node_idx = {n: i for i, n in enumerate(nodes)}

    # Simple circular layout
    n_nodes = len(nodes)
    angles = np.linspace(0, 2 * np.pi, n_nodes, endpoint=False)
    x = np.cos(angles)
    y = np.sin(angles)

    fig, ax = setup_axes(ax, figsize=figsize)

    # Draw edges
    max_weight = edges_df["weight"].abs().max()
    for _, row in edges_df.iterrows():
        i = node_idx[row["regulator"]]
        j = node_idx[row["target"]]
        w = abs(row["weight"]) / (max_weight + 1e-10)
        color = "red" if row["weight"] > 0 else "blue"
        ax.annotate(
            "",
            xy=(x[j], y[j]),
            xytext=(x[i], y[i]),
            arrowprops=dict(
                arrowstyle="->",
                color=color,
                alpha=0.3 + 0.7 * w,
                linewidth=0.5 + 2.0 * w,
            ),
        )

    # Draw nodes
    ax.scatter(x, y, s=100, c="lightgray", edgecolors="black", zorder=5)
    for node, idx in node_idx.items():
        ax.annotate(
            node,
            (x[idx], y[idx]),
            fontsize=7,
            ha="center",
            va="bottom",
            fontweight="bold",
        )

    ax.set_xlim(-1.5, 1.5)
    ax.set_ylim(-1.5, 1.5)
    ax.set_aspect("equal")
    ax.axis("off")
    ax.set_title("PT Regulatory Network")
    fig.tight_layout()

    save_or_show(fig, save, show)
    return fig if not show else None