File size: 2,758 Bytes
08a3e96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
graph_metrics.py  Β·  Lightweight NetworkX helpers for MedGenesis

Key features
────────────
β€’ Accepts edge dictionaries in either Streamlit-agraph or PyVis style:
      {"source": "n1", "target": "n2"}   ← agraph
      {"from":   "n1", "to":     "n2"}   ← PyVis
β€’ Silently skips malformed edges (no KeyError).
β€’ Provides three public helpers:
      build_nx(nodes, edges)         β†’ networkx.Graph
      get_top_hubs(G, k=5)           β†’ List[(node_id, degree_centrality)]
      get_density(G)                 β†’ float  (0–1)
"""

from __future__ import annotations
from typing import List, Dict, Tuple
import networkx as nx


# ────────────────────────────────────────────────────────────────────
# Internal helpers
# ────────────────────────────────────────────────────────────────────
def _edge_ends(e: Dict) -> Tuple[str, str] | None:
    """Return (src, dst) tuple if both ends exist; else None."""
    src = e.get("source") or e.get("from")
    dst = e.get("target") or e.get("to")
    if src and dst:
        return src, dst
    return None


# ────────────────────────────────────────────────────────────────────
# Public API
# ────────────────────────────────────────────────────────────────────
def build_nx(nodes: List[Dict], edges: List[Dict]) -> nx.Graph:
    """
    Convert agraph / PyVis node+edge dicts into a NetworkX Graph.

    Nodes: must contain "id"   (a unique string)
    Edges: accepted shapes β†’ {"source":, "target":} or {"from":, "to":}
    """
    G = nx.Graph()

    # Add nodes with label attribute (used by Metrics tab)
    for n in nodes:
        G.add_node(n["id"], label=n.get("label", n["id"]))

    # Add edges (skip malformed)
    for e in edges:
        ends = _edge_ends(e)
        if ends:
            G.add_edge(*ends)

    return G


def get_top_hubs(G: nx.Graph, k: int = 5) -> List[Tuple[str, float]]:
    """
    Return top-k nodes by degree-centrality.
    Example output: [('TP53', 0.42), ('EGFR', 0.36), ...]
    """
    dc = nx.degree_centrality(G)
    return sorted(dc.items(), key=lambda x: x[1], reverse=True)[:k]


def get_density(G: nx.Graph) -> float:
    """Graph density in [0, 1]."""
    return nx.density(G)