| name | network-biology |
| description | Build PPI networks from STRING with NetworkX, find hub genes via centrality, detect Louvain modules, infer GRNs with GENIE3. Use for protein interaction networks, hub/bottleneck genes, network communities, GRN inference. |
| tool_type | python |
| primary_tool | networkx |
Network Biology
When to Use
Use this skill when:
- Building protein-protein interaction (PPI) networks from STRING/BioGRID
- Computing network topology metrics (degree, betweenness, closeness, clustering) to find hub genes or bottlenecks
- Detecting functional modules/communities in biological networks (Louvain) and running GO enrichment per module
- Inferring gene regulatory networks (GRNs) from expression data with GENIE3
- Integrating network modules with differential expression results, or exporting a network to Cytoscape
Version Compatibility
- Python ≥3.10,
networkx ≥3.2, pandas ≥2.0, requests ≥2.31
python-louvain (community package) ≥0.16 for modularity-based community detection
gseapy ≥1.1 for Enrichr-based GO enrichment
- R ≥4.3,
GENIE3 (Bioconductor) ≥1.24 for GRN inference
- STRING REST API v11/v12 (
string-db.org/api)
Prerequisites
pip install networkx pandas requests python-louvain gseapy scipy matplotlib
Assumes familiarity with basic graph concepts (nodes/edges, degree) and a gene list or expression matrix to start from.
Quick Reference
| Task | Tool | Key Method |
|---|
| Retrieve PPI data | STRING REST API | requests.post(string_api, data=params) |
| Build graph | NetworkX | nx.from_pandas_edgelist(df) |
| Degree centrality | NetworkX | dict(G.degree()) |
| Betweenness | NetworkX | nx.betweenness_centrality(G) |
| Community detection | python-louvain | best_partition(G) |
| GO enrichment | gseapy | gp.enrichr(gene_list, gene_sets='GO_BP') |
| Export to Cytoscape | NetworkX | nx.write_graphml(G, 'net.graphml') |
| GRN inference | GENIE3 (R) | GENIE3(exprMatrix, regulators=tfs) |
Key Patterns
Pattern 1: STRING PPI network
import requests
import pandas as pd
import networkx as nx
genes = ['TP53', 'BRCA1', 'EGFR', 'MYC', 'KRAS']
string_api = 'https://string-db.org/api/json/network'
params = {
'identifiers': '%0d'.join(genes),
'species': 9606,
'required_score': 700
}
resp = requests.post(string_api, data=params)
interactions = pd.DataFrame(resp.json())
G = nx.from_pandas_edgelist(
interactions,
source='preferredName_A',
target='preferredName_B',
edge_attr='score'
)
Pattern 2: Network metrics
degree = dict(G.degree())
betweenness = nx.betweenness_centrality(G)
closeness = nx.closeness_centrality(G)
clustering = nx.clustering(G)
metrics = pd.DataFrame({
'degree': degree,
'betweenness': betweenness,
'closeness': closeness,
'clustering': clustering
})
hubs = metrics[metrics['degree'] >= metrics['degree'].quantile(0.9)]
Pattern 3: Louvain community detection
from community import best_partition
partition = best_partition(G, random_state=42)
modules = {}
for node, mod_id in partition.items():
modules.setdefault(mod_id, []).append(node)
print(f'{len(modules)} modules detected')
Pattern 4: GO enrichment per module
import gseapy as gp
for mod_id, genes in modules.items():
if len(genes) < 5:
continue
enr = gp.enrichr(
gene_list=genes,
gene_sets='GO_Biological_Process_2021',
organism='Human',
outdir=None
)
top = enr.results.head(5)[['Term', 'Adjusted P-value']]
print(f'Module {mod_id} ({len(genes)} genes): {top.to_string()}')
Pattern 5: GENIE3 GRN inference (R)
library(GENIE3)
tfs <- c('TP53', 'MYC', 'E2F1', 'FOXM1')
weight_matrix <- GENIE3(exprMatrix=expr_matrix, regulators=tfs,
nTrees=1000, nCores=8)
link_list <- getLinkList(weight_matrix, reportMax=20000)
Biological Network Properties
| Property | Random Network | Scale-Free (Biological) |
|---|
| Degree distribution | Poisson | Power law (P(k) ~ k^-γ) |
| Hubs | Absent | Present (oncogenes, TFs) |
| Robustness | Uniform | Robust to random, vulnerable to hub removal |
| Clustering | Low | High (modular) |
| Path length | √N | log(N) |
Pitfalls
- Literature bias — STRING PPI data is biased toward well-studied genes; hub genes may reflect research attention, not true biology
- Directionality — PPI networks are undirected (physical); GRNs are directed (regulatory); do not mix
- Community detection randomness — Louvain is stochastic; use
random_state and run multiple times
- Score threshold — STRING combined_score > 700 = high confidence; lower thresholds inflate network with false positives
- GRN validation — GENIE3 weights are correlational; validate against known TF binding data (JASPAR, TRRUST, ENCODE ChIP-seq)
Code Templates
Degree Distribution Plot
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
def plot_degree_distribution(G, title='Degree Distribution'):
degrees = [d for _, d in G.degree()]
counts = Counter(degrees)
k = sorted(counts.keys())
pk = [counts[ki] / len(degrees) for ki in k]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
ax1.bar(k, pk, color='steelblue', alpha=0.7)
ax1.set(xlabel='Degree k', ylabel='P(k)', title='Linear scale')
ax2.loglog(k, pk, 'o', color='steelblue', alpha=0.7)
ax2.set(xlabel='Degree k', ylabel='P(k)', title='Log-log (power law check)')
plt.suptitle(title)
plt.tight_layout()
return degrees
degs = plot_degree_distribution(G, title=f'Network ({G.number_of_nodes()} nodes)')
Identify Hub Genes and Bottlenecks
import networkx as nx
import pandas as pd
def find_network_key_nodes(G, top_n=20):
metrics = pd.DataFrame({
'degree': dict(G.degree()),
'betweenness': nx.betweenness_centrality(G, normalized=True),
'closeness': nx.closeness_centrality(G),
'pagerank': nx.pagerank(G),
})
metrics['composite'] = (
metrics['degree'] / metrics['degree'].max() +
metrics['betweenness'] / metrics['betweenness'].max() +
metrics['pagerank'] / metrics['pagerank'].max()
) / 3
return metrics.sort_values('composite', ascending=False).head(top_n)
key_nodes = find_network_key_nodes(G)
print(key_nodes[['degree', 'betweenness', 'pagerank']].head(10))
Overlap Network Modules with DEGs
import pandas as pd
def module_deg_overlap(modules, degs, background_size=20000):
"""Fisher's exact test for overlap between modules and DEG list."""
from scipy.stats import fisher_exact
results = []
deg_set = set(degs)
for mod_id, genes in modules.items():
module_set = set(genes)
a = len(module_set & deg_set)
b = len(module_set) - a
c = len(deg_set) - a
d = background_size - a - b - c
odds, pval = fisher_exact([[a, b], [c, d]], alternative='greater')
results.append({'module': mod_id, 'size': len(module_set),
'overlap': a, 'odds_ratio': odds, 'pvalue': pval})
df = pd.DataFrame(results).sort_values('pvalue')
df['padj'] = df['pvalue'] * len(df)
return df
overlap = module_deg_overlap(modules, degs=significant_genes)
print(overlap[overlap['padj'] < 0.05])
Export Network to GraphML (Cytoscape)
import networkx as nx
def export_for_cytoscape(G, metrics_df, output_path):
"""Add node attributes from metrics and save as GraphML."""
for node, row in metrics_df.iterrows():
if node in G:
G.nodes[node]['degree'] = int(row['degree'])
G.nodes[node]['betweenness'] = float(row['betweenness'])
G.nodes[node]['pagerank'] = float(row['pagerank'])
nx.write_graphml(G, output_path)
print(f"Saved {G.number_of_nodes()} nodes, {G.number_of_edges()} edges to {output_path}")
export_for_cytoscape(G, key_nodes, 'network.graphml')
See Also
bio-applied-ppi-networks — additional PPI network construction and analysis patterns
bio-applied-gene-regulatory-networks — GRN inference beyond GENIE3 (SCENIC, ARACNe)
bio-applied-network-modules — module/community detection deep dive
bio-applied-data-harmonization — integrating network modules with multi-omics expression data