0
点赞
收藏
分享

微信扫一扫

图机器学习(Graph Machine Learning)

第一章 图的基本概念1 Getting Started with Graphs

  • 本专栏是Claudio Stamile , Aldo Marzullo , Enrico Deusebio,Graph Machine Learning,Packt ,2021.的读书笔记。
  • 官方代码可以从 GitHub上下载。

文章目录


前言

图是一种数学结构,用于描述实体之间的关系,几乎无处不在。例如,社交网络是一种图,用户之间的联系取决于一个用户是否“关注”另一个用户的更新。图可以用来描述生物结构,网页,甚至神经退化疾病的进展。

图论多年来一直受到人们的极大兴趣,它引导人们开发算法,识别属性,定义数学模型,以更好地理解复杂的行为。

本章将回顾图结构数据背后的一些基本概念。本文将介绍一些理论概念,以及一些例子,以帮助您理解一些更一般的概念,并将其应用到实践中。

在本章中,我们将介绍和使用一些最广泛使用的库,用于创建、操作和研究复杂网络的结构动力学和函数,特别是Python networkx库。

本章将介绍如下内容:

  • 利用networkx模块介绍图的概念
  • 图的绘制
  • 图的性质
  • 基准和资源
  • 处理大型图

一、环境要求

所有实验将在Python 3.8和Jupyter Notebook中完成。

实验所需的常用模块及建议版本如下:

  • Jupyter==1.0.0
  • networkx==2.5
  • snap-stanford==5.0.0
  • matplotlib==3.2.2
  • pandas==1.1.3
  • scipy==1.6.2
import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt

二、利用networkx模块介绍图

在本节中,我们将对图论作一个一般性的介绍。此外,为了将理论概念与实际实现相结合,我们将使用使用Python中networkx模块的代码片段来丰富我们的解释。

def draw_graph(G, pos_nodes, node_names={}, node_size=50, plot_weight=False):
    nx.draw(G, pos_nodes, with_labels=False, node_size=node_size, edge_color='gray', arrowsize=30)
    
    pos_attrs = {}
    for node, coords in pos_nodes.items():
        pos_attrs[node] = (coords[0], coords[1] + 0.08)
        
    nx.draw_networkx_labels(G, pos_attrs, font_family='serif', font_size=20)
    
    
    if plot_weight:
        pos_attrs = {}
        for node, coords in pos_nodes.items():
            pos_attrs[node] = (coords[0], coords[1] + 0.08)
        
        nx.draw_networkx_labels(G, pos_attrs, font_family='serif', font_size=20)
        edge_labels=dict([((a,b,),d["weight"]) for a,b,d in G.edges(data=True)])
        nx.draw_networkx_edge_labels(G, pos_nodes, edge_labels=edge_labels)
    
    plt.axis('off')
    axis = plt.gca()
    axis.set_xlim([1.2*x for x in axis.get_xlim()])
    axis.set_ylim([1.2*y for y in axis.get_ylim()])

2.1 无向图 Undirected Graph

一个简单无向图(简称图) G G G可以被表示为 G = ( V , E ) G=(V,E) G=(V,E),其中 V = { v 1 , … , v n } V=\{v_1, \ldots,v_n\} V={v1,,vn} 为节点集合, E = { { v k , v w } , … , { v i , v j } } E=\{\{v_k,v_w\}, \ldots,\{v_i,v_j\} \} E={{vk,vw},,{vi,vj}}为边的集合,表示属于 V V V中两个节点的连接关系。

值得注意的是,每条边中的节点是无序的,即, { v k , v w } \{v_k,v_w\} {vk,vw}和{v_w,v_k}表示同一条边。

下面给出图和节点的一些基本定义和性质:

  • 图所有节点的数目 ∣ V ∣ |V| V被称为图的序(order),所有边的数目 ∣ E ∣ |E| E被称为图的大小(size)
  • 节点的度(drgee) 定义为邻接到该节点所有边的数目。
  • G G G中节点 v v v邻域(neighbors) 定义为邻接到 v v v的所有节点的子集 V ′ V' V
  • G G G中节点 v v v邻域图(neighborhood graph) (也称作自我图(ego graph) )是 G G G的子图,由所有与 v v v相邻的顶点和与 v v v相邻的顶点相连的边组成。

一个简单的图的例子如下:

V = {‘Dublin’, ‘Paris’, ‘Milan’, ‘Rome’}

E = [(‘Milan’,‘Dublin’), (‘Milan’,‘Paris’), (‘Paris’,‘Dublin’), (‘Milan’,‘Rome’)]

根据以上表示,由于没有方向,从米兰到巴黎的一条边等于从巴黎到米兰的一条边。因此,可以不受任何约束地向两个方向移动。

同样,可以看到该图的序和大小都是4(即有4个节点和4条边),节点Paris和Dublin的度都是2,节点Milan的度是3,节点Rome的度是1。每个节点的邻域如下:

  • Paris = {Milan, Dublin}
  • Milan = {Paris, Dublin, Rome}
  • Dublin = {Paris, Milan}
  • Rome = {Milan}

networkx画出该图的代码如下:

G = nx.Graph()
V = {'Dublin', 'Paris', 'Milan', 'Rome'}
E = [('Milan','Dublin'), ('Milan','Paris'), ('Paris','Dublin'), ('Milan','Rome')]
G.add_nodes_from(V)
G.add_edges_from(E)
draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500)

在这里插入图片描述

默认情况下,nx.Graph() 命令会生成一个无向图,所以我们不需要指定每条边的两个方向。

networkx 中,节点可以是任何可哈希对象:字符串、类,甚至其他networkx图。

现在让我们计算之前生成的图的一些性质。

# 运行以下代码可以得到图的所有节点和边
print(f"V = {G.nodes}")
print(f"E = {G.edges}")
V = ['Rome', 'Dublin', 'Milan', 'Paris']
E = [('Rome', 'Milan'), ('Dublin', 'Milan'), ('Dublin', 'Paris'), ('Milan', 'Paris')]
{G.degree(v): v for v in G.nodes}
{1: 'Rome', 2: 'Paris', 3: 'Milan'}

我们还可以计算图的序,大小,度和每个节点的邻域,使用以下命令:

print(f"Graph Order: {G.number_of_nodes()}")
print(f"Graph Size: {G.number_of_edges()}")
print(f"Degree for nodes: { {v: G.degree(v) for v in G.nodes} }")
print(f"Neighbors for nodes: { {v: list(G.neighbors(v)) for v in G.nodes} }")
Graph Order: 4
Graph Size: 4
Degree for nodes: {'Rome': 1, 'Dublin': 2, 'Milan': 3, 'Paris': 2}
Neighbors for nodes: {'Rome': ['Milan'], 'Dublin': ['Milan', 'Paris'], 'Milan': ['Dublin', 'Paris', 'Rome'], 'Paris': ['Milan', 'Dublin']}

最后,我们还可以为图 G G G计算一个特定节点的自我图,如下所示

ego_graph_milan = nx.ego_graph(G, "Milan")
print(f"Nodes: {ego_graph_milan.nodes}")
print(f"Edges: {ego_graph_milan.edges}")
Nodes: ['Rome', 'Dublin', 'Milan', 'Paris']
Edges: [('Rome', 'Milan'), ('Dublin', 'Milan'), ('Dublin', 'Paris'), ('Milan', 'Paris')]

原始图也可以通过添加新的节点和/或 边来修改,如下所示:

#Add new nodes and edges
new_nodes = {'London', 'Madrid'}
new_edges = [('London','Rome'), ('Madrid','Paris')]
G.add_nodes_from(new_nodes)
G.add_edges_from(new_edges)
print(f"V = {G.nodes}")
print(f"E = {G.edges}")
V = ['Rome', 'Dublin', 'Milan', 'Paris', 'Madrid', 'London']
E = [('Rome', 'Milan'), ('Rome', 'London'), ('Dublin', 'Milan'), ('Dublin', 'Paris'), ('Milan', 'Paris'), ('Paris', 'Madrid')]

可以通过运行以下代码来删除节点:

node_remove = {'London', 'Madrid'}
G.remove_nodes_from(node_remove)
print(f"V = {G.nodes}")
print(f"E = {G.edges}")
V = ['Rome', 'Dublin', 'Milan', 'Paris']
E = [('Rome', 'Milan'), ('Dublin', 'Milan'), ('Dublin', 'Paris'), ('Milan', 'Paris')]

正如预期的那样,包含被删除节点的所有边都会自动从边列表中删除。

同样,可以通过运行以下代码来删除边:

node_edges = [('Milan','Dublin'), ('Milan','Paris')]
G.remove_edges_from(node_edges)
print(f"V = {G.nodes}")
print(f"E = {G.edges}")
V = ['Rome', 'Dublin', 'Milan', 'Paris']
E = [('Rome', 'Milan'), ('Dublin', 'Paris')]
print(nx.to_edgelist(G)) #Returns a list of edges in the graph.
[('Rome', 'Milan', {}), ('Dublin', 'Paris', {})]
print(nx.to_pandas_adjacency(G)) #Returns the graph adjacency matrix as a Pandas DataFrame.
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    1.0    0.0
Dublin   0.0     0.0    0.0    1.0
Milan    1.0     0.0    0.0    0.0
Paris    0.0     1.0    0.0    0.0

2.2 其他类型的图

在前一节中,我们描述了如何创建和修改简单的无向图。

这里,我们将展示如何扩展这个基本数据结构,以便封装更多的信息,这要归功于有向图(有向图)、加权图和多重图的引入。

2.2.1 有向图 Directed Graph

有向图 G G G可以被表示为 G = ( V , E ) G=(V,E) G=(V,E),其中 V = { v 1 , … , v n } V=\{v_1, \ldots,v_n\} V={v1,,vn} 为节点集合, E = { { v k , v w } , … , { v i , v j } } E=\{\{v_k,v_w\}, \ldots,\{v_i,v_j\} \} E={{vk,vw},,{vi,vj}}为有序对的集合,表示属于 V V V中两个节点的连接关系。

由于 E E E的每个元素都是有序的对,它强调了连接的方向。 边 ( v k , v w ) (v_k,v_w) (vk,vw)表示从节点 v k v_k vk出发到达节点 v w v_w vw。这与 ( v w , v k ) (v_w,v_k) (vw,vk)不同,因为它意味着从节点 v w v_w vw v k v_k vk。起始节点 v w v_w vw称为头,结束节点 v w v_w vw称为尾。

由于边方向的存在,节点度的定义也需要扩展。

有向图的一个例子如下图所示:

从箭头可以看到边的方向,例如,Milan —>Dublin 的意思是从Milan到Dublin,各个节点的入度和出度分别为:

  • Dublin: d e g − ( v ) = 2 deg^-(v)= 2 deg(v)=2 d e g + ( v ) = 0 deg^+(v) = 0 deg+(v)=0
  • Paris: d e g − ( v ) = 0 deg^-(v)= 0 deg(v)=0 d e g + ( v ) = 2 deg^+(v) = 2 deg+(v)=2
  • Milan: d e g − ( v ) = 1 deg^-(v)= 1 deg(v)=1 d e g + ( v ) = 2 deg^+(v) = 2 deg+(v)=2
  • Rome: d e g − ( v ) = 1 deg^-(v)= 1 deg(v)=1 d e g + ( v ) = 0 deg^+(v) = 0 deg+(v)=0

同样的图可以用networkx表示如下:

import networkx as nx
G = nx.DiGraph()
V = {'Dublin', 'Paris', 'Milan', 'Rome'}
E = [('Milan','Dublin'), ('Paris','Milan'), ('Paris','Dublin'), ('Milan','Rome')]
G.add_nodes_from(V)
G.add_edges_from(E)
print(nx.to_pandas_edgelist(G))
print(nx.to_pandas_adjacency(G))
  source  target
0  Milan  Dublin
1  Milan    Rome
2  Paris   Milan
3  Paris  Dublin
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    0.0    0.0
Dublin   0.0     0.0    0.0    0.0
Milan    1.0     1.0    0.0    0.0
Paris    0.0     1.0    1.0    0.0

它的定义与简单无向图的定义相同;唯一的区别是用于实例化对象的networkx类。对于有向图,使用nx.DiGraph()类。

入度和出度的计算如下所示:

print(f"Indegree for nodes: { {v: G.in_degree(v) for v in G.nodes} }")
print(f"Outegree for nodes: { {v: G.out_degree(v) for v in G.nodes} }")
Indegree for nodes: {'Rome': 1, 'Dublin': 2, 'Milan': 1, 'Paris': 0}
Outegree for nodes: {'Rome': 0, 'Dublin': 0, 'Milan': 2, 'Paris': 2}
draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500)

在这里插入图片描述

2.2.2 多重图 Multigraph

现在我们将介绍multigraph对象,它是图定义的一般化,允许多条边拥有相同的开始节点和结束节点对。

多重图 G G G可以被表示为 G = ( V , E ) G=(V,E) G=(V,E),其中 V = { v 1 , … , v n } V=\{v_1, \ldots,v_n\} V={v1,,vn} 为节点集合, E E E为边的多重集合(集合中元素可重复)。

如果E是多重有序对集,则称为有向多重图;反之称为无向多重图。

有向多重图的一个例子如下:

在下面的代码片段中,我们展示了如何使用networkx来创建有向或无向多重图:

import networkx as nx
directed_multi_graph = nx.MultiDiGraph()
V = {'Dublin', 'Paris', 'Milan', 'Rome'}
E = [('Milan','Dublin'), ('Milan','Dublin'), ('Paris','Milan'), ('Paris','Dublin'), ('Milan','Rome'), ('Milan','Rome')]
directed_multi_graph.add_nodes_from(V)
directed_multi_graph.add_edges_from(E)

# 用draw_graph 画多重图有点问题,无法显示多重边
draw_graph(directed_multi_graph, pos_nodes=nx.shell_layout(G), node_size=500)

在这里插入图片描述

print(nx.to_pandas_edgelist(directed_multi_graph))
print(nx.to_pandas_adjacency(directed_multi_graph))
  source  target
0  Milan  Dublin
1  Milan  Dublin
2  Milan    Rome
3  Milan    Rome
4  Paris   Milan
5  Paris  Dublin
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    0.0    0.0
Dublin   0.0     0.0    0.0    0.0
Milan    2.0     2.0    0.0    0.0
Paris    0.0     1.0    1.0    0.0
import networkx as nx
undirected_multi_graph = nx.MultiGraph()
V = {'Dublin', 'Paris', 'Milan', 'Rome'}
E = [('Milan','Dublin'), ('Milan','Dublin'), ('Paris','Milan'), ('Paris','Dublin'), ('Milan','Rome'), ('Milan','Rome')]
undirected_multi_graph.add_nodes_from(V)
undirected_multi_graph.add_edges_from(E)

draw_graph(undirected_multi_graph, pos_nodes=nx.shell_layout(G), node_size=500)

在这里插入图片描述

print(nx.to_pandas_edgelist(undirected_multi_graph))
print(nx.to_pandas_adjacency(undirected_multi_graph))
   source target
0    Rome  Milan
1    Rome  Milan
2  Dublin  Milan
3  Dublin  Milan
4  Dublin  Paris
5   Milan  Paris
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    2.0    0.0
Dublin   0.0     0.0    2.0    1.0
Milan    2.0     2.0    0.0    1.0
Paris    0.0     1.0    1.0    0.0

有向多重图和无向多重图之间的唯一区别是在创建了两个不同的对象:nx.MultiGraph()用于创建有向multigraph,而nx.MultiGraph()用于构建无向multigraph。用于添加节点和边的函数对于这两个对象是相同的。

2.2.3 加权有向图 Weighted Directed Graph

边加权图(加权图) G G G可以被表示为 G = ( V , E , w ) G=(V,E,w) G=(V,E,w),其中 V = { v 1 , … , v n } V=\{v_1, \ldots,v_n\} V={v1,,vn} 为节点集合, E E E为边集合, w : E → R w:E \rightarrow \mathbb{R} w:ER为加权函数,将每条边 e ∈ E e \in E eE 分配一个实数权重。

节点加权图 G G G可以被表示为 G = ( V , E , w ) G=(V,E,w) G=(V,E,w),其中 V = { v 1 , … , v n } V=\{v_1, \ldots,v_n\} V={v1,,vn} 为节点集合, E E E为边集合, w : V → R w:V \rightarrow \mathbb{R} w:VR为加权函数,为每个节点 v ∈ V v \in V vV 分配一个实数权重。

注意:

  • 如果E是一组有序对集合,那么我们称它为有向加权图
  • 如果E是无序对集合,那么我们称它为无向加权图
  • 如果E是一个多重边集,我们称它为一个无向加权多重图
  • 如果E是一个多重有序边,则它是一个有向加权多重图

一个有向加权边图的例子如下:

import networkx as nx
G = nx.MultiDiGraph()
V = {'Paris', 'Dublin','Milan', 'Rome'}
E = [ ('Paris','Dublin', 11), ('Paris','Milan', 8),
     ('Milan','Rome', 5),('Milan','Dublin', 19)]
G.add_nodes_from(V)
G.add_weighted_edges_from(E)
draw_graph(G, pos_nodes=nx.shell_layout(G), node_size=500, plot_weight=True)
print(nx.to_pandas_edgelist(G))
print(nx.to_pandas_adjacency(G))
  source  target  weight
0  Milan    Rome       5
1  Milan  Dublin      19
2  Paris  Dublin      11
3  Paris   Milan       8
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    0.0    0.0
Dublin   0.0     0.0    0.0    0.0
Milan    5.0    19.0    0.0    0.0
Paris    0.0    11.0    8.0    0.0

在这里插入图片描述

print(nx.to_pandas_edgelist(G))
print(nx.to_pandas_adjacency(G))
  source  target  weight
0  Milan    Rome       5
1  Milan  Dublin      19
2  Paris  Dublin      11
3  Paris   Milan       8
        Rome  Dublin  Milan  Paris
Rome     0.0     0.0    0.0    0.0
Dublin   0.0     0.0    0.0    0.0
Milan    5.0    19.0    0.0    0.0
Paris    0.0    11.0    8.0    0.0

很容易看出图上权重的存在如何帮助向数据结构添加有用的信息。实际上,我们可以把边权值想象成从另一个节点到达一个节点的“代价”。例如,从Milan到达Dublin的“成本”是19,而从Paris到达Dublin 的“成本”是11。

2.2.4 二部图 Bipartite Graph

现在我们将介绍另一种类型的图,它将在本节中使用:多部图。

二部图和三部图——更普遍地说,是 k k k部图——它们的顶点可以分别划分为两个、三个或更多的第 k k k$个节点集。

边只允许跨不同的集合,而不允许在属于同一集合的节点内。

在大多数情况下,属于不同集合的节点也具有特定的节点类型。

在第7章,利用图进行文本分析和自然语言处理和第八章信用卡交易的图分析中,我们将处理一些基于图的应用程序的实际例子,你将看到多图是如何在几种情况下出现的——例如,在以下场景中:

  • 处理文档并在文档和出现在文档中的实体的二部图中构造信息时;
  • 在处理交易数据时,为了对买家和商家之间的关系进行编码。

networkx中,用以下代码可以很容易地创建一个二部图:

n_nodes = 10
n_edges = 12
bottom_nodes = [ith for ith in range(n_nodes) if ith % 2 ==0]
top_nodes = [ith for ith in range(n_nodes) if ith % 2 ==1]
iter_edges = zip(
    np.random.choice(bottom_nodes, n_edges),  
    np.random.choice(top_nodes, n_edges))
edges = pd.DataFrame([
    {"source": a, "target": b} for a, b in iter_edges])
B = nx.Graph()
B.add_nodes_from(bottom_nodes, bipartite=0)
B.add_nodes_from(top_nodes, bipartite=1)
B.add_edges_from([tuple(x) for x in edges.values])

网络也可以使用networkx的bipartite_layout实用函数来方便地绘制,如下代码片段所示:

from networkx.drawing.layout import bipartite_layout
pos = bipartite_layout(B, bottom_nodes)
nx.draw_networkx(B, pos=pos)

在这里插入图片描述

三、 图表示 Graph representation

如前几节所述,使用networkx,我们可以使用节点和边对象来定义和操作图形。在不同的实例中,这样的表示不会很容易处理。

在本节中,我们将展示两种方法来执行图数据结构的紧凑表示—即邻接矩阵(adjacency matrix)边列表(edge list)

3.1 邻接矩阵(adjacency matrix)

G = ( V , E ) G=(V,E) G=(V,E)邻接矩阵 M M M是一个大小为 ∣ V ∣ × ∣ V ∣ |V|\times |V| V×V的方阵,若存在从节点 i i i j j j的边,则 M i j = 1 M_{ij}=1 Mij=1,否则 M i j = 0 M_{ij}=0 Mij=0

  • 容易看出,由于没有定义边的方向,因此无向图的邻接矩阵总是对称的。

  • 相反,由于在边方向上存在约束,有向图的邻接矩阵的对称性不能得到保证。

  • 对于多重图,我们可以使用大于1的值,因为多个边可以用来连接相同的一对节点。

  • 对于加权图,特定单元格中的值等于连接两个节点的边的权值。

networkx中,给定图的邻接矩阵可以用两种不同的方法计算。nx.to_pandas_adjacency 将邻接矩阵表示成pandas数据结构,而nx.to_numpy_matrix将邻接矩阵表示成numpy矩阵。如下所示:

nx.to_pandas_adjacency(G) #adjacency matrix as pd DataFrame
	Rome	Dublin	Milan	Paris
Rome	0.0	0.0	0.0	0.0
Dublin	0.0	0.0	0.0	0.0
Milan	5.0	19.0	0.0	0.0
Paris	0.0	11.0	8.0	0.0
nx.to_numpy_matrix(G) #adjacency matrix as numpy matrix
matrix([[ 0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.],
        [ 5., 19.,  0.,  0.],
        [ 0., 11.,  8.,  0.]])

由于numpy矩阵不能表示节点的名称,邻接矩阵中元素的顺序是 G.nodes 列表中定义的顺序。

3.2 边列表

和邻接矩阵一样,边表也是表示图的另一种简洁的方法。 这种格式背后的思想是将一个图表示为一个边列表。

G = ( V , E ) G=(V,E) G=(V,E)边列表 L L L是一个大小为 ∣ E ∣ |E| E的列表,其元素 L i L_i Li表示边 i i i的头和尾节点。

在下面的代码片段中,我们展示了如何在networkx中计算简单无向图G的边列表。

print(nx.to_pandas_edgelist(G))
 source  target  weight
0  Milan    Rome       5
1  Milan  Dublin      19
2  Paris  Dublin      11
3  Paris   Milan       8

在networkx中也有其他表示方法,我们将不详细讨论。例如nx.to_dict_of_dicts(G)nx.to_numpy_ array(G)等等。

四、 图绘制

正如我们在前几节中看到的,图是用图形表示的直观数据结构。节点可以被画成简单的圆,而边则是连接两个节点的线。

尽管它们很简单,但当边和节点的数量增加时,可能很难做出清晰的表示。这种复杂性的来源主要与在最终绘图中分配给每个节点的位置(空间/笛卡尔坐标)有关。实际上,手工将每个节点在最终图中的具体位置分配给有数百个节点的图是不可行的。

在本节中,我们将看到如何在不为每个节点指定坐标的情况下绘制图形。我们将利用两种不同的解决方案:networkx和Gephi。

4.1 networkx

Networkx通过 nx.draw 提供了一个简单的接口来绘制图形对象。在下面的代码片段中,我们展示了如何使用库来绘制图形:

def draw_graph(G, pos_position, plot_weight):
    nx.draw(G, pos_position, with_labels=True, font_size=15, node_size=400, edge_color='gray', arrowsize=30)
    if plot_weight:
        edge_labels=nx.get_edge_attributes(G,'weight')
        nx.draw_networkx_edge_labels(G, pos_position, edge_labels=edge_labels)

在这里,nodes_position是一个字典,其中键是节点,赋给每个键的值是一个长度为2的数组,使用笛卡尔坐标绘制特定节点。

nx.Draw函数通过将节点放在给定的位置来绘制整个图形。with_labels选项将用特定的font_size值将其名称绘制在每个节点的顶部。Node_size和edge_color将分别指定圆的大小,代表节点和边的颜色。最后,arrowsize将定义有向边的箭头大小。当要绘制的图形是有向图时,将使用此选项。

在下面的代码示例中,我们展示了如何使用之前定义的draw_graph函数来绘制图形:

G = nx.Graph()
V = {'Paris', 'Dublin','Milan', 'Rome'}
E = [('Paris','Dublin', 11), ('Paris','Milan', 8), ('Milan','Rome', 5), ('Milan','Dublin', 19)]
G.add_nodes_from(V)
G.add_weighted_edges_from(E)
node_position = {"Paris": [0,0], "Dublin": [0,1], "Milan":[1,0], "Rome": [1,1]}
draw_graph(G, node_position, True)

在这里插入图片描述

前面描述的方法很简单,但在实际场景中无法使用,因为node_position值可能很难确定。为了解决这个问题,networkx提供了不同的功能,可以根据不同的布局(layouts)自动计算每个节点的位置。

networkx提供了一系列可用的不同布局的无向图,可直接调用相关命令获取。我们只需要将node_position分配给我们想要使用的布局的结果—例如,node_position = nx_circular_layout(G)即可获得一个圆形布局的图。

Networkx是一个很好的工具,可以很容易地操作和分析图形,但是它没有提供很好的功能来执行复杂而美观的图形图。在下一节中,我们将研究另一个执行复杂图形可视化的工具:Gephi。

4.2 Gephi


总结

未完待续

举报

相关推荐

Machine-Learning 机器学习

0 条评论