NetworkX入门及实战教程

  • 环境要求和工具包安装
  • 自带图的绘制
  • 连接表和邻接表创建图
    • 通过连接表edge list创建图
    • 可视化
    • 查看全图参数
    • 保存并载入邻接表
  • 用NetworkX创建图
    • 创建空图
    • 添加单个节点
    • 添加多个节点
    • 添加带属性的节点
    • 可视化
    • Note
  • 用NetworkX创建连接
    • 创建单个连接
    • 创建多个连接
    • 节点连接数(Node degree)
  • nx.draw出图美化
    • 原生可视化
    • 高级设置
    • 设置每个节点的坐标(例如地铁坐标是有自己的分布位置的,想保留这种空间信息)
    • 有向图代码美化模板(论文绘图专用)
  • 计算节点特征
    • Node Degree
    • Degree Centrality
    • Node Degree分析
      • 最大连通域子图
      • 每个节点的连接数(degree)
    • Eigenvector Centrality
    • Betweenness Centrality
    • Closeness Centrality
    • PageRank
    • Katz Centralily
    • HITS Hubs and Authorities
    • Clustering Coefficient
    • Briges
    • Common Neighbors & Jaccard Coefficient & Adamic Adar Index
  • Graphlet特征
  • 拉普拉斯矩阵分解
    • 计算拉普拉斯矩阵L和节点degree对角矩阵D
    • 计算归一化拉普拉斯矩阵Ln和特征值分解
  • 社群检测

NetworkX是一个基于Python的库,用于创造、操作复杂网络,是专门用作网络分析的工具包。

手动防爬虫,作者CSDN:总是重复名字我很烦啊,联系邮箱daledeng123@163.com

环境要求和工具包安装

Anaconda环境,安装jupyter notebook即可。以下所有操作都是在jupyter中进行。建议在anaconda中创建一个新的独立虚拟环境,取名networkX。

在jupyter notebook中输入以下代码,完成相关依赖的初步安装,后续有需求会单独说明。

!pip install numpy pandas matplotlib tqdm networkx scipy -i https://pypi.tuna.tsinghua.edu.cn/simple

运行nx.__version__可以查看networkx的版本。这一步如果没有报错,则说明安装成功。

import networkx as nximport matplotlib.pyplot as pltnx.__version__

自带图的绘制

图名称代码
全连接无向图G = nx.complete_graph(7)
全连接有向图G = nx.complete_graph(7, nx.DiGraph())
环状图G = nx.cycle_graph(5)
梯状图G = nx.ladder_graph(5)
线性串珠图G = nx.path_graph(15)
星状图G = nx.star_graph(7)
轮辐图G = nx.wheel_graph(7)
二项树G = nx.binomial_tree(5)
二维矩形网络G = nx.grid_2d_graph(3,5)
多维矩形网络G = nx.grid_graph(dim=(2,3,4))
二维三角形网络图G = nx.triangular_lattice_graph(2,5)
二维六边形蜂窝图G = nx.hexagonal_lattice_graph(2,3)
n维超立方体G = nx.hypercube_graph(4)
钻石图G = nx.diamond_graph()
frucht图G = nx.frucht_graph()
房子图G = nx.house_graph()
封顶房子图G = nx.house_x_graph()
彼得森图G = nx.petersen_graph()
krackhardt图G = nx.krackhardt_kite_graph()
随机图G = nx.erdos_renyi_graph(10,0.5)
## 无标度有向图(20%的节点拥有80%的连接)G = nx.scale_free_graph(100)
社交网络图(拳击俱乐部)G = nx.karate_club_graph()
社交网络图(雨果悲惨世界)G = nx.les_miserables_graph()
家庭图G = nx.florentine_families_graph()
社群聚类G = nx.caveman_graph(4,3)
树图tree = nx.random_tree(n=10, seed=0)

这里具体展示几个常见的代码:

# 树tree = nx.random_tree(n=10, seed=0)print(nx.forest_str(tree,sources=[0]))

# 家庭图G = nx.florentine_families_graph()nx.draw(G, with_labels=True)

# 雨果悲惨世界小说人物关系G = nx.les_miserables_graph()plt.figure(figsize=(12,10))pos = nx.spring_layout(G, seed=10)nx.draw(G, pos, with_labels=True)

# 空手道俱乐部样例数据集G = nx.karate_club_graph()nx.draw(G, with_labels=True)


并且可以看节点的类别:

连接表和邻接表创建图

导入数据

# 数据来源:http://www.openkg.cn/dataset/ch4masterpiecesdf = pd.read_csv('data(广东外贸外语大学)/三国演义/triples.csv')df.head()

通过连接表edge list创建图

# 创建有向图G = nx.DiGraph()edges = [edge for edge in zip(df['head'], df['tail'])]G.add_edges_from(edges)G.edges('关羽')>> OutEdgeDataView([('关羽', '刘备'), ('关羽', '张飞')])

可视化

pos = nx.spring_layout(G, seed = 123)plt.figure(figsize=(15,15))nx.draw(G, pos=pos, with_labels = True)

查看全图参数

print(G, len(G), G.size(), G.nodes)

保存并载入邻接表

for line in nx.generate_adjlist(G):print(line)break
# 将邻接表导出为本地文件 grid.edgelistnx.write_edgelist(G, path='grid.edgelist', delimiter=":")# 从本地读取grid.edgelist邻接表H = nx.read_edgelist(path='grid.edgelist', delimiter=":")# 可视化pos = nx.spring_layout(H, iterations=3, seed = 5)plt.figure(figsize=(15,14))nx.draw(G, pos=pos, with_labels = True)

用NetworkX创建图

创建空图

G = nx.Graph()nx.draw(G)

添加单个节点

# 节点可以是中文英文字符串G.add_node('刘备')G.add_node('Tommy')G.add_node('1')G.nodes>>NodeView(('刘备', 'Tommy', '1'))

添加多个节点

G.add_nodes_from(['诸葛亮','曹操'])G.add_nodes_from(range(100,105))G.nodes>>NodeView(('刘备', 'Tommy', '1', '诸葛亮', '曹操', 100, 101, 102, 103, 104))

添加带属性的节点

G.add_nodes_from([('关羽',{'武器':'青龙偃月刀','武力值':90,'智力值':80}),('张飞',{'武器':'丈八蛇矛','武力值':85,'智力值':75}),('吕布',{'武器':'方天画戟','武力值':100,'智力值':70})])G.nodes>>NodeView(('刘备', 'Tommy', '1', '诸葛亮', '曹操', 100, 101, 102, 103, 104, '关羽', '张飞', '吕布'))

可视化

nx.draw(G)

# 创建另一个首尾相连的path graphH = nx.path_graph(10)nx.draw(H)

# 把H节点添加到G中G.add_nodes_from(H)G.nodes>>NodeView(('刘备', 'Tommy', '1', '诸葛亮', '曹操', 100, 101, 102, 103, 104, '关羽', '张飞', '吕布', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
nx.draw(G)

# 把H整张图作为一个节点添加到G中G.add_node(H)G.nodes>>NodeView(('刘备', 'Tommy', '1', '诸葛亮', '曹操', 100, 101, 102, 103, 104, '关羽', '张飞', '吕布', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, <networkx.classes.graph.Graph object at 0x00000240BE5625B0>))
nx.draw(G)

Note

任何可哈希的对象,比如字符串、图像、XML对线甚至另一个Graph都可以作为节点。通过这种方式可以多模态的构建图网络。

用NetworkX创建连接

# 无向图G = nx.Graph()G.is_directed()>> FlaseH = nx.DiGraph()H.is_directed()>> True

首先创建节点

# 创建单个节点G.add_node(0, features=5, label=0, tezheng2=2)# 创建2个节点G.add_nodes_from([('node1',{'feature1':1, 'feature2':2, 'feature3':3}),('node2',{'feature1':4, 'feature2':5, 'feature3':6}),])G.number_of_nodes()>>3
for node in G.nodes(data=True):print(node)>>(0, {'features': 5, 'label': 0, 'tezheng2': 2})('node1', {'feature1': 1, 'feature2': 2, 'feature3': 3})('node2', {'feature1': 4, 'feature2': 5, 'feature3': 6})

创建单个连接

# 0节点和node1节点之间创建连接G.add_edge(0, 'node1', weight=0.5, like=3)

创建多个连接

G.add_edges_from([('node1','node2',{'weight':0.3, 'like':5}),('node2',0,{'weight':0.1, 'like':8})])
# 可视化nx.draw(G, with_labels=True)

节点连接数(Node degree)

node_id = 'node1'G.degree[node_id]>>2for neighbor in G.neighbors(node_id):print('Node {} has neighbor {}'.format(node_id, neighbor))>>Node node1 has neighbor 0Node node1 has neighbor node2

nx.draw出图美化

# 创建4x4网络图G = nx.grid_2d_graph(4,4)

原生可视化

pos = nx.spring_layout(G, seed=123)nx.draw(G, pos=pos)

# 不显示节点 node_size=0nx.draw(G, pos=pos, node_size=0)

高级设置

# 无向图nx.draw(G,node_color = 'orange', # 节点颜色edgecolors='red',# 节点外边缘颜色edge_color = 'blue',# edge颜色# edge_cmap=plt.cm.plasma, # 配色方案node_size = 800,with_labels = False,width =3,)

# 有向图nx.draw(G.to_directed(),node_color = 'orange', # 节点颜色edgecolors='red',# 节点外边缘颜色edge_color = 'blue',# edge颜色# edge_cmap=plt.cm.coolwarm, # 配色方案node_size = 800,with_labels = False,width =3,)

设置每个节点的坐标(例如地铁坐标是有自己的分布位置的,想保留这种空间信息)

# 无向图G = nx.Graph()G.add_edge(1,2)G.add_edge(1,3)G.add_edge(1,5)G.add_edge(2,3)G.add_edge(3,4)G.add_edge(4,5)nx.draw(G)

pos = {1:(0,0), 2:(-1,0.3), 3:(2,0.17), 4:(4,0.255), 5:(5, 0.03)}options = {'font_size':36,'node_size':3000,'node_color':'white','edgecolors':'black','linewidths':5,'width':5}nx.draw_networkx(G, pos=pos, **options)ax = plt.gca()ax.margins(0.20)plt.axis('off')plt.show()

# 有向图G = nx.DiGraph([(0,3), (1,3), (2,4), (3,5), (3,6), (4,6), (5,6)])nx.draw(G, with_labels=True)

# 可视化每一列包含的节点left_nodes = [0, 1, 2]middle_nodes = [3, 4]right_nodes = [5, 6]# 可视化每个节点坐标pos = {n:(0, i) for i, n in enumerate(left_nodes)}pos.update({n: (1, i+0.5) for i, n in enumerate(middle_nodes)})pos.update({n: (2, i+0.5) for i, n in enumerate(right_nodes)})nx.draw(G, pos=pos, with_labels=True, **options)ax = plt.gca()ax.margins(0.20)plt.axis('off')plt.show()

有向图代码美化模板(论文绘图专用)

原图:

seed = 13648G = nx.random_k_out_graph(10, 3, 0.5, seed=seed)pos = nx.spring_layout(G, seed = seed)nx.draw(G, pos, with_labels = True)


美化过程:

node_sizes = [12 + 10 * i for i in range(len(G))]M = G.number_of_edges()edge_colors = range(2, M+2)edge_alphas = [(5 + i) / (M + 4) for i in range(M)]cmap = plt.cm.plasmaplt.figure(figsize=(10,8))nodes = nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='indigo')edges = nx.draw_networkx_edges(G,pos,node_size=node_sizes,arrowstyle='->',arrowsize =20,edge_color=edge_colors,edge_cmap = cmap,width = 4)for i in range(M):edges[i].set_alpha(edge_alphas[i])pc = mpl.collections.PathCollection(edges, cmap=cmap)pc.set_array(edge_colors)plt.colorbar(pc)ax = plt.gca()ax.set_axis_off()plt.show()

计算节点特征

首先定义一个画图的辅助函数,方便后续绘制。

# 定义可视化辅助函数def draw(G, pos, measures, measure_name):nodes = nx.draw_networkx_nodes(G, pos, node_size=250, cmap=plt.cm.plasma, node_color = list(measures.values()), nodelist=measures.keys())nodes.set_norm(mcolors.SymLogNorm(linthresh=0.01, linscale=1, base=10))# labels = nx.draw_networkx_labels(G, pos)edges = nx.draw_networkx_edges(G, pos)# plt.figure(figsize=(10,8))plt.title(measure_name)plt.colorbar(nodes)plt.axis('off')plt.show()

导入无向图和有向图

# 导入无向图G = nx.karate_club_graph()pos = nx.spring_layout(G, seed=675)# 导入有向图DiG = nx.DiGraph()DiG.add_edges_from([(2, 3), (3, 2), (4, 1), (4, 2), (5, 2), (5, 4), (5, 6), (6, 2), (6, 5), (7, 2), (7, 5), (8, 2), (8, 5), (9, 2), (9, 5), (10, 5), (11, 5)])

无向图如下:

有向图如下:

Node Degree

通过连接度可视化,颜色黄说明这个和这个点先连接的点越多。

# 无向图draw(G, pos, dict(G.degree()), 'Node Degree')

Degree Centrality

实际上这个绘制和Degree是差不多的。因为只关注了连接度这一个指标。

# 无向图draw(G, pos, nx.degree_centrality(G), 'Degree Centrality')# 有向图,分in和out两个方向draw(DiG, pos, nx.in_degree_centrality(DiG), 'in Degree Centrality')draw(DiG, pos, nx.out_degree_centrality(DiG), 'out Degree Centrality')



Node Degree分析

绘制一个随机无向图:

G = nx.gnp_random_graph(100, 0.02, seed=10374196)pos = nx.spring_layout(G, seed = 10)nx.draw(G ,pos)

最大连通域子图

我们会发现这样一幅图内,有的点是连接的,有的点是分散的,因此我们可以找出最大连接子图:

Gcc = G.subgraph(sorted(nx.connected_components(G), key = len, reverse = True)[0])pos = nx.spring_layout(Gcc, seed = 10396953)nx.draw_networkx_nodes(Gcc, pos, node_size=20)nx.draw_networkx_edges(Gcc, pos, alpha=0.4)


当然这样的图不够美观,可以进一步优化:

plt.figure(figsize=(12,8))pos = nx.spring_layout(Gcc, seed = 10396953)options = {'font_size':12,'node_size':350,'node_color':'white','edgecolors':'black','linewidths':1,'width':2}nx.draw_networkx(Gcc, pos, **options)plt.title('Connected components of G', fontsize=20)plt.axis('off')plt.show()

每个节点的连接数(degree)

用G.degree()可以查看节点连接的情况:

G.degree()


由于这个G.degree输出的结果比较混乱,因此我们需要统计后完成可视化工作。

# 梯子图plt.plot(degree_sequence, 'b--', marker='o')plt.title('Degree Rank Plot', fontsize=20)plt.ylabel('Degree', fontsize=25)plt.xlabel('Rank', fontsize=25)plt.tick_params(labelsize=20) # 坐标轴文字大小plt.show()

# 直方图X = np.unique(degree_sequence, return_counts=True)[0]Y = np.unique(degree_sequence, return_counts=True)[1]plt.bar(X, Y)plt.title('Degree Histogram', fontsize=20)plt.ylabel('Number', fontsize=25)plt.xlabel('Degree', fontsize=25)plt.tick_params(labelsize=20) # 坐标轴文字大小plt.show()

Eigenvector Centrality

这是一个很常规的思想:如果一个节点和他相邻的节点都很重要,那这个节点也很重要。

# 无向图draw(G, pos, nx.eigenvector_centrality(G), 'Eigenvector Centrality')# 有向图draw(DiG, pos, nx.eigenvector_centrality_numpy(DiG), 'Eigenvector Centrality')


Betweenness Centrality

间重要度表示这个点是不是处于交通要道,一夫当关万夫莫开的位置。

# 无向图draw(G, pos, nx.betweenness_centrality(G), 'Betweenness Centrality')# 有向图draw(DiG, pos, nx.betweenness_centrality(DiG), 'Betweenness Centrality')


Closeness Centrality

如果一个点到哪都近,那他的最近距离重要度就越高。

draw(G, pos, nx.closeness_centrality(G), 'Closeness Centrality')

PageRank

PageRank在传统图机器学习里有举足轻重的影响,因此后续会对PageRank的论文做精读,理解他的核心思想和贡献(敬请期待)。

draw(DiG, pos, nx.pagerank(DiG, alpha=0.85), 'PageRank')

此外,绘制一个简单的无向图

G = nx.star_graph(7)nx.draw(G, with_labels = False)


计算PageRank重要度:

pagerank = nx.pagerank(G, alpha=0.8)

注意,PageRank只能计算有向图,所以对无向图使用这样的工具后,会自动转化为有向图。

pagerank>>{0: 0.4583348922684132, 1: 0.07738072967594098, 2: 0.07738072967594098, 3: 0.07738072967594098, 4: 0.07738072967594098, 5: 0.07738072967594098, 6: 0.07738072967594098, 7: 0.07738072967594098}

Katz Centralily

假如两个节点没有共同好友,那么这个时候共同好友个数、交并比都是0,但实际上这两个节点可能存在一定的联系。因此在这种情况,需要看全图信息。往往用卡姿系数(Katz index)来表示,他表示节点u和节点v之间长度为k的路径个数。

# 无向图draw(G, pos, nx.katz_centrality(G, alpha=0.1, beta=1.0), 'Katz Centrality')# 有向图draw(DiG, pos, nx.katz_centrality(DiG, alpha=0.1, beta=1.0), 'Katz Centrality')



可以用邻接矩阵的幂来计算katz index。

# 计算主特征向量L = nx.normalized_laplacian_matrix(G)e = np.linalg.eigvals(L.A)print('最大特征值',max(e))# 折减系数beta = 1/max(e)# 创建单位矩阵I = np.identity(len(G.nodes))# 计算Katz IndexS = inv(I - nx.to_numpy_array(G)*beta) - I

HITS Hubs and Authorities

h, a = nx.hits(DiG)draw(DiG, pos, h, 'DiGraph HITS Hubs')draw(DiG, pos, a, 'DiGraph HITS Authorities')


Clustering Coefficient

越抱团取暖的点,他的聚集系数就越大。

draw(G, pos, nx.clustering(G), 'Clustering Coefficient')

Briges

如果某个连接断掉,会使连通域个数增加,则该连接是brige,brige连接不属于环的一部分。例如上图右侧蓝色的,如果断开连接,那么整张图的连通域变成2个,所以他的连接属于brige。
用代码工具可以直接检测brige节点。

list(nx.bridges(G))

Common Neighbors & Jaccard Coefficient & Adamic Adar Index

领域关系的相关代码实现如下:

# Common Neighborssorted(nx.common_neighbors(G, 0, 4))#Jaccard Coefficientpreds = nx.jaccard_coefficient(G, [(0,1), (2,3)])for u, v, p in preds:print(f'{u} , {v} -> {p:.8f}')#Adamic Adar Indexpreds = nx.adamic_adar_index(G, [(0,1), (2,3)])for u, v, p in preds:print(f'{u} , {v} -> {p:.8f}')

Graphlet特征

导入空手道俱乐部图

G = nx.karate_club_graph()plt.figure(figsize=(10,8))pos = nx.spring_layout(G, seed=123)nx.draw(G, pos, with_labels=True)

指定Graphlet

target = nx.complete_graph(3)nx.draw(target)


匹配Graphlet,统计个数。

num = 0for sub_nodes in itertools.combinations(G.nodes(), len(target.nodes())): # 遍历全图中符合graphlet节点个数的所有节点组合subg = G.subgraph(sub_nodes) # 从全图中抽出子图 if nx.is_connected(subg) and nx.is_isomorphic(subg, target):#如果子图是完整连通域,并且符合graphlet特征,输出原图节点编号num += 1print(subg.edges())

拉普拉斯矩阵分解

拉普拉斯矩阵常见的有两种,一种是普通拉普拉斯矩阵L,一种是归一化拉普拉斯矩阵Ln,具体计算公式如下:

创建图

n, m = 1000, 5000 # 1000个节点,5000个连接G = nx.gnm_random_graph(n, m, seed=5040)

计算邻接矩阵

# 邻接矩阵A = nx.adjacency_matrix(G)A.shape

注意这里的A是一个非常稀疏的矩阵,可以用A.todense()变成稠密矩阵。

计算拉普拉斯矩阵L和节点degree对角矩阵D

# Laplacian MatrixL = nx.laplacian_matrix(G)# 对角矩阵DD = L + AD.todense()

计算归一化拉普拉斯矩阵Ln和特征值分解

L_n = nx.normalized_laplacian_matrix(G)

特征值分解

e = np.linalg.eigvals(L_n.A)max(e), min(e)>>(1.5924617911776022, -2.3557437655170784e-16)

直方图可视化

plt.figure(figsize=(12,8))plt.hist(e, bins=100)plt.xlim(0,2)plt.title('Eigenvalue Histogram', fontsize=20)plt.ylabel('Frequency', fontsize=25)plt.xlabel('Eigenvalue', fontsize=25)plt.tick_params(labelsize=20)plt.show()

社群检测

import networkx as nximport matplotlib.pyplot as pltimport warningswarnings.filterwarnings('ignore')plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.rcParams['axes.unicode_minus']=FalseG = nx.karate_club_graph()from networkx.algorithms import communitycommunities = community.label_propagation_communities(G)node_groups = []for com in communities:node_groups.append(list(com))color_map = []for node_id in G:if node_id in node_groups[0]:color_map.append('blue')elif node_id in node_groups[1]:color_map.append('red')else:color_map.append('green')nx.draw(G, node_color = color_map, with_labels=True)