生物医学网络科学Biomedical Network Science
Example08 / 15

特征向量、Katz、PageRank 与 HITS 的幂迭代实现

所属专题
网络结构指标与随机图
下载
Notebook · .py

用同一个 6 节点有向小图说明谱类中心性的差别(约定 A[i, j] = 1 表示边 i -> j): 特征向量中心性在有向无环部分会退化为 0;Katz 加常数项 β 解决这一问题; PageRank 再按出度分摊并加随机跳转;HITS 同时给出权威值(authority)与枢纽值(hub)。 每种方法都用幂迭代手工实现,并与 NetworkX 核对。 输出:λ1 = 1(来自环 3→4→5→3);不在环上的节点 0、1、2 特征向量中心性都为 0, 而在 Katz 与 PageRank 中为正,节点 0(无入边)最小; HITS 中节点 0 是最强的“枢纽”,被多方指向的节点 3 是最强的“权威”。

Output输出图

特征向量、Katz、PageRank 与 HITS 的幂迭代实现:输出图 1

Output文字输出

λ1 = 1.000;特征向量中心性: [0.  0.  0.  0.4 0.2 0.4]
Katz(α=0.500): [0.143 0.214 0.321 0.674 0.48  0.383]  NetworkX 一致: True
PageRank: [0.025 0.032 0.046 0.321 0.298 0.278]  NetworkX 一致: True
authority: [0.    0.177 0.323 0.5   0.    0.   ]  NetworkX 一致: True
hub      : [0.354 0.292 0.177 0.    0.    0.177]  NetworkX 一致: True

Code代码

# 用同一个 6 节点有向小图说明谱类中心性的差别(约定 A[i, j] = 1 表示边 i -> j):
#   特征向量中心性在有向无环部分会退化为 0;Katz 加常数项 β 解决这一问题;
#   PageRank 再按出度分摊并加随机跳转;HITS 同时给出权威值(authority)与枢纽值(hub)。
# 每种方法都用幂迭代手工实现,并与 NetworkX 核对。
# 输出:λ1 = 1(来自环 3→4→5→3);不在环上的节点 0、1、2 特征向量中心性都为 0,
#       而在 Katz 与 PageRank 中为正,节点 0(无入边)最小;
#       HITS 中节点 0 是最强的“枢纽”,被多方指向的节点 3 是最强的“权威”。
import networkx as nx
import numpy as np

edges = [(0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 4), (4, 5), (5, 3), (1, 2)]
D = nx.DiGraph(edges)
nodes = sorted(D)
A = nx.to_numpy_array(D, nodelist=nodes)       # A[i, j] = 1: i -> j
N = len(nodes)


def power_iter(M, x0, n_iter=1000):
    x = x0.copy()
    for _ in range(n_iter):
        y = M @ x
        x = y / np.linalg.norm(y)
    return x


# 特征向量中心性(按入边累积):λ x = A^T x
lam = max(abs(np.linalg.eigvals(A)))
x_eig = power_iter(A.T, np.ones(N))
print("λ1 = %.3f;特征向量中心性:" % lam, np.round(x_eig / x_eig.sum(), 3))

# Katz:x = α A^T x + β 1  =>  x = β (I - α A^T)^{-1} 1,要求 α < 1/λ1
alpha = 0.5 / lam
x_katz = np.linalg.solve(np.eye(N) - alpha * A.T, np.ones(N))
ref = nx.katz_centrality_numpy(D, alpha=alpha, beta=1.0)
print("Katz(α=%.3f):" % alpha, np.round(x_katz / np.linalg.norm(x_katz), 3),
      " NetworkX 一致:", np.allclose(x_katz / np.linalg.norm(x_katz), [ref[v] for v in nodes]))

# PageRank:x = d A^T D_out^{-1} x + (1-d)/N;本图没有出度为 0 的节点
d = 0.85
kout = A.sum(axis=1)
P = (A / kout[:, None]).T                      # 列随机矩阵
x_pr = np.ones(N) / N
for _ in range(200):
    x_pr = d * P @ x_pr + (1 - d) / N
ref = nx.pagerank(D, alpha=d)
print("PageRank:", np.round(x_pr, 3), " NetworkX 一致:", np.allclose(x_pr, [ref[v] for v in nodes], atol=1e-6))

# HITS:a = A^T h, h = A a  =>  a ∝ A^T A a, h ∝ A A^T h
a = power_iter(A.T @ A, np.ones(N)); h = power_iter(A @ A.T, np.ones(N))
h_ref, a_ref = nx.hits(D, max_iter=1000)
print("authority:", np.round(a / a.sum(), 3), " NetworkX 一致:", np.allclose(a / a.sum(), [a_ref[v] for v in nodes], atol=1e-6))
print("hub      :", np.round(h / h.sum(), 3), " NetworkX 一致:", np.allclose(h / h.sum(), [h_ref[v] for v in nodes], atol=1e-6))

# 作图:5 种谱类中心性(各自归一化到和为 1)的分组柱状图
import matplotlib.pyplot as plt

scores = {"eigenvector": x_eig / x_eig.sum(), "Katz": x_katz / x_katz.sum(), "PageRank": x_pr,
          "authority": a / a.sum(), "hub": h / h.sum()}
fig, ax = plt.subplots(figsize=(6.4, 4.2))
w = 0.16
for j, (name, v) in enumerate(scores.items()):
    ax.bar(np.arange(N) + (j - 2) * w, v, width=w, label=name, hatch=["", "//", "", "..", "xx"][j],
           edgecolor="white" if j % 2 == 0 else "0.3")
ax.set_xticks(range(N)); ax.set_xticklabels(["node %d" % v for v in nodes])
ax.set_ylabel("score (sums to 1)"); ax.legend(fontsize=8, ncol=2)
ax.set_title("Nodes 0-2 vanish under eigenvector centrality only", fontsize=10)
plt.tight_layout(); plt.show()

Run在浏览器中运行

首次加载 Python 环境约需 20–40 秒;可以修改参数后重新运行。

特征向量、Katz、PageRank 与 HITS 的幂迭代实现