教程 (西班牙语)

本页面是关于igraph在Python中功能的详细教程。要快速了解igraph的功能,请参阅快速入门。如果您尚未安装igraph,请按照安装igraph的说明进行操作。

注意

对于不耐烦的读者,请查看Gallery of Examples页面以获取简短且自包含的示例。

开始使用 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的子目录scripts中找到脚本,并且可能需要手动将其添加到路径中。

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

注意

您可以通过Configuration指定此脚本应使用的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对象,但如果您将图保存到文件中,则只会保留字符串(“string”)和数字属性。如果您需要保存其他类型的属性,请参考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") 来计算入度和出度。你也可以使用单个顶点的ID或顶点ID列表来调用 degree(),如果你只想计算一部分顶点的度数:

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

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

注意

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

除了度数之外,igraph 还包括内置的例程来计算许多其他中心性属性,例如顶点和边的中介性或谷歌的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
    

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

Keyword argument

含义

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空间中的特定表示。这意味着当我们想要可视化一个图时,我们首先需要找到将顶点映射到二维或三维空间中的坐标的方法,最好是以一种视觉上令人愉悦的方式。图论的一个独立分支,称为图绘制,试图通过各种图布局算法来解决这个问题。igraph实现了多种布局算法,并且还能够使用Cairo库在屏幕上或PDF、PNG或SVG文件中绘制它们。

重要

要跟随本节中的示例,需要Python中的Cairo库或matplotlib。

设计算法(“布局”)

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

用于大型图的[大型图布局]算法

layout_random

random

将顶点完全随机放置

layout_random_3d

random_3d

将顶点完全随机地放置在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"]])

如果您希望保持图表视觉表示属性与图表本身分离,则最后一种方法是首选。您可以简单地创建一个包含参数的Python字典,这些参数包含您将传递给函数plot()的关键字,然后使用双星号(**)将您的特定样式属性传递给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

顶点的形状。一些形状: rectangle, circle, hidden, triangle-up, triangle-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

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

plot()的通用参数

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

关键字参数

用途

autocurve

自动确定图中边的曲率,适用于具有多条边的图。标准设置为 True 用于少于10000条边的图,而 False 用于其他情况。

bbox

图表的边界框。必须是一个包含图表所需宽度和高度的元组。默认情况下,图表的宽度为600像素,长度为600像素。

layout

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

margin

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

图表中的颜色规范

igraph 理解以下颜色规范,只要它期望一个颜色(例如,在相应的属性中的边颜色、顶点颜色或标签颜色):

*X11颜色名称*

请查阅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()

腌制的图形

pickle

Graph.Read_Pickle()

Graph.write_pickle()

作为练习,下载著名的Zachary的空手道俱乐部研究的图形表示,格式为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() 的反向操作:它允许保存一个图,其中首选格式再次根据扩展名推断。read()Graph.write() 的格式检测可以通过关键字参数 format 来覆盖,该参数接受上表中其他格式的短名称:

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

接下来去哪里

本教程仅触及了igraph功能的表面。长期计划是在接下来的章节中扩展本教程,使其成为igraph的适当手册风格文档。一个很好的起点是Graph类的文档。如果你遇到困难,首先尝试在我们的Discourse group中提问——也许有人能立即帮助你。