使用Python/NetworkX的图表数据科学

526 阅读12分钟

使用Python/NetworkX的图形数据科学

Albanese是一名开发人员和数据科学家,曾在Facebook工作,在那里他进行了机器学习模型的预测。他是一位Python专家,也是一位大学讲师。他的博士研究与图形机器学习有关。

我们被数据所淹没。不断扩大的数据库和电子表格中充斥着隐藏的商业洞察力。当数据如此之多时,我们如何分析数据并提取结论?图表(网络,而不是条形图)提供了一种优雅的方法。

我们经常使用表格来通用地表示信息。但图表使用了一种专门的数据结构。一个节点代表一个元素,而不是一个表格行。一条连接两个节点以表示它们的关系。

这种图数据结构使我们能够从独特的角度观察数据,这就是为什么图数据科学被用于从分子生物学到社会科学的各个领域。

左图来源:TITZ, Björn, 等人 "The Binary Protein Interactome of Treponema Pallidum ..."PLoS One, 3, no.5 (2008).

右图来源:ALBANESE, Federico, et al. "Predicting Shifting Individuals Using Text Mining and Graph Machine Learning on Twitter."。(2020年8月24日): arXiv:2008.10749 [cs.SI]

那么,开发者如何利用图数据科学呢?让我们来看看最常用的数据科学编程语言。Python。

在Python中开始使用 "图论 "图形

Python开发者有几个可用的图数据库,如NetworkX、igraph、SNAP和graph-tool。撇开优点和缺点不谈,它们都有非常相似的接口来处理Python的图数据结构。

我们将使用流行的NetworkX 。它的安装和使用都很简单,并且支持我们将要使用的社区检测算法。

用NetworkX创建一个新图是很简单的:

import networkx as nx
G = nx.Graph()

但是G ,因为没有节点和边,所以还算不上是一个图。

如何向图中添加节点

我们可以通过将Graph() 的返回值与.add_node() (或.add_nodes_from() ,用于列表中的多个节点)连锁起来,向网络添加一个节点。我们还可以通过传递一个字典作为参数来向节点添加任意的特征或属性,正如我们在node 4node 5 中所展示的:

G.add_node("node 1")
G.add_nodes_from(["node 2", "node 3"])
G.add_nodes_from([("node 4", {"abc": 123}), ("node 5", {"abc": 0})])
print(G.nodes)
print(G.nodes["node 4"]["abc"]) # accessed like a dictionary

这将输出:

['node 1', 'node 2', 'node 3', 'node 4', 'node 5']
123

但是,如果没有节点之间的边,它们就会被孤立起来,而数据集也不会比一个简单的表格好。

如何向图添加边

与节点的技术类似,我们可以使用.add_edge() ,将两个节点的名称作为参数(或者.add_edges_from() ,用于列表中的多条边),还可以选择包含一个属性字典。

G.add_edge("node 1", "node 2")
G.add_edge("node 1", "node 6")
G.add_edges_from([("node 1", "node 3"), 
                  ("node 3", "node 4")])
G.add_edges_from([("node 1", "node 5", {"weight" : 3}), 
                  ("node 2", "node 4", {"weight" : 5})])

NetworkX库支持像这样的图,其中每条边可以有一个权重。例如,在一个社交网络图中,节点是用户,边是互动,权重可以表示在给定的一对用户之间发生多少次互动--这是一个高度相关的指标。

NetworkX在使用G.edges ,列出了所有的边,但它不包括它们的属性。如果我们想要边的属性,我们可以使用G[node_name] 来获得与一个节点相连的所有东西,或者使用G[node_name][connected_node_name] 来获得一个特定边的属性。

print(G.nodes)
print(G.edges)
print(G["node 1"])
print(G["node 1"]["node 5"])

这将输出:

['node 1', 'node 2', 'node 3', 'node 4', 'node 5', 'node 6']
[('node 1', 'node 2'), ('node 1', 'node 6'), ('node 1', 'node 3'), ('node 1', 'node 5'), ('node 2', 'node 4'), ('node 3', 'node 4')]
{'node 2': {}, 'node 6': {}, 'node 3': {}, 'node 5': {'weight': 3}}
{'weight': 3}

但是,以这种方式阅读我们的第一个图是不切实际的。值得庆幸的是,有一个更好的表示方法。

如何从图(和加权图)中生成图像

图形的可视化是至关重要的。它可以让我们快速而清晰地看到节点之间的关系和网络的结构。

快速调用nx.draw(G) 就可以了。

让我们通过对nx.draw() 的调用使较重的边相应地变粗。

weights = [1 if G[u][v] == {} else G[u][v]['weight'] for u,v in G.edges()]
nx.draw(G, width=weights)

我们为无权重的边缘提供了一个默认的厚度,如结果所示。

我们的方法和图形算法即将变得更加复杂,所以下一步是使用一个更知名的数据集。

使用电影《星球大战:第四集》中的数据的图数据科学

为了更容易解释和理解我们的结果,我们将使用这个数据集。节点代表重要的角色,边(这里没有加权)标志着在一个场景中的共同出现。

注:该数据集来自Gabasova, E. (2016)。星球大战社交网络。DOI:doi.org/10.5281/zen…

首先,我们将用nx.draw(G_starWars, with_labels = True) ,将数据可视化。

通常一起出现的角色,如R2-D2和C-3PO,显得紧密相连。相比之下,我们可以看到达斯-维德并没有与欧文共享场景。

Python NetworkX的可视化布局

为什么每个节点都位于前面图形中的位置?

这是默认的spring_layout 算法的结果。它模拟了弹簧的力量,吸引连接的节点,排斥不连接的节点。这有助于突出连接良好的节点,这些节点最终会出现在中心位置。

NetworkX还有其他布局,使用不同的标准来定位节点,比如circular_layout

pos = nx.circular_layout(G_starWars)
nx.draw(G_starWars, pos=pos, with_labels = True)

结果。

这种布局是中性的,因为节点的位置不取决于它的重要性--所有的节点都被平等地表示。(圆形布局也可以帮助可视化独立的连接组件--在任何两个节点之间有一条路径的子图,但在这里,整个图是一个大的连接组件。)

我们看到的两种布局都有一定程度的视觉混乱,因为边可以自由地与其他边交叉。但是Kamada-Kawai,另一种类似于spring_layout 的力导向算法,将节点定位为最小化系统的能量。

这减少了边的交叉,但也是有代价的。它比其他布局慢,因此对于有许多节点的图来说,不强烈推荐。

这个有一个专门的绘制函数:

nx.draw_kamada_kawai(G_starWars, with_labels = True)

这就产生了这个形状,而不是。

在没有任何特殊干预的情况下,该算法将主要人物(如卢克、莱亚和C-3PO)放在中心,而将不太突出的人物(如卡米和多多纳将军)放在边界。

用特定的布局将图形可视化可以给我们带来一些有趣的定性结果。尽管如此,定量结果仍是任何数据科学分析的重要组成部分,所以我们需要定义一些指标。

节点分析。度数和PageRank

现在我们可以清楚地看到我们的网络,我们可能对节点的特征感兴趣。有多种指标可以描述节点的特征,在我们的例子中,也有描述字符的特征。

节点的一个基本度量是它的*度:*它有多少条边。一个《*星球大战》*角色的节点的程度衡量了他们与多少其他角色共享一个场景。

degree() 函数可以计算出一个角色或整个网络的度:

print(G_starWars.degree["LUKE"])
print(G_starWars.degree)

这两个命令的输出:

15
[('R2-D2', 9), ('CHEWBACCA', 6), ('C-3PO', 10), ('LUKE', 15), ('DARTH VADER', 4), ('CAMIE', 2), ('BIGGS', 8), ('LEIA', 12), ('BERU', 5), ('OWEN', 4), ('OBI-WAN', 7), ('MOTTI', 3), ('TARKIN', 3), ('HAN', 6), ('DODONNA', 3), ('GOLD LEADER', 5), ('WEDGE', 5), ('RED LEADER', 7), ('RED TEN', 2)]

根据程度从高到低对节点进行排序,只需一行代码就可以完成:

print(sorted(G_starWars.degree, key=lambda x: x[1], reverse=True))

输出:

[('LUKE', 15), ('LEIA', 12), ('C-3PO', 10), ('R2-D2', 9), ('BIGGS', 8), ('OBI-WAN', 7), ('RED LEADER', 7), ('CHEWBACCA', 6), ('HAN', 6), ('BERU', 5), ('GOLD LEADER', 5), ('WEDGE', 5), ('DARTH VADER', 4), ('OWEN', 4), ('MOTTI', 3), ('TARKIN', 3), ('DODONNA', 3), ('CAMIE', 2), ('RED TEN', 2)]

由于只是一个总数,度数并不考虑个别边缘的细节。一条给定的边是连接到一个原本孤立的节点还是连接到一个与整个网络相连的节点?谷歌的PageRank算法汇总了这些信息来衡量一个节点在网络中的 "重要性"。

PageRank指标可以被解释为一个代理人从一个节点随机地移动到另一个节点。连接较好的节点有更多的路径通过它们,所以代理将倾向于更经常地访问它们。

这样的节点会有一个更高的PageRank,我们可以用NetworkX库来计算:

pageranks = nx.pagerank(G_starWars) # A dictionary
print(pageranks["LUKE"])
print(sorted(pageranks, key=lambda x: x[1], reverse=True))

这打印出了卢克的排名和我们的角色按排名排序的情况:

0.12100659993223405
['OWEN', 'LUKE', 'MOTTI', 'DODONNA', 'GOLD LEADER', 'BIGGS', 'CHEWBACCA', 'LEIA', 'BERU', 'WEDGE', 'RED LEADER', 'RED TEN', 'OBI-WAN', 'DARTH VADER', 'CAMIE', 'TARKIN', 'HAN', 'R2-D2', 'C-3PO']

欧文是拥有最高PageRank的角色,超过了拥有最高等级的卢克。的分析。虽然欧文不是与其他角色共享场景最多的角色,但他是一个与许多重要角色共享场景的角色,如卢克本人、R2-D2和C-3PO。

更大的对比是,C-3PO,这个拥有第三高程度的角色,是PageRank最低的角色。尽管C-3PO有很多联系,但很多都是与不重要的角色。

经验之谈。使用多个指标可以更深入地了解一个图的节点的不同特征。

社区检测算法

当分析一个网络时,可能需要分离出社区:那些相互之间有高度联系但与社区外的节点联系最少的节点组。

这方面有多种算法。其中大多数是在无监督的机器学习算法中发现的,因为它们为节点分配了一个标签,而不需要它们之前被标记过。

最著名的算法之一是标签传播法。在该算法中,每个节点开始时都有一个独特的标签,在一个群体中。节点的标签根据邻近节点的大多数标签进行迭代更新。

标签在网络中扩散,直到所有节点与它们的大多数邻居共享一个标签。彼此紧密相连的节点组最终会有相同的标签。

使用NetworkX库,运行这个算法只需要三行Python程序:

from networkx.algorithms.community.label_propagation import label_propagation_communities

communities = label_propagation_communities(G_starWars)
print([community for community in communities])

输出:

[{'R2-D2', 'CAMIE', 'RED TEN', 'RED LEADER', 'OBI-WAN', 'DODONNA', 'LEIA', 'WEDGE', 'HAN', 'OWEN', 'CHEWBACCA', 'GOLD LEADER', 'LUKE', 'BIGGS', 'C-3PO', 'BERU'}, {'DARTH VADER', 'TARKIN', 'MOTTI'}]

在这个集合列表中,每个集合代表一个社区。熟悉这部电影的读者会注意到,该算法成功地将 "好人 "和 "坏人 "分开,在不使用任何真正的(社区)标签或元数据的情况下有意义地区分了这些角色。

在Python中使用图形数据科学的智能洞察力

我们已经看到,开始使用图形数据科学工具比听起来要简单得多。一旦我们使用Python中的NetworkX库将数据表示为图形,短短的几行代码就能让人眼前一亮。我们可以将我们的数据集可视化,测量和比较节点的特征,并通过社区检测算法对节点进行合理的分类。

拥有使用Python从网络中提取结论和见解的技能,使开发者能够与数据科学服务管道中常见的工具和方法相结合。从搜索引擎到航班调度到电气工程,这些方法很容易适用于广泛的环境。

图形数据科学的推荐阅读

社区检测算法
Zhao Yang, René Algesheimer, and Claudio Tessone."人工网络上社区检测算法的比较分析"。Scientific Reports, 6, no.30750 (2016).

图深度学习
Thomas Kipf."图卷积网络"。2016年9月30日。

图数据科学的应用
Albanese, Federico, Leandro Lombardi, Esteban Feuerstein, and Pablo Balenzuela."在Twitter上使用文本挖掘和图谱机器学习预测转变的个人"。(2020年8月24日):arXiv:2008.10749[cs.SI]。

Cohen, Elior."PyData Tel Aviv Meetup。Node2vec."YouTube。2018年11月22日。视频,21:09。www.youtube.com/watch?v=828…

了解基础知识

Python可以用于数据可视化吗?

是的,它可以。Python有多个用于数据可视化的库,例如NetworkX库。

如何在Python中绘制数据图表?

像NetworkX、igraph、SNAP和graph-tool这样的Python图形数据可视化库已经内置了这种功能。NetworkX库对于网络的节点和边的可视化非常有用。

Graph是Python中的一种数据类型吗?

Python NetworkX 库提供了不同的数据图表类型。根据图的特性,可能的类型有Graph, DiGraph, MultiGraph, 和MultiDiGraph。

图论在数据科学中使用吗?

是的,NetworkX库使Python数据科学家能够轻松利用不同的基于图论的算法,如PageRank和标签传播。

NetworkX在Python中的用途是什么?

NetworkX是一个用于在Python中表示图的库。开发者可以用它来创建、操作和可视化图形,也可以用于非可视化的图形数据科学分析。

我应该在什么时候使用NetworkX?

易于使用的NetworkX库应该用于图分析;例如,当需要社区检测算法或其他特殊功能时。但它的功能在其他方面与其他图库如igraph、SNAP和graph-tool相当。

NetworkX的速度快吗?

对于许多应用来说,NetworkX已经足够快了,但是对于大规模的图数据集来说,其他Python库可能更快,这取决于算法。使用NetworkX的好处是它的易用性和广泛的开发者社区。

什么是社区检测算法?

社区检测算法旨在根据网络节点的连通性对其进行聚类。标签传播是一种广泛使用的方法,并在Python NetworkX库中有一个实现。