python-igraph 手册

用于从 Python 使用 igraph

教程

教程

本页是 igraph Python 功能的详细教程。要快速了解 igraph 可以做什么,请查看 快速入门。如果您尚未安装 igraph,请按照 安装 igraph 进行安装。

注意

对于心急的读者,请参阅 图库 页面,其中包含简短、独立的示例。

启动 igraph

使用 igraph 的最常见方法是在 Python 环境中(例如,裸 Python shell、IPython shell、Jupyter notebook 或 JupyterLab 实例、Google ColabIDE)作为命名导入。

$ 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 *

但这通常不被鼓励

启动 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 子目录中找到该脚本,可能需要手动将其添加到他们的路径中。

此脚本启动一个合适的 shell(如果找到 IPythonIDLE,否则为纯 Python shell)并使用星号导入(见上文)。这有时可以方便地使用 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

这意味着:U 非有向图,具有 10 个顶点和 2 条边,并列出了确切的边。如果图具有 name 属性,也会打印出来。

注意

Graph.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 是分别获取所有顶点和边序列的标准方式。就像 Python 字典一样,我们可以使用方括号设置每个属性。该值必须是一个列表,其长度与顶点(对于 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 是表示顶点子集的等效类。

要删除属性,请使用 Python 关键字 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 中,则可以 pickle 整个 Graph

图的结构属性

除了上面描述的简单图和属性操作例程之外,igraph 还提供了大量方法来计算图的各种结构属性。本文档的范围超出了本教程的范围,因此本节仅介绍其中的几个属性以用于说明目的。我们将使用在上一节中构建的小型社交网络。

人们可以想到的最简单的属性可能是顶点度。顶点的度等于与其相邻的边的数量。在有向网络的情况下,我们还可以定义入度(指向顶点的边的数量)和出度(从顶点发出的边的数量)。igraph 能够使用简单的语法来计算所有这些度。

>>> 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 列表。您可以适当地提供 VertexSeqEdgeSeq 实例,而不是 ID 列表。稍后在下一章中,您将学习如何将它们限制为您想要的顶点或边。

注意

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

除了度之外,igraph 还包括内置例程来计算许多其他中心性属性,包括顶点和边介数 (Graph.betweennessGraph.edge_betweenness) 或 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)]

通过调用感兴趣的 VertexSeqEdgeSeqVertexEdge 对象的相应方法,也可以为顶点或边的子集或单个顶点或边检索大多数结构属性。

>>> 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']

对于第一眼来看,该语法可能看起来有点笨拙,因此让我们尝试逐步解释它。select()VertexSeq 的一种方法,其唯一目的是根据单个顶点的属性筛选 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(age < 30),因为只允许相等运算符出现在 Python 中的参数列表中。

为了节省您的一些输入,如果您愿意,您甚至可以省略 select() 方法。

>>> g.vs(age_lt=30)

从理论上讲,可能会存在具有相同名称的属性和结构属性(例如,您可以具有名为 degree 的顶点属性)。在这种情况下,我们将无法确定用户是指 degree 作为结构属性还是顶点属性。为了消除这种歧义,结构属性名称必须始终以一个下划线 (_) 开头,以便用于筛选。例如,要查找度大于 2 的顶点。

>>> g.vs(_degree_gt=2)

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

  • EdgeSeq.select() 的关键字参数列表中使用 _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])
    

    我们也可以使用 VertexSeq 对象。

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

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

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

在许多情况下,我们正在寻找图中具有某些属性的单个顶点或边,或者我们不关心返回哪个匹配项(如果存在多个匹配项),或者我们事先知道只有一个匹配项。一个典型的例子是在 name 属性中按名称查找顶点。VertexSeqEdgeSeq 对象为此类用例提供了 find() 方法。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 列表或 VertexSeq 实例的任何位置。例如,您可以简单地按如下方式查找 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 文件中。

重要提示

要学习本小节的示例,你需要 Cairo 库或 matplotlib 的 Python 绑定(取决于选择的后端)。前一章(安装 igraph)介绍了如何安装 Cairo 的 Python 绑定。

布局算法

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

在 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, bbox=(300, 300), margin=20, 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 的默认设置(有关覆盖系统范围的默认设置,请参见 配置)。此外,提供给 plot() 的适当关键字参数将覆盖由顶点和边属性提供的视觉属性。以下两个表分别总结了顶点和边最常用的视觉属性

控制图形绘图的顶点属性

属性名称

关键字参数

目的

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

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

shape

vertex_shape

顶点的形状。已知的形状有:rectanglecirclehiddentriangle-uptriangle-down。还接受几个别名,请参见 drawing.known_shapes

size

vertex_size

顶点大小(以像素为单位)

控制图形绘图的边属性

属性名称

关键字参数

目的

color

edge_color

边的颜色

curved

edge_curved

边的曲率。正值对应于沿 CCW 方向弯曲的边,负值对应于沿顺时针 (CW) 方向弯曲的边。零表示直边。True 被解释为 0.5,False 被解释为零。这对于使多个边可见很有用。另请参见 plot()autocurve 关键字参数。

font

edge_font

边的字体系列

arrow_size

edge_arrow_size

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

arrow_width

edge_arrow_width

如果图形是有向的,则边上的箭头宽度,相对于 10 像素。

width

edge_width

边的宽度(以像素为单位)

label

edge_label

如果指定,则向边添加标签。

background

edge_background

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

align_label

edge_align_label

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

plot() 的通用关键字参数

这些设置可以指定为 plot() 函数的关键字参数,以控制绘图的整体外观。

关键字参数

目的

autocurve

是否在具有多个边的图形中自动确定边的曲率。对于具有少于 10,000 个边的图形,默认值为 True,否则为 False

bbox

绘图的边界框。这必须是一个元组,其中包含绘图所需的宽度和高度。默认绘图的宽度为 600 像素,高度为 600 像素。

layout

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

边距

绘图的顶部、右侧、底部和左侧边距(以像素为单位)。此参数必须是一个列表或元组,如果指定一个元素少于四个的列表或元组,则将重用其元素。

在绘图中指定颜色

igraph 可以在任何需要颜色(例如,各个属性中的边、顶点或标签颜色)的地方理解以下颜色规范

X11 颜色名称

有关完整列表,请参见 Wikipedia 中的 X11 颜色名称列表。或者,你可以查看 igraph.drawing.colors.known_colors 字典的键。颜色名称在 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%)"

范围 0-1 中的 RGB 值列表或元组

示例:(1.0, 0.5, 0)[1.0, 0.5, 0]

保存绘图

通过要求 plot() 函数将绘图保存到文件中而不是在屏幕上显示,可以使用 igraph 创建出版质量的绘图。只需在图形本身之后传递目标文件名作为附加参数即可完成此操作。首选格式是从扩展名推断出来的。igraph 可以保存到 Cairo 支持的任何格式,包括 SVG、PDF 和 PNG 文件。如果需要,SVG 或 PDF 文件稍后可以转换为 PostScript (.ps) 或 Encapsulated 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()

作为练习,从 此文件 下载著名的 Zachary 空手道俱乐部研究的图形表示,将其解压缩并尝试将其加载到 igraph 中。由于它是 GraphML 文件,因此必须使用上表中的 GraphML 读取器方法(确保使用下载文件的适当路径)

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

如果要将同一图形转换为 Pajek 的格式,可以使用上表中的 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 的适当的手册式文档。与此同时,请查看 API 参考,其中应提供有关几乎所有 igraph 类、函数或方法的信息。一个好的起点是 Graph 类的文档。如果你遇到问题,请先在我们的 Discourse 组中提问 - 也许有人可以立即帮助你。