教程 (西班牙语)

本页面是 igraph 针对 Python 功能的详细教程。要快速了解 igraph 可以做什么,请参阅快速入门。如果您尚未安装 igraph,请遵循安装 igraph 中的说明。

注意

对于不耐烦的读者,请参阅示例图库页面,获取简短的、自包含的示例。

igraph 入门

使用 igraph 最常见的方式是在 Python 环境中(例如,一个简单的 Python shell、一个 IPython shell、一个 Jupyter notebook 或 JupyterLab 实例、Google Colab 或一个 IDE)将其作为命名导入:

$ python
Python 3.9.6 (default, Jun 29 2021, 05:25:02)
[Clang 12.0.5 (clang-1205.0.22.9)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import igraph as ig

要调用函数,需要加上前缀 ig(或您选择的名称):

>>> import igraph as ig
>>> print(ig.__version__)
0.9.8

注意

可以使用 星号导入 来导入 igraph

>>> from igraph import *

但通常不建议这样做 https://stackoverflow.com/questions/2386714/why-is-import-bad

还有第二种启动 igraph 的方式,即从您的终端调用 igraph 脚本:

$ igraph
No configuration file, using defaults
igraph 0.9.6 running inside Python 3.9.6 (default, Jun 29 2021, 05:25:02)
Type "copyright", "credits" or "license" for more information.
>>>

注意

Windows 用户会在 Python 的 file:scripts 子目录中找到该脚本,并且可能需要手动将其添加到路径中。

此脚本启动一个合适的命令行解释器(如果找到,则为 IPythonIDLE,否则为纯 Python 解释器)并使用 星号导入(参见上文)。这有时方便使用 igraph 的功能。

注意

您可以通过 igraph配置指定此脚本应使用哪个 shell。

本教程将假定您已使用命名 ig 导入 igraph。

创建图

创建图最简单的方法是使用 Graph 构造函数。要创建一个空图:

>>> g = ig.Graph()

要创建一个包含 10 个节点(编号 09)和连接节点 0-10-5 的两条边的图:

>>> g = ig.Graph(n=10, edges=[[0, 1], [0, 5]])

我们可以打印图来获取其节点和边的摘要:

>>> print(g)
IGRAPH U--- 10 2 --
+ edges:
0--1 0--5

我们得到:无向图 (Undirected),具有 10 个顶点和 2 条边,这些边在最后部分列出。如果图有一个“名称”属性,它也会被打印出来。

注意

summary 类似于 print,但不列出边,这对于包含数百万条边的大图很方便:

>>> summary(g)
IGRAPH U--- 10 2 --

添加和删除顶点与边

让我们从一个空图重新开始。要向现有图添加顶点,请使用 Graph.add_vertices()

>>> g = ig.Graph()
>>> g.add_vertices(3)

igraph 中,顶点总是从零开始编号。顶点的编号是其 顶点 ID。一个顶点可以有名称也可以没有。

类似地,要添加边,请使用 Graph.add_edges()

>>> g.add_edges([(0, 1), (1, 2)])

通过指定每条边的源顶点和目标顶点来添加边。此调用添加两条边,一条连接顶点 01,另一条连接顶点 12。边也从零开始编号(边 ID)并具有可选名称。

警告

如上所示创建空图并添加顶点和边可能比以前所示的创建包含顶点和边的图慢得多。如果速度是一个问题,您应该特别避免 逐个 添加顶点和边。如果您仍然需要这样做,可以使用 Graph.add_vertex()Graph.add_edge()

如果您尝试向无效 ID 的顶点添加边(例如,当图只有三个顶点时,您尝试向顶点 5 添加边),您会得到一个 igraph.InternalError 错误:

>>> g.add_edges([(5, 4)])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.10/site-packages/igraph/__init__.py", line 376, in add_edges
    res = GraphBase.add_edges(self, es)
igraph._igraph.InternalError: Error at src/graph/type_indexededgelist.c:270: cannot add edges. -- Invalid vertex id

该消息试图解释哪里出了问题(cannot add edges. -- Invalid vertex id)以及发生错误的相应源代码行。

注意

完整的跟踪,包括源代码信息,在向我们的 GitHub 问题页面报告错误时很有用。如果您创建新问题,请完整包含它。

让我们向图中添加更多顶点和边:

>>> g.add_edges([(2, 0)])
>>> g.add_vertices(3)
>>> g.add_edges([(2, 3), (3, 4), (4, 5), (5, 3)])
>>> print(g)
IGRAPH U---- 6 7 --
+ edges:
0--1 1--2 0--2 2--3 3--4 4--5 3--5

现在我们有一个包含 6 个顶点和 7 条边的无向图。顶点和边的 ID 总是 连续的,因此如果您删除一个顶点,所有后续的顶点都将重新编号。当一个顶点被重新编号时,边 不会 被重新编号,但它们的源和目标顶点会被重新编号。使用 Graph.delete_vertices()Graph.delete_edges() 来执行这些操作。例如,要删除连接顶点 2-3 的边,获取其 ID,然后删除它们:

>>> g.get_eid(2, 3)
3
>>> g.delete_edges(3)

生成图

igraph 包含确定性生成器和随机性生成器。确定性 生成器每次调用函数时都会生成相同的图,例如:

>>> g = ig.Graph.Tree(127, 2)
>>> summary(g)
IGRAPH U--- 127 126 --

使用 Graph.Tree() 生成一个具有 127 个顶点、每个顶点有两个子节点(当然还有一个父节点)的正则树形图。无论您调用 Graph.Tree() 多少次,如果您使用相同的参数,生成的图将始终相同:

>>> g2 = ig.Graph.Tree(127, 2)
>>> g2.get_edgelist() == g.get_edgelist()
True

上面的代码片段还展示了 get_edgelist() 方法,它返回所有边的源和目标顶点列表,按边 ID 排序。如果您打印前 10 个元素,您会得到:

>>> g2.get_edgelist()[:10]
[(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6), (3, 7), (3, 8), (4, 9), (4, 10)]

随机性 生成器每次都会生成不同的图;例如,Graph.GRG()

>>> g = ig.Graph.GRG(100, 0.2)
>>> summary(g)
IGRAPH U---- 100 516 --
+ attr: x (v), y (v)

注意

+ attr 显示顶点 (v) 和边 (e) 的属性,在此例中是两个顶点属性,没有边属性。

这生成一个随机几何图:从单位正方形内随机均匀地选择 n 个点,并且根据预定义的距离 d,相互之间距离最近的点对通过边连接起来。如果用相同的参数生成 GRG,它们将不同:

>>> g2 = ig.Graph.GRG(100, 0.2)
>>> g.get_edgelist() == g2.get_edgelist()
False

一种更宽松的检查图是否等效的方法是使用 isomorphic()

>>> g.isomorphic(g2)
False

检查同构性可能需要一些时间,对于大型图来说更是如此(在本例中,可以通过检查两个图的度分布来快速给出答案)。

设置和获取属性

如前所述,在 igraph 中,每个顶点和每条边都有一个从 0 开始的数字 ID。因此,删除顶点或边可能会导致顶点和/或边 ID 的重新分配。除了 ID,顶点和边还可以有 属性,例如名称、绘图坐标、元数据和权重。图本身也可以有这些属性(例如,一个名称,将在 printGraph.summary() 中显示)。从某种意义上说,每个 Graph、顶点和边都可以用作 Python 字典来存储和检索这些属性。

为了演示属性的使用,让我们创建一个简单的社交网络:

>>> g = ig.Graph([(0,1), (0,2), (2,3), (3,4), (4,2), (2,5), (5,0), (6,3), (5,6)])

每个顶点代表一个人,因此我们希望存储姓名、年龄和性别:

>>> g.vs["name"] = ["Alice", "Bob", "Claire", "Dennis", "Esther", "Frank", "George"]
>>> g.vs["age"] = [25, 31, 18, 47, 22, 23, 50]
>>> g.vs["gender"] = ["f", "m", "f", "m", "f", "m", "m"]
>>> g.es["is_formal"] = [False, False, True, True, True, False, True, False, False]

Graph.vsGraph.es 是分别获取所有顶点和边的序列的标准方法。该值必须是与顶点(对于 Graph.vs)或边(对于 Graph.es)长度相同的列表。这会一次性为 所有 顶点/边分配属性。

要为单个顶点/边分配或修改属性,您可以这样做:

>>> g.es[0]["is_formal"] = True

实际上,单个顶点由 Vertex 类表示,单个边由 Edge 表示。两者,连同 Graph,都可以像字典一样被索引以设置属性,例如,向图中添加日期:

>>> g["date"] = "2009-01-10"
>>> print(g["date"])
2009-01-10

要检索属性字典,可以使用 Graph.attributes()Vertex.attributes()Edge.attributes()

此外,每个 Vertex 都有一个特殊属性 Vertex.index,用于查询顶点的 ID。每个 Edge 都有 Edge.index 加上两个额外属性:Edge.sourceEdge.target,用于查找此边连接的顶点的 ID。要同时获取这两个属性,可以使用 Edge.tuple

要为顶点或边的子集分配属性,可以使用切片:

>>> g.es[:1]["is_formal"] = True

g.es[:1] 的输出是一个 EdgeSeq 实例,而 VertexSeq 是表示顶点子集的等效类。

要删除属性,可以使用 del,例如:

>>> g.vs[3]["foo"] = "bar"
>>> g.vs["foo"]
[None, None, None, 'bar', None, None, None]
>>> del g.vs["foo"]
>>> g.vs["foo"]
Traceback (most recent call last):
  File "<stdin>", line 25, in <module>
KeyError: 'Attribute does not exist'

警告

属性可以是任意的 Python 对象,但是当您将图保存到文件中时,只有字符串和数字属性会被保留。如果您正在寻找保存其他类型属性的方法,请参阅 Python 标准库的 pickle 模块。您可以单独对属性进行 pickle,将其存储为字符串并保存,或者如果您知道要在 Python 中加载图,则可以对整个 Graph 进行 pickle。

图的结构属性

除了上述简单的图和属性操作功能,igraph 提供了一套全面的方法来计算图的各种结构属性。本文档无法涵盖所有这些方法,因此本节仅出于说明目的介绍其中一些。我们将使用我们在上一节中构建的小型社交网络。

可能最简单的属性是“顶点度”(vertex degree)。一个顶点的度等于连接到它的边的数量。对于有向图,我们还可以定义入度in-degree,指向该顶点的边的数量)和出度out-degree,从该顶点发出的边的数量):

>>> g.degree()
[3, 1, 4, 3, 2, 3, 2]

如果图是有向的,我们可以使用 g.degree(mode="in")g.degree(mode="out") 分别计算入度和出度。您也可以向 degree() 传递单个顶点 ID 或顶点 ID 列表,如果您只想计算顶点子集的度数:

>>> g.degree(6)
2
>>> g.degree([2,3,4])
[4, 3, 2]

这个过程适用于 igraph 可以计算的大多数结构属性。对于顶点属性,方法接受一个顶点 ID 或一个顶点 ID 列表(如果省略,默认为所有顶点的集合)。对于边属性,方法也接受一个边 ID 或一个边 ID 列表。除了 ID 列表,您还可以提供一个适当的 VertexSeq 实例或一个 EdgeSeq 实例。稍后,在下一章“顶点和边的查询”中,您将学习如何将其精确限制到您想要的顶点或边。

注意

在某些情况下,仅对少量顶点或边进行计算是没有意义的,因为无论如何都需要相同的时间。在这种情况下,方法不接受顶点或边 ID,但您可以使用标准的索引和切片运算符在稍后限制结果列表。一个例子是特征向量中心性(Graph.evcent()):

除了度数之外,igraph 还包含用于计算许多其他中心性属性的内置例程,例如顶点和边的中介中心性或 Google 的 PageRank(Graph.pagerank()),仅举几例。这里我们只演示边的中介中心性:

>>> g.edge_betweenness()
[6.0, 6.0, 4.0, 2.0, 4.0, 3.0, 4.0, 3.0. 4.0]

现在我们还可以通过一些 Python 魔法来找出哪些连接具有最高的中介中心性:

>>> ebs = g.edge_betweenness()
>>> max_eb = max(ebs)
>>> [g.es[idx].tuple for idx, eb in enumerate(ebs) if eb == max_eb]
[(0, 1), (0, 2)]

大多数结构属性也可以通过调用感兴趣的 VertexSeqEdgeSeq 类上的相应方法来获取顶点或边子集或单个顶点或边的属性:

>>> g.vs.degree()
[3, 1, 4, 3, 2, 3, 2]
>>> g.es.edge_betweenness()
[6.0, 6.0, 4.0, 2.0, 4.0, 3.0, 4.0, 3.0. 4.0]
>>> g.vs[2].degree()
4

基于属性的顶点和边查询

选择顶点和边

以前面创建的社交网络为例,您可能想找出谁拥有最高的度数或中介中心性。您可以使用迄今为止介绍的工具和基本的 Python 知识来做到这一点,但是由于根据属性或结构特性选择顶点和边是一项常见任务,igraph 提供了一种更简单的方法来完成它:

>>> g.vs.select(_degree=g.maxdegree())["name"]
['Claire']

这个语法乍一看可能有点奇怪,所以我们来一步步解释。meth:~VertexSeq.selectVertexSeq 的一个方法,其唯一目的是根据单个顶点的属性过滤 VertexSeq。它过滤顶点的方式取决于其位置参数和关键字参数。位置参数(那些没有像 _degree 这样显式名称的参数)总是按以下方式在关键字参数之前处理:

  • 如果第一个位置参数是 None,则返回一个空序列(不包含任何顶点):

    >>> seq = g.vs.select(None)
    >>> len(seq)
    0
    
  • 如果第一个位置参数是一个可调用对象(即一个函数、一个绑定方法或任何行为像函数的东西),该对象将对序列中当前存在的每个顶点进行调用。如果函数返回 True,则该顶点将被包含;否则将被排除:

    >>> graph = ig.Graph.Full(10)
    >>> only_odd_vertices = graph.vs.select(lambda vertex: vertex.index % 2 == 1)
    >>> len(only_odd_vertices)
    5
    
  • 如果第一个位置参数是可迭代的(即一个列表、一个生成器或任何可以迭代的东西),它 必须 返回整数,并且这些整数将被视为当前顶点集(不 一定 是整个图)的索引。只有与给定索引匹配的顶点才会被包含在过滤后的顶点集中。浮点数、字符串和无效顶点 ID 将被忽略:

    >>> seq = graph.vs.select([2, 3, 7])
    >>> len(seq)
    3
    >>> [v.index for v in seq]
    [2, 3, 7]
    >>> seq = seq.select([0, 2])         # filtering an existing vertex set
    >>> [v.index for v in seq]
    [2, 7]
    >>> seq = graph.vs.select([2, 3, 7, "foo", 3.5])
    >>> len(seq)
    3
    
  • 如果第一个位置参数是一个整数,则所有其他参数也应该都是整数,并且它们被解释为当前顶点集的索引。这只是“语法糖”,通过传递一个列表作为第一个位置参数可以达到类似的效果,这样就可以省略方括号:

    >>> seq = graph.vs.select(2, 3, 7)
    >>> len(seq)
    3
    

关键字参数可用于根据顶点的属性或结构特性进行过滤。每个关键字参数的名称最多由两部分组成:属性或结构特性的名称和过滤操作符。操作符可以省略;在这种情况下,默认会自动假定为相等操作符。可能性如下(其中 name 表示属性或特性的名称):

关键字参数

含义

name_eq

属性/属性的值必须 等于

name_ne

属性/属性的值必须 不等于

name_lt

属性/属性的值必须 小于

name_le

属性/属性的值必须 小于或等于

name_gt

属性/属性的值必须 大于

name_ge

属性/属性的值必须 大于或等于

name_in

属性/属性的值必须 包含于,在这种情况下必须是一个序列

name_notin

属性/属性的值必须 不包含于,在这种情况下必须是一个序列

例如,以下命令会给出我们假想社交网络中年龄小于 30 岁的人:

>>> g.vs.select(age_lt=30)

注意

由于 Python 的语法限制,不能使用更简单的 g.vs.select(edad < 30) 语法,因为在 Python 中,只有相等运算符才允许出现在参数列表中。

为了节省一些键入,您甚至可以省略 select() 方法:

>>> g.vs(age_lt=30)

还有一些特殊的结构属性用于选择边:

  • 使用 _source_from 根据边起始的顶点进行筛选。例如,要选择所有来自 Claire(顶点索引为 2)的边:

    >>> g.es.select(_source=2)
    
  • 根据目标顶点使用 _target_to 过滤器。如果图是有向的,这与 _source_from 不同。

  • _within 接受一个 VertexSeq 对象或一个顶点集合,并选择所有起始和终止于给定顶点集合的边。例如,以下表达式选择 Claire(索引 2)、Dennis(索引 3)和 Esther(索引 4)之间的所有边:

    >>> g.es.select(_within=[2,3,4])
    
  • _between 接受一个元组,由两个 VertexSeq 对象或包含顶点索引的列表或一个 Vertex 对象组成,并选择所有源自其中一个集合并终止于另一个集合的边。例如,要选择所有连接男性和女性的边:

    >>> men = g.vs.select(gender="m")
    >>> women = g.vs.select(gender="f")
    >>> g.es.select(_between=(men, women))
    

查找具有某些属性的单个顶点或边

在许多情况下,我们会在图中查找具有某些属性的单个顶点或边,而不在意返回的是哪一个匹配项,无论是否存在多个匹配项,或者我们事先知道只会有一个匹配项。一个典型的例子是根据其 name 属性查找顶点。VertexSeqEdgeSeq 对象为这些情况提供了 find() 方法。此方法的工作方式类似于 select(),但如果存在多个结果,则只返回第一个匹配项,如果没有找到匹配项,则抛出异常。例如,要查找对应于 Claire 的顶点,可以这样做:

>>> claire = g.vs.find(name="Claire")
>>> type(claire)
igraph.Vertex
>>> claire.index
2

搜索一个未知名称会导致异常:

>>> g.vs.find(name="Joe")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: no such vertex

按名称查找顶点

按名称查找顶点是一项非常常见的操作,通常记住图的顶点名称比记住它们的 ID 容易得多。为此,igraph 特别处理顶点的 name 属性;它们被索引,以便可以按名称查找顶点。为了更简单,igraph 在任何期望指定顶点 ID 的地方(几乎)都接受顶点名称,甚至在任何期望顶点 ID 列表的地方也接受顶点名称集合(元组、列表等)。例如,您可以按以下方式查找 Dennis 的度数(连接数):

>>> g.degree("Dennis")
3

或换一种方式:

>>> g.vs.find("Dennis").degree()
3

顶点名称和 ID 之间的映射由 igraph 在后台透明地维护;每次图发生变化时,igraph 也会更新内部映射。但是,顶点的名称 强制要求唯一性;您可以轻松创建两个顶点具有相同名称的图,但 igraph 在按名称搜索时只会返回其中一个,另一个只能通过其索引访问。

将图视为邻接矩阵

邻接矩阵是形成图的另一种方式。在邻接矩阵中,行和列由图的顶点标记:矩阵元素表示顶点 ij 是否具有共同的边 (i, j)。我们假想的社交网络的邻接矩阵是:

>>> g.get_adjacency()
Matrix([
  [0, 1, 1, 0, 0, 1, 0],
  [1, 0, 0, 0, 0, 0, 0],
  [1, 0, 0, 1, 1, 1, 0],
  [0, 0, 1, 0, 1, 0, 1],
  [0, 0, 1, 1, 0, 0, 0],
  [1, 0, 1, 0, 0, 0, 1],
  [0, 0, 0, 1, 0, 1, 0]
])

例如,Claire([1, 0, 0, 1, 1, 1, 0])直接连接到 Alice(索引 0)、Dennis(索引 3)、Esther(索引 4)和 Frank(索引 5),但未连接到 Bob(索引 1)和 George(索引 6)。

布局与绘图

图是一个抽象的数学对象,没有特定的2D或3D空间表示。这意味着当我们想要可视化一个图时,我们必须首先找到顶点到2D或3D空间中坐标的映射,最好是以一种美观的方式。图论的一个独立分支,称为图绘制,旨在通过各种图布局算法来解决这个问题。igraph 实现了多种布局算法,并且能够使用 Cairo 库将其绘制到屏幕或 PDF、PNG 或 SVG 文件中。

重要

要遵循本节中的示例,需要 Python 或 Matplotlib 中的 Cairo 库。

布局算法

igraph 中的布局方法位于 Graph 对象中,并且总是以 layout_ 开头。下表对其进行了总结:

方法名称

简称

算法描述

layout_circle

circle, circular

将顶点放置在圆上的确定性布局

layout_drl

drl

大型图的 [分布式递归布局] 算法

layout_fruchterman_reingold

fr

Fruchterman-Reingold 有向算法

layout_fruchterman_reingold_3d

fr3d, fr_3d

三维 Fruchterman-Reingold 有向算法

layout_kamada_kawai

kk

Kamada-Kawai 有向算法

layout_kamada_kawai_3d

kk3d, kk_3d

三维 Kamada-Kawai 有向算法

layout_lgl

large, lgl, large_graph

大型图的 [Large Graph Layout] 算法

layout_random

random

完全随机放置顶点

layout_random_3d

random_3d

在三维空间中完全随机放置顶点

layout_reingold_tilford

rt, tree

Reingold-Tilford 树布局,适用于(近似)树形图

layout_reingold_tilford_circular

rt_circular

tree

Reingold-Tilford 树布局,带极坐标后处理,适用于(近似)树形图

layout_sphere

sphere, spherical, circular_3d

将顶点均匀地放置在球体表面上的确定性布局

布局算法可以直接调用,也可以使用 layout() 调用:

>>> layout = g.layout_kamada_kawai()
>>> layout = g.layout("kamada_kawai")

layout() 方法的第一个参数必须是布局算法的短名称(参见上表)。所有其他位置参数和关键字参数都将完整地传递给所选的布局方法。例如,以下两个调用是完全等效的:

>>> layout = g.layout_reingold_tilford(root=[2])
>>> layout = g.layout("rt", [2])

布局方法返回一个 Layout 对象,它主要表现为列表的列表。 Layout 对象中的每个列表条目都对应于原始图中的一个顶点,并包含该顶点在 2D 或 3D 空间中的坐标。Layout 对象还包含一些有用的方法,用于批量平移、缩放或旋转坐标。然而,Layout 对象的主要用途是,您可以将其与图一起传递给 plot() 函数以获得 2D 绘图。

使用布局绘制图

例如,我们可以使用 Kamada-Kawai 布局算法绘制我们假想的社交网络,如下所示:

>>> layout = g.layout("kk")
>>> ig.plot(g, layout=layout)

这应该会打开一个外部图像查看器,显示网络的视觉表示,类似于下图所示(尽管节点的精确位置在您的机器上可能有所不同,因为布局是非确定性的):

The visual representation of our social network (Cairo backend)

我们的社交网络,使用 Kamada-Kawai 布局算法

如果您更喜欢使用 matplotlib 作为绘图引擎,请创建一个轴并使用 target 参数:

>>> import matplotlib.pyplot as plt
>>> fig, ax = plt.subplots()
>>> ig.plot(g, layout=layout, target=ax)
The visual representation of our social network (matplotlib backend)

嗯,这目前还不太美观。一个简单的补充是使用名称作为顶点标签,并根据性别对顶点进行着色。顶点标签默认取自 label 属性,顶点颜色由 color 属性决定:

>>> g.vs["label"] = g.vs["name"]
>>> color_dict = {"m": "blue", "f": "pink"}
>>> g.vs["color"] = [color_dict[gender] for gender in g.vs["gender"]]
>>> ig.plot(g, layout=layout, bbox=(300, 300), margin=20)  # Cairo backend
>>> ig.plot(g, layout=layout, target=ax)  # matplotlib backend

请注意,这里我们只是重用了以前的布局对象,但我们也指定了需要一个更小的图形(300 x 300 像素)以及图形周围更大的边距以容纳标签(20 像素)。结果是:

The visual representation of our social network - with names and genders

我们的社交网络——名称为标签,性别为颜色

对于 matplotlib:

The visual representation of our social network - with names and genders

除了将视觉属性指定为顶点和边属性外,您还可以将它们作为参数传递给 plot()

>>> color_dict = {"m": "blue", "f": "pink"}
>>> ig.plot(g, layout=layout, vertex_color=[color_dict[gender] for gender in g.vs["gender"]])

后一种方法是更可取的,如果您希望将图形的视觉表示属性与图形本身分开。您可以简单地创建一个包含您将传递给 plot() 函数的关键字参数的 Python 字典,然后使用双星号(**)将您的特定样式属性传递给 plot()

>>> visual_style = {}
>>> visual_style["vertex_size"] = 20
>>> visual_style["vertex_color"] = [color_dict[gender] for gender in g.vs["gender"]]
>>> visual_style["vertex_label"] = g.vs["name"]
>>> visual_style["edge_width"] = [1 + 2 * int(is_formal) for is_formal in g.es["is_formal"]]
>>> visual_style["layout"] = layout
>>> visual_style["bbox"] = (300, 300)
>>> visual_style["margin"] = 20
>>> ig.plot(g, **visual_style)

最终的图显示了粗线条的正式链接和细线条的非正式链接:

The visual representation of our social network - with names, genders and formal ties

我们的社交网络——也显示了哪些链接是正式的

总结一下:存在一些特殊的顶点和边属性,它们对应于图的视觉表示。这些属性会覆盖 igraph 的默认设置(即颜色、权重、名称、形状、布局等)。以下两张表分别总结了顶点和边最常用的视觉属性:

控制绘图的顶点属性

属性名称

关键字参数

用途

color

vertex_color

顶点颜色

font

vertex_font

顶点字体族

label

vertex_label

顶点标签。

label_angle

vertex_label_angle

定义顶点标签的位置,相对于顶点中心。解释为弧度角,零表示“向右”。

label_color

vertex_label_color

顶点标签颜色

label_dist

vertex_label_dist

顶点标签距离,相对于顶点大小

label_size

vertex_label_size

顶点标签字体大小

order

vertex_order

顶点的绘制顺序。具有较小顺序参数的顶点将首先绘制。

shape

vertex_shape

顶点形状。一些形状包括:rectanglecirclehiddentriangle-uptriangle-down。参见 drawing.known_shapes

size

vertex_size

顶点大小(像素)

控制绘图的边属性

属性名称

关键字参数

用途

color

edge_color

边的颜色。

curved

edge_curved

边的弯曲度。正值对应逆时针弯曲的边,负值则相反。零弯曲度表示直线边。True 表示 0.5 的弯曲度,False 表示零弯曲度。

font

edge_font

边的字体族。

arrow_size

edge_arrow_size

如果图是有向的,边的箭头尖端大小(长度),相对于 15 像素。

arrow_width

edge_arrow_width

箭头宽度。相对于 10 像素。

loop_size

edge_loop_size

环的大小。可以为负值以根据相应顶点的大小进行缩放。此属性不用于其他边。此属性仅存在于 matplotlib 后端。

width

edge_width

边框宽度(像素)。

label

edge_label

如果指定,为边添加标签。

background

edge_background

如果指定,在边标签周围添加一个矩形框(仅限 matplotlib)。

align_label

edge_align_label

如果为 True,则旋转边标签使其与边的方向对齐。倒置的标签会被翻转(仅限 matplotlib)。

plot() 的通用参数

这些设置可以作为关键字参数传递给 plot 函数,以控制图的整体外观。

关键字参数

用途

autocurve

具有多重边的图中边的弯曲度自动确定。默认情况下,对于边数少于 10000 的图为 True,否则为 False

bbox

图形的边界框。它应该是一个包含图形所需宽度和高度的元组。默认情况下,图形为 600 像素宽和 600 像素高。

layout

要使用的布局。它可以是 layout 的实例,一个包含 X-Y 坐标的元组列表,或一个布局算法的名称。默认值为 auto,它会根据图的大小和连通性自动选择布局算法。

margin

图形下方、上方、左侧和右侧的空白区域量。

绘图中颜色的指定

igraph 在需要颜色时(例如,相应的属性中的边、顶点或标签颜色)会理解以下颜色规范:

*X11 颜色名称*

请查阅 Wikipedia 上的 X11 颜色名称列表以获取完整列表。在 igraph 中,颜色名称不区分大小写,因此 "DarkBLue" 也可以写成 "darkblue"

*CSS 语法中的颜色规范*

这是一个字符串,格式如下之一(其中 RGB 分别表示红色、绿色和蓝色分量):

  • #RRGGBB,分量范围从 0 到 255(十六进制格式)。示例:"#0088ff"

  • #RGB,分量范围从 0 到 15(十六进制格式)。示例:"#08f"

  • rgb(R, G, B),分量范围从 0 到 255 或从 0% 到 100%。示例:"rgb(0, 127, 255)""rgb(0%, 50%, 100%)"

保存图形

igraph 可用于创建出版质量的图形,方法是请求 plot() 函数将图形保存到文件中,而不是显示在屏幕上。为此,只需将目标文件名作为附加参数(在图之后)传递即可。首选格式根据扩展名推断。igraph 可以保存到 Cairo 支持的任何格式,包括 SVG、PDF 和 PNG 文件。SVG 或 PDF 文件可以在以后转换为 PostScript (.ps) 或封装 PostScript (.eps) 格式,如果您喜欢的话,而 PNG 文件可以转换为 TIF (.tif)。

>>> ig.plot(g, "social_network.pdf", **visual_style)

如果您正在使用 matplotlib,您可以像往常一样保存图形:

>>> fig, ax = plt.subplots()
>>> ig.plot(g, **visual_style)
>>> fig.savefig("social_network.pdf")

matplotlib 支持许多文件格式。

igraph 与外部世界

任何图模块都离不开某种导入/导出功能,以使其能够与外部程序和工具包通信。igraph 也不例外:它提供了读取最常见图格式和将 Graph 对象保存到符合这些格式规范的文件中的函数。下表总结了 igraph 可以读取或写入的格式:

格式

简称

读取方法

写入方法

邻接列表

lgl

Graph.Read_Lgl()

Graph.write_lgl()

(又称 LGL)

邻接矩阵

adjacency

Graph.Read_Adjacency()

Graph.write_adjacency()

DIMACS

dimacs

Graph.Read_DIMACS()

Graph.write_dimacs()

DL

dl

Graph.Read_DL()

暂不支持

边列表

edgelist, edges, edge

Graph.Read_Edgelist()

Graph.write_edgelist()

GraphViz

graphviz, dot

暂不支持

Graph.write_dot()

GML

gml

Graph.Read_GML()

Graph.write_gml()

GraphML

graphml

Graph.Read_GraphML()

Graph.write_graphml()

Gzipped GraphML

graphmlz

Graph.Read_GraphMLz()

Graph.write_graphmlz()

LEDA

leda

暂不支持

Graph.write_leda()

带标签的边列表

ncol

Graph.Read_Ncol()

Graph.write_ncol()

(又称 NCOL)

Pajek 格式

pajek, net

Graph.Read_Pajek()

Graph.write_pajek()

Pickled graph

pickle

Graph.Read_Pickle()

Graph.write_pickle()

作为练习,下载著名的 扎卡里空手道俱乐部研究的 graphml 格式图表示。由于这是一个 GraphML 文件,您应该使用上表中 GraphML 的读取方法(请确保使用下载文件的正确路径):

>>> karate = ig.Graph.Read_GraphML("zachary.graphml")
>>> ig.summary(karate)
IGRAPH UNW- 34 78 -- Zachary's karate club network

如果您想将同一个图转换为,例如 Pajek 格式,您可以使用上表中的方法:

>>> karate.write_pajek("zachary.net")

注意

大多数格式都有其自身的限制;例如,并非所有格式都能存储属性。如果您想将 igraph 图保存为外部包可读的格式并保留数字和字符串属性,GraphML 或 GML 可能是您的最佳选择。如果您没有属性(尽管 NCOL 支持顶点名称和边权重),边列表和 NCOL 也很好。如果您不想在 igraph 之外使用图,但想将其存储以供以后会话使用,pickled 图格式可确保您获得完全相同的图。pickled 图格式使用 Python 的 pickle 模块来保存和读取图。

还存在两个辅助方法:read() 是一个通用的读取方法入口点,它尝试根据文件扩展名推断出合适的格式。Graph.write()read() 的反向操作:它允许您保存图,其中首选格式同样从扩展名中推断出来。可以通过关键字参数 format 来覆盖 read()Graph.write() 的格式检测,它接受上表中其他格式的短名称。

>>> karate = ig.load("zachary.graphml")
>>> karate.write("zachary.net")
>>> karate.write("zachary.my_extension", format="gml")

下一步去哪里

本教程只触及了 igraph 功能的皮毛。长期计划是扩展本教程,使其在接下来的章节中成为 igraph 适当的手册式文档。一个很好的起点是 Graph 类的文档。如果您遇到困难,请首先尝试在我们的 论坛 上提问——也许有人可以立即帮助您。