Skip to content

Latest commit

 

History

History
1205 lines (847 loc) · 63.2 KB

07.md

File metadata and controls

1205 lines (847 loc) · 63.2 KB

七、使用社交图

在本章中,我们将介绍:

  • 准备在 Python 中使用社交网络
  • 导入网络
  • 探索英雄网络中的子图
  • 寻找牢固的联系
  • 寻找关键人物
  • 探索整个网络的特征
  • 社交网络中的聚类和社区检测
  • 可视化图形
  • R 中的社交网络

简介


得益于 Facebook 和 Twitter 等社交网站,社交网络已成为现代生活的工具。 但是,社交网络本身并不新鲜。 这种网络的研究可以追溯到 20 世纪初,特别是在社会学和人类学领域。 正是由于它们在主流应用中的盛行,才将这些类型的研究转移到了数据科学领域。

事实证明,社交网络作为人类行为的模型非常有趣。 人类文明起源于部落社会,因此,邓巴的数字[a HTG1]假设在任何给定时间我们扩展的社交网络中只能有 150 个人。 网络。 潜在的社交网络无处不在,不仅存在于流行的 Web 2.0 应用中。 我们通过与各种网络的连接来管理生活,因此,我们生成了大量相关的丰富数据,可用于对自己和我们的关系做出预测。

就像我们将在本章中讨论的那样,网络以关系为中心的世界观。 通过利用现有的人与人关系数据结构(一个社交网络),我们可以使用聚类技术对大型网络进行分析,以发现社区,揭示对图的重要成员的作用的洞察力,甚至产生行为 通过关系推理进行预测。 这些分析具有从执法到选举预测以及建议到应用优化的大量实际应用。

这些分析的数学基础来自图论。 因此,本章中的分析技术将集中于图的基数,遍历和聚类。 为了介绍这些技术,我们将使用出色的 Python 图形库 NetworkX。 我们将在网络的各个级别进行一些分析,例如在个人级别进行成对比较,在组级别进行社区检测以及在网络级别进行内聚分析。 最后,我们将看一下如何使用各种工具来可视化和绘制图形和子图形。

了解图和网络

本章进行分析的基础来自图论-图的应用和性质的数学研究,其最初是出于对机会博弈的研究。 一般而言,这涉及对网络编码和图形特性的研究。 图论可以追溯到 1735 年 Euler 在柯尼斯堡七桥问题上的工作。但是,在最近的几十年中,社交网络的兴起影响了该学科,特别是计算机科学 图形数据结构和数据库。

让我们从争论的角度开始。 网络和图形之间有什么区别? 术语图可以用来暗示变量和函数的可视化表示,一组节点和边的数学概念或基于该概念的数据结构。 同样,术语网络有多个定义。 它可以是一个相互连接的系统,也可以是一种特殊类型的数学图。 因此,在这种情况下,无论是社交网络还是社交图,都是合适的,特别是在我们指的是数学概念和数据结构时。

图是网络的符号表示,它由一组顶点(节点)及其连接(关系或边)组成。 更正式地说,图可以定义为:G =(V,E),由有限的节点集(由 V(顶点)或 V(G)表示)和集合 E(边)或 E(G)组成的实体 的无序对{u,v},其中 u,v VV。下图所示的可视示例应为读者所熟悉:

图可以是有向的或无向的。 有向图具有有序关系。 无向图可以看作是双向有向图。 社交网络中的有向图往往具有方向性的语义关系,例如,朋友,其中安倍晋三可能与简成为朋友,但简可能不会互惠。 无向社交网络具有更一般的语义关系,例如“知道”。 任何有向图都可以轻松转换为更通用的无向图。 在这种情况下,邻接矩阵变得对称,即每个关系都是互惠的。

邻接矩阵是二维图形表示,其中第 i 个节点和第个节点连接时,每个单元格或元素(i,j)为1; 否则为0。 当然,这不是存储有关图形信息的最紧凑的方式。 即使大多数节点不与大多数其他节点共享一条边,也必须为每对节点存储一个字节。 但是,此表示在计算上是有效的,并用于许多图形算法。 考虑在该方案中可以将节点表示为其边缘的向量。 下图显示了具有四个节点的无向​​图的小邻接矩阵示例:

最后几个术语将有助于我们的讨论。 顶点的基数称为图的顺序,而边的基数称为大小。 在上图所示的图中,顺序为 7,大小为 10。如果两个节点共享一条边,则它们是相邻的;否则,两个节点相邻。 如果是这样,则它们也称为邻居。 顶点的邻域是该顶点连接到的所有顶点的集合。 顶点的度数是其邻域的大小,即与顶点共享一条边的节点数。

考虑到这一点,图形问题通常分为几类。 存在问题试图确定是否存在节点,路径或子图,尤其是在存在约束的情况下。 构造问题集中于在给定约束下给定一组节点和路径的图的构造。 枚举问题试图确定一组约束内的顶点和关系列表。 最后,优化问题决定了两个节点之间的最短路径。

准备在 Python 中使用社交网络


值得重复的 Python 的主要优势之一是可用于该语言的出色的预制软件包的数量。 对我们来说幸运的是,网络分析也不例外。 这个简短的食谱将引导您完成本章其余部分所需的库的安装。

准备

本章中的任务所需的外部库如下:

  • 网络 X
  • matplotlib
  • python-Louvain

怎么做...

此时,我们将使用以下应熟悉的步骤来准备其余食谱:

  1. 打开一个新的终端或命令提示符,然后转到您的项目目录。
  2. 如果您使用的是虚拟环境,请激活您的虚拟环境并键入以下内容:
pip install networkx

如果您没有使用虚拟环境,则很可能需要使用sudo,如下所示:

sudo pip install networkx
  1. 现在,我们必须安装python-louvain软件包:
pip install python-louvain

工作原理...

NetworkX 是一个维护良好的 Python 库,用于创建,操作和研究复杂网络的结构。 它的工具允许快速创建图形,并且该库还包含许多常见的图形算法。 特别是,NetworkX 补充了 Python 的科学计算套件 SciPy / NumPy,matplotlib 和 Graphviz,并且可以处理内存中 10M 个节点和 100M 个链接的图形。 NetworkX 应该成为每个数据科学家工具包的一部分。

NetworkX 和 Python 是进行社交网络分析的完美组合。 NetworkX 旨在处理大规模数据。 其中包含的核心算法是在极快的旧版代码上实现的。 图非常灵活(节点可以是任何可散列的类型),并且有大量的本机 IO 格式。 最后,使用 Python,您将能够访问或使用 Internet 上数据库中的大量数据源。

Python-louvain 是一个用于单一目的的小型 Python 库,它使用 Louvain 方法进行社区检测,如大型网络中社区的快速展开论文( Journal of Statistics Mechanics:理论与实验,2008(10))。 也可以使用 C ++ 实现。

还有更多...

尽管我们将在本章中专门使用 NetworkX,但还有许多值得一提的出色的替代社交网络分析库。 首先,可以使用 R,Python 和 C/C++ 使用内置在 C 中的基础工具对 igraph 进行编程和使用。 / C ++ 以提高性能。 第二个要看的库是 graph-tool。 在它的 Python 可用性之下,它也可以用 C ++ 实现,但具有利用 OpenMP 在多核计算机之间进行并行化的额外好处。

导入网络


我们将在本章中探讨的数据集很有趣。 这是 CescRossellóRicardo Alberich 和 Joe Miro 构建的 Marvel Universe 社会图数据集,作为他们对无序系统和神经网络的研究的一部分。 他们通过用出现的漫画来编辑角色来创建网络。 事实证明,该网络实际上是模仿现实世界的社交网络。 从那时起,使用这个著名的数据集(以及扩展名)进行了许多可视化以及其他混搭。 在本食谱中,我们将所需的数据导入到我们的 Python 环境中。

准备

安装完前面的配方中所需的库后,将需要使用本章提供的数据集。

怎么做...

执行以下步骤来导入数据:

  1. 为了使该图成为 NetworkX 图表示形式,请遍历数据集并为每个英雄对添加边(自动创建节点):
import networkx as nx
import unicodecsv as csv 
def graph_from_csv(path):
 graph = nx.Graph(name="Heroic Social Network")
 with open(path, 'rU') as data: reader = csv.reader(data)
 for row in reader:
 graph.add_edge(*row)
return graph

每行都是(herohero)元组。 使用*row表示法,我们扩展了元组,以便函数定义实际上是graph.add_edge(hero, hero)

  1. 数据集很大,重达 21 MB,需要一两秒钟才能加载到内存中。 您可以如下计算图形的大小和顺序:
>>> graph.order() # graph.number_of_nodes()

6426

>>> graph.size()  # graph.number_of_edges()

167219

保持此功能方便; 我们将需要它来获取本章其余大部分的图形!

  1. 从中导出社交网络的备用数据集包括出现角色的漫画。 这种格式需要略有不同的图形生成机制:
def graph_from_gdf(path):
 graph = nx.Graph(name="Characters in Comics")
 with open(path, 'rU') as data:
 reader = csv.reader(data)
 for row in reader:
 if 'nodedef' in row[0]:
 handler = lambda row,G: G.add_node(row[0], 
                TYPE=row[1])
 elif 'edgedef' in row[0]:
 handler = lambda row,G: G.add_edge(*row)
 else:
 handler(row, graph)
return graph

在此制表符分隔值TSV)文件中,在节点或边定义的行之前有标语nodedefedgedef的标语。 当我们遍历每一行时,我们将根据是否看到横幅创建一个处理程序函数lambda。 然后,对于横幅下的每一行,我们都使用定义的处理程序,就像在本节中的节点或边一样。

  1. 此时,我们可以使用 NetworkX 的内置方法为图形计算一些快速信息:
>>> nx.info(graph)
Name: Heroic SociaL Network
Type: Graph
Number of nodes: 6426
Number of edges: 167219
Average degree:  52.0445

请注意,图表的名称是在我们实例化nx.Graph时添加的,HTG0 是一项可选功能,可让您更轻松地在代码中跟踪多个图表。

工作原理...

在任何数据科学项目中,数据导入都是一个挑战,图形数据可以采用多种格式。 对于此配方,数据很简单; 它是一个 TSV 文件,其中包含知道关系的英雄与英雄的联系。

还有一个备用数据集,通过包括它们的关系源(英雄一起出现的漫画)来扩展已知关系。 通过通过出现在关系中来扩展漫画对英雄网络,此扩展在英雄对英雄网络之间添加了额外的跃点。 这个扩展的网络可能使我们能够计算已知关系的强度。 例如,英雄一起出现的漫画越多,他们彼此之间可能就越了解。 有趣的是,这种类型的数据集已被证明对社区发现和关系聚类有效。

graph_from_gdf函数确定我们是在读取边还是在读取节点,并且当看到名为nodedefedgedef的行标语时,通过lambda函数实现一个新的处理程序,它可以适当地处理 TSV 文件的每一行。 ,指示其下方的行分别是节点或边。 此功能还为我们提供了创建属性图的机会。

属性图通过在节点和边上甚至可能在图本身上包含键/值对扩展了我们当前的图定义。 属性图更具表现力,并且它们是许多图数据库的基础,因为它们可以按每个节点和每个关系保存更多信息(因此可以替代传统的关系数据库)。 NetworkX 还允许您为节点和边指定其他属性。

注意

注意add_node方法; 其他关键字参数另存为属性。 在这种情况下,我们设置TYPE属性来确定该节点是漫画书还是英雄。 NetworkX 还允许使用set_node_attributes (G, name, attributes)get_node_attributes(G, name)批量设置和检索节点上的属性,这将返回一个字典,该字典将节点映射到请求的属性(或将属性保存到节点组)。

探索英雄网络中的子图


上一食谱导入网络中的图表对于我们来说太大了,无法让人体会到个人水平的变化,尽管我们很快就会看到一些分析,这些分析告诉我们有关人口的有趣信息 和社区。 但是,为了让我们立即看到一些有趣的东西,让我们提取一个子图来玩。 特别是,我们将在数据集中为特定英雄收集一个子图。 当以一个人或一个演员为焦点生成一个子图时,它被称为自我网络,实际上,自我网络的程度可能是一个人的自我价值的量度!

准备

只要您完成了上一个食谱,您就会为这个食谱做好准备。

怎么做...

以下步骤将引导您从大型数据集中提取子图并可视化自我网络:

  1. 每个社交网络都具有与节点一样多的自我。 自我的邻居被称为“改变者”。 自我子图的定义受 n 步邻域的约束,n 步邻域定义了远离自我的跳数,以包括在子图中。 NetworkX 提供了一种非常简单的机制来提取自我图,如以下命令所示:
>>> ego = nx.ego_graph(graph, actor, 1)

此函数返回参与者节点的所有邻居的子图,该子图的最大路径长度由第三个参数指定。

  1. 要绘制自我网络图,我们使用以下函数:
def draw_ego_graph(graph, character, hops=1):
 """
 Expecting a graph_from_gdf
 """

 # Get the Ego Graph and Position
 ego = nx.ego_graph(graph, character, hops)
 pos = nx.spring_layout(ego)
 plt.figure(figsize=(12,12))
 plt.axis('off')

 # Coloration and Configuration
 ego.node[character]["TYPE"] = "center"
 valmap = { "comic": 0.25, "hero": 0.54, "center": 0.87 }
 types  = nx.get_node_attributes(ego, "TYPE")
 values = [valmap.get(types[node], 0.25) for node in 
    ego.nodes()]

 # Draw
 nx.draw_networkx_edges(ego, pos, alpha=0.4)
 nx.draw_networkx_nodes(ego, pos,
 node_size=80,
 node_color=values,
 cmap=plt.cm.hot, with_labels=False)

 plt.show()
  1. 让我们看一下LONGBOW/AMELIA GREER的自我网络,从单跳网络开始:
>>> graph = graph_from_gdf('comic-hero-network.gdf'))
>>> draw_ego_graph(graph, "LONGBOW/AMELIA GREER")

前面的命令将为您提供下图:

上图显示了此单跳自我网络,该网络是从扩展的漫画到英雄的社交图谱得出的。 由于存在两种不同类型的节点,因此我们在视觉上对它们进行了不同的着色。 橙色节点是角色,蓝色节点是漫画。 自我节点LONGBOW/AMELIA GREER她自己是白色的。

  1. 让我们为同一角色创建一个两跳自我网络:
>>> draw_ego_graph(graph, "LONGBOW/AMELIA GREER", 2)

前面的命令将为您提供以下图形:

工作原理...

阿米莉亚·格里尔(又称长弓)是雇佣军特种作战部队“ Har”的一部分。 她出现在两部 Marvel 漫画中,尤其是 1990 年 5 月的 Uncanny X-Men Vol 1#261。她的自我网络非常紧密(主要包含来自“猎兔犬”的成员),如您所见。 前一跳网络图。

前面的两跳网络大大扩展了网络的容量。 黑色节点代表到下一个漫画人物社区的第二跳。 集群很明显,即使在这个小的自我网络中也是如此。 尽管如此,即使是单跳网络也可以说出很多关于团体成员和演员的重要性的信息。 我们将在本章稍后讨论如何构建这样的图。

还有更多...

自我可以或不能成为网络的一部分。 实际上,由于子图是根据自我的邻域生成的,因此对自我的成员资格测试并不重要! 相反,去除自我可以显示网络的结构配置。 考虑下图所示的社交网络:

在上图中,自我是图表的一部分; 网络的结构似乎是统一的和有凝聚力的。

如您所见,没有自我,隔离变得很重要,明显的离群值也是如此。 跨自我聚合这些隔离是发现组成员身份和社区的方式。 我们将在下一部分中对此进行更多探讨。

找到牢固的联系


目前,我们的英雄网络只是测量两个字符是否已连接。 这种计算很简单; 它们会一起出现在同一本漫画书中吗? 我们假设在漫画的短暂时间空间中,甚至浮雕也意味着角色之间已经相互作用。 但是,这并不能告诉我们与特定角色最重要的关系是谁。

为了确定自我网络中最重要的人(或确定两个参与者之间的相对亲和力),我们需要确定边缘权重。 由于边缘代表互动,从属关系或社会关系,因此权重确定两个演员之间的距离,相对于具有相似关系的其他演员。 社交网络中边缘权重的代理包括:

  • 频率,例如,两个参与者之间的交流频率
  • 互惠,例如,关系是否互惠
  • 类型或属性,例如,已婚演员的关系要比大学室友更强
  • 邻居的结构,例如,共同朋友的数量

在我们的英勇社交图表中,我们将使用其中一对角色同时出现的漫画数量来代替它们的联系强度。 这似乎是有道理的; 如果一个角色是与英雄出现在同一漫画中的恶棍,那么他们之间的关系就是克星,那不是简单的主角/对立关系! 另一个例子是两个英雄经常一起出现。 他们可能是英雄团队的一部分(例如,复仇者联盟),或者是同伴关系(例如,Bucky 与美国队长)。

准备

如果您完成了前面的食谱,则应该准备解决这个问题。

怎么做...

以下步骤将引导我们逐步找到网络中的牢固纽带:

  1. 为了计算关系,我们将从漫画英雄网络数据集中重新创建英雄网络图。 由于这表示整个图形计算(例如,我们将遍历图形中的每个节点),因此我们需要使用内存安全的迭代器并将中间数据保存到磁盘。 这是完整的代码; 我们将逐行介绍它,如下所示:
def transform_to_weighted_heros(comics):
 # Create new graph to fill in
 heros = nx.Graph(name="Weighted Heroic Social Network")
 # Iterate through all the nodes and their properties
 for node, data in graph.nodes(data=True):
 # We don't care about comics, only heros
 if data['TYPE'] == 'comic': continue
 # Add the hero and their properties (this will also update data)
 heros.add_node(node, **data)

 # Find all the heros connected via the comic books
 for comic in graph[node]:
 for alter in graph[comic]:
 # Skip the hero that we're on
 if alter == node: continue

 # Setup the default edge
 if alter not in heros[node]:
 heros.add_edge(node, alter, weight=0.0, 
                    label="knows")

 # The weight of the hero is the fraction of 
                connections / 2
 heros[node][alter]["weight"] += 1.0 / 
               (graph.degree(comic) *2)
 return heros
  1. 现在让我们看看 Longbow 的社交加权图:
def draw_weighted_ego_graph(graph, character, hops=1):
 # Graph and Position
 ego = nx.ego_graph(graph, character, hops)
 pos = nx.spring_layout(ego)
 plt.figure(figsize=(12,12))
 plt.axis('off')

 # Coloration and Configuration
 ego.node[character]["TYPE"] = "center"
 valmap = { "hero": 0.0, "center": 1.0 }
 types  = nx.get_node_attributes(ego, "TYPE")
 values = [valmap.get(types[node], 0.25) for node in 
    ego.nodes()]
 char_edges = ego.edges(data=True, nbunch=[character,])
 nonchar_edges = ego.edges(nbunch=[n for n in ego.nodes() 
    if n != character])
 elarge=[(u,v) for (u,v,d) in char_edges if d['weight'] >=0.12]
 esmall=[(u,v) for (u,v,d) in char_edges if d['weight'] < 0.12]
 print set([d['weight'] for (u,v,d) in char_edges])

 # Draw
 nx.draw_networkx_nodes(ego, pos,
 node_size=200,
 node_color=values,
 cmap=plt.cm.Paired, 
                           with_labels=False)

 nx.draw_networkx_edges(ego,pos,edgelist=elarge,
 width=1.5, edge_color='b')
 nx.draw_networkx_edges(ego,pos,edgelist=esmall,
 width=1,alpha=0.5,
                        edge_color='b',style='dashed')
 nx.draw_networkx_edges(ego,pos,edgelist=nonchar_edges,
 width=0.5,alpha=0.2,style='dashed')
plt.show()

前面的命令将为您提供以下图形:

如您所见,Longbow 有两条牢固的纽带(用深蓝色的粗线表示),这是有道理的,因为她在两本漫画书中只有两个其他角色。 与她直接相关的其他角色具有淡蓝色的虚线,表示她与这些角色之间的联系较弱。

请注意,所有字符都与它们在自我网络中共享联系的其他字符相连(用浅灰色虚线表示)。 我们已经开始看到聚类了,因为来自不同漫画书的角色已经移到了 Longbow 红色节点的任一侧。 与两个漫画中的人物都具有亲和力的人物位于中间。

工作原理...

transform_to_weighted_heros功能是此配方的关键,我们在此处逐行对其进行说明。 首先,我们创建一个空图,在其中添加转换。 请注意,在 NetworkX 中,有诸如create_empty_graphGraph.subgraph之类的功能; 但是,前者将不会传输我们需要的数据,而后者将传输过多的数据。 我们将遍历原始图中的每个节点,跳过漫画书。 请注意,我们将使用heros.add_node方法添加英雄两次,因为每次添加一条边时,都会创建一个节点(如果尚不存在)。 但是,为了确保获得所有数据,我们为每个英雄调用add_node,并且如果节点中已经存在该节点,则它将简单地更新该节点的属性。

接下来,我们将通过漫画之间的联系将英雄联系在一起。 我们将收集与当前英雄所在的同一本漫画书相关的所有英雄(跳过当前英雄以防止自我循环)。 如果没有边缘,我们将在添加权重计算之前创建一个默认边缘。 我们要分配的权重与漫画书中的字符数有关。 可以认为,如果漫画书中包含更多字符,它们之间的联系就会更加松散。 因此,我们将权重计算为漫画程度的倒数,除以二。 由于这是一个无向图,因此必须除以 2,否则我们将使所有权重加倍!

还有更多...

加权边缘对于能够在图形上进行预测至关重要,部分原因是它们允许对路径进行排名,但主要是因为它们反映了社交网络的底层语义关联。 进行预测依赖于建立同构的关系-这是具有相似兴趣的人聚集在一起的趋势,并且导致形成称为簇的同质群体。 亲密关系可以强也可以弱。 例如,一起住在同一所学校的人们比住在同一座城市的人们之间的联系更牢固。

传递性是允许我们进行预测的边的属性。 简而言之,它提供了执行三元闭合的能力。 如果节点 A 和 B 已连接,并且 A 和 C 也已连接,则 B 和 C 也可能也已连接。 及物性是牢固关系存在的证据,但这不是必要条件或充分条件,它可能不适用于脆弱关系。

另一方面,网桥是连接各组节点的边缘。 桥促进集群通信,通常是弱联系或异性关系的产物。 了解和理解与您的特定社交图表相关的这些属性对于解锁关键见解以及能够对图表可能发生的变化做出未来的预测至关重要。

寻找关键参与者


在先前的食谱*中发现牢固的联系,*我们开始探索自我网络和社交网络中个人之间的牢固联系。 我们开始看到与其他演员紧密联系的演员创建​​了以自己为中心的集群。 这就引出了一个显而易见的问题:图中的关键人物是谁,他们具有什么样的吸引力? 我们将研究几种方法来确定节点的重要性或其中心性,以尝试发现度中心性,中间性中心性,邻近性中心性和特征向量中心性。

准备

如果您完成了前面的食谱,则可以开始这一食谱了。

怎么做...

以下步骤将确定该漫画人物网络中的主要角色:

  1. 为了找到英雄网络中的前十个节点,我们计算节点的度并对它们进行排序:
import operator

>>> degrees = sorted(graph.degree().items(), key=operator.itemgetter(1), reverse=True)

>>> for node in degrees: print node
  1. 另外,我们计算图中一个节点所连接的节点的百分比; NetworkX 为我们提供了有用的功能degree_centrality。 在进行此操作时,我们也可以将其设置为节点的属性,以便于查找:
>>> centrality = nx.degree_centrality(graph)

>>> nx.set_node_attribues(graph, 'centrality', centrality)

>>> degrees = sorted(centrality.items(), key=itemgetter(1), reverse=True)

>>> for item in degrees[0:10]: print "%s: %0.3f" % item

前面的命令为我们提供了数据集中的十大关键角色,我认为很明显,它们是 Marvel 宇宙中最有影响力的角色:

1\.  CAPTAIN AMERICA:       0.297 (1908)
2\.  SPIDER-MAN/PETER PAR:  0.270 (1737)
3\.  IRON MAN/TONY STARK :  0.237 (1522)
4\.  THING/BENJAMIN J. GR:  0.220 (1416)
5\.  MR. FANTASTIC/REED R:  0.215 (1379)
6\.  WOLVERINE/LOGAN :      0.213 (1371)
7\.  HUMAN TORCH/JOHNNY S:  0.212 (1361)
8\.  SCARLET WITCH/WANDA :  0.206 (1325)
9\.  THOR/DR. DONALD BLAK:  0.201 (1289)
10\. BEAST/HENRY &HANK& P:  0.197 (1267)

考虑到平均程度为52.045,这些字符在具有大量连接的情况下具有巨大的影响力! 在进行此操作时,我们还可以创建图形的连通性的直方图。 在向您展示直方图之前,请快速注意一下:NetworkX 确实有一个函数degree_histogram,它将返回度数的列表。 但是,在这种情况下,列表的索引是度值,而 bin 宽度是1; 这意味着列表的长度可能非常大(Order(len(edges)))。 使用graph.degree().values()的效率更高,尤其是对于社交网络图,我们将在后面看到。

  1. 有了下面的小片段,您现在应该可以看出,就影响力而言,我们的主要角色与正常角色相距多远:
>>> import matplotlib.pyplot as plt
>>> plt.hist(graph.degree().values(), bins=500)
>>> plt.title("Connectedness of Marvel Characters")
>>> plt.xlabel("Degree")
>>> plt.ylabel("Frequency")
>>> plt.show()

上面的代码片段将为您提供以下图形:

  1. 实际上,您可以过滤出节点度大于 500 的字符数,即最高的百分之一。 此过滤器返回 98.8%的字符:
>>> filter(lambda v: v < 500, graph.degree().values())

如果这样做,曲线将变得更加明显:

>>> import matplotlib.pyplot as plt
>>> plt.hist(graph.degree().values(), bins=500) >>> plt.title("Connectedness of Marvel Characters")
>>> plt.xlabel("Degree")
>>> plt.ylabel("Frequency")

前面的命令将为您提供以下输出:

  1. 为了计算其他集中度指标,我们将使用 NetworkX 内置函数。 要计算中间性中心度,请使用以下函数:
>>> centrality = nx.betweenness_centrality(graph)
>>> normalied  = nx.betweenness_centrality(graph, normalized=True)
>>> weighted   = nx.betweenness_centrality(graph, weight="weight")

前面的功能使您可以计算中间性中心度,这将在后面讨论,并且可以归一化或加权。

  1. 要计算紧密度中心度,我们可以使用以下函数,它们类似于中间度中心度:
>>> centrality = nx.closeness_centrality(graph)
>>> normalied  = nx.closeness_centrality(graph, normalized=True)
>>> weighted   = nx.closeness_centrality(graph, distance="weight")
  1. 最后,要计算特征向量的中心性,您可以使用 NetworkX 进行两种选择:
>>> centrality = nx.eigenvector_centality(graph)
>>> centrality = nx.eigenvector_centrality_numpy(graph)
  1. 为了轻松地在图表上浏览这些中心度指标,让我们创建一个函数,该函数通常根据中心度指标打印前 10 个节点:
def nbest_centrality(graph, metric, n=10, attribute="centrality", **kwargs):
 centrality = metric(graph, **kwargs)
 nx.set_node_attributes(graph, attribute, centrality)
 degrees = sorted(centrality.items(), key=itemgetter(1), reverse=True)
 for idx, item in enumerate(degrees[0:n]):
 item = (idx+1,) + item
 print "%i. %s: %0.3f" % item
  1. 现在,我们可以简单地将此函数与我们的中心度度量结合使用,以根据它们的中心度找到nbest(默认为前 10 个)节点。 用法如下:
>>> nbest_centrality(graph, nx.degree_centrality)
>>> nbest_centrality(graph, nx.betweenness_centrality, normalized=True)
>>> nbest_centrality(graph, nx.closeness_centrality)
>>> nbest_centrality(graph, nx.eigenvector_centrality_numpy)

工作原理...

显然,Marvel Universe 中的次要字符要多得多,还有 100 个左右的主要字符。 有趣的是,这与现实世界相比非常好! 在现实世界的社交图中,具有更多联系且几乎没有联系的演员,与联系紧密的顶级梯队角色也相对较少。 我们很直观地知道这一点,我们将联系比例最高的百分之一的演员称为名人。

实际上,名人是极端的离群人,他们在社交图谱上施加如此强烈的影响,因此我们倾向于称其为超级节点。 超级节点具有以下特性:对于足够远的跃点距离,图的所有遍历都将不可避免地找到通过超级节点的最短路径。 这对于计算可能是非常糟糕的。 甚至更早的 Longbow 图也经历了巨大的计算滞后,由于 Longbow 连接到了两个超级节点:金刚狼和让·格雷,我们在绘制该图时也遇到了困难。 虽然这不仅证明了凯文·培根(Kevin Bacon)的六个分离度,但处理超级节点也是图计算的重要组成部分。

另一方面,我们可以为我们的社交网络计算邓巴的数字。 Dunbar 不仅针对人类社区,而且针对灵长类动物,确定并测量了新皮层体积与社会群体规模之间的相关性。 为了计算此数字,由于此数据集中的强烈左手偏斜,我们不能简单地计算所有节点的平均度。 让我们将均值,众数和中位数作为对 Dunbar 数的推论进行比较。 稍后,我们将研究如何使用加权关系来进一步建立牢固的联系:

>>> import numpy as np
>>> import scipy.stats as st
>>> data = np.array(graph.degree().values())
>>> np.mean(data)
52.0445066916
>>> st.mode(data)
(array([ 11.]), array([ 254.]))
>>> np.median(data)
20.0

邓巴的漫画英雄人数似乎比典型的社交图要低得多,但与自然比例图成正比(考虑到您在高中时的社交图可能小于成人的社交图)。 英勇的邓巴(Dunbar)的电话号码似乎在 11 到 20 之间。 难道是因为外来或突变的新皮层体积小于人类的体积? 还是因为我们的英雄因能力而自然与世隔绝,使他们与众不同?

还有更多...

查找图的关键参与者的最常见(也许是最简单)的技术是测量每个顶点的程度。 程度是确定节点连接方式的信号,可以是影响力或受欢迎度的隐喻。 至少,连接最紧密的节点是那些以最快的速度传播信息或对其社区产生最大影响的节点。 程度的度量往往会受到稀释的影响,并受益于统计技术来规范化数据集。

中间性中心

路径是开始节点和结束节点之间的一系列节点,其中没有节点在路径上出现两次,并由所包含的边数(也称为跃点)来衡量。 对于两个给定节点,最有趣的计算路径是最短路径,例如,到达另一个节点所需的最小边数,这也称为节点距离。

请注意,路径的长度可以为 0,即从节点到其自身的距离。 以下图为例:

DF之间的最短距离是路径 {D,C,E,F},该距离为三。 另一方面,从AE的最短路径是两条路径 {A,C,E}{A,B, E},它们的路径长度为(以紫色和红色突出显示)。

找到两个节点之间的最短路径会在上一节中提出一个问题。 如果经常遍历关键节点以找到最短路径,是否有基于最短路径的中心度度量? 答案是肯定的。 中间性中心性标识比其他节点更可能位于最短路径中的节点。 这不仅在发现社交图中的优点时非常有用,而且在发现删除中心节点时也会被切断的薄弱点非常有用。

给定节点的中间性中心度的计算如下。 对于V节点,中间性中心定义为通过 v 的所有最短路径对的分数之和。中间性中心性也可以针对图中的节点数进行归一化 ,或加权以接受边缘权重:

>>> centrality = nx.betweeenness_centrality(graph)
>>> normalied  = nx.betweenness_centrality(graph, normalized=True)
>>> weighted   = nx.betweenness_centrality(graph, weight="weight")

前面的命令将为您提供以下输出:

当为我们的小示例图计算中间性中心度时,BC具有良好的中心度得分(分别为 0.1 和 0.433)。 这是因为它们位于网络的很大一部分中。 但是E的中心得分为 0.6,这是因为E连接了网络的两个不同部分(GF)。

和以前一样,我们可以检查英雄图的中间性,并找到具有最高中间性的英雄:

注意

请注意,此计算非常昂贵,可能会在您的计算机上花费很长时间!

>>> nbest_centrality(graph, nx.betweenness_centrality, normalized=True)

以下是输出:

1\.  SPIDER-MAN/PETER PAR: 0.074
2\.  CAPTAIN AMERICA:      0.057
3\.  IRON MAN/TONY STARK:  0.037
4\.  WOLVERINE/LOGAN:      0.036
5\.  HAVOK/ALEX SUMMERS:   0.036
6\.  DR. STRANGE/STEPHEN:  0.029
7\.  THING/BENJAMIN J. GR: 0.025
8\.  HAWK:                 0.025
9\.  HULK/DR. ROBERT BRUC: 0.024
10\. MR. FANTASTIC/REED R: 0.024

与中心度结果相比,这些数字有很大的不同。 Spider-ManCaptain America在第一和第二位切换了位置,但仍与Iron Man共享前三名。 Wolverine已提升至第四位,Mr. Fantastic和 The Thing 已被降级。 然而,真正令人感兴趣的是HawkHulkDr. StrangeHavok的新出现,取代了野兽,雷神,猩红色的女巫和人类的火炬,这些人虽然很受欢迎 不能将字符链接在一起!

紧密度中心

另一个中心度度量(紧密度)是对特定节点 v 的出站路径进行统计分析。从 v 到达网络中任何其他节点的平均跳数是多少? 可以简单地计算为到图中所有其他节点的平均距离的倒数,可以将其标准化为 n-1 / size(G)-1,其中n为 如果图中的所有节点都已连接,则该邻域中的所有节点都将连接。 倒数可确保距离较近(例如,跳数较少)的节点得分更高,例如,与其他中心性得分相比,距离节点更近:

>>> centrality = nx.closeness_centrality(graph)
>>> normalied  = nx.closeness_centrality(graph, normalized=True)
>>> weighted   = nx.closeness_centrality(graph, distance="weight")

同样,当我们在英雄社交网络上运行此指标时,您应该发现它需要一段时间才能运行; 但是,如果使用归一化方法,则可以大大加快该过程:

>>> nbest_centrality(graph, nx.closeness_centrality)
1\.  CAPTAIN AMERICA:      0.584
2\.  SPIDER-MAN/PETER PAR: 0.574
3\.  IRON MAN/TONY STARK : 0.561
4\.  THING/BENJAMIN J. GR: 0.558
5\.  MR. FANTASTIC/REED R: 0.556
6\.  WOLVERINE/LOGAN :     0.555
7\.  HUMAN TORCH/JOHNNY S: 0.555
8\.  SCARLET WITCH/WANDA : 0.552
9\.  THOR/DR. DONALD BLAK: 0.551
10\. BEAST/HENRY &HANK& P: 0.549

再次,我们返回到原始列表,该列表是通过学位中心获得的。 在这种情况下,凯文·培根(Kevin Bacon)的规则适用。 这些非常受欢迎的超级节点名人将具有最大的影响力,也就是说,他们能够在最快的时间内到达所有其他节点。 但是,对于较小的图表,情况已经发生了变化:

在这里,我们看到CE是最接近中心的节点; 它们可以平等地到达所有其他节点。BCE距离近,但效果很好,而A由于具有两个连接,所以性能稍好 比DGF能够访问网络中的所有其他节点。

特征向量中心性

节点的特征向量中心度 v 与相邻节点的中心度分数之和成正比。 例如,与您联系的人越重要,您就越重要。 这种集中性度量非常有趣,因为具有少量极具影响力的联系人的演员可能会比具有更多平庸联系人的演员更胜一筹。 对于我们的社交网络,希望它将使我们深入了解英雄团队的名人结构,并了解谁真正将社交图谱整合在一起。

要计算特征向量中心性,请计算图的成对邻接矩阵特征分解的 argmax。 本征向量中的i元素给出了i节点的中心。 如果您熟悉 Google 的 PageRank,那么听起来应该很熟悉,实际上,PageRank 是一种经过改进的特征向量中心性度量,其中 Google 不用针对邻接矩阵进行计算,而是使用概率并计算整个矩阵的本征分解。 随机矩阵。

邻接矩阵是二维矩阵,其中每个节点的向量都是一个标志,指示它和另一个节点是否共享一条边。 无向图总是具有对称的邻接矩阵,而有向图则可能更复杂。 对于本节中的简单图形示例,这是邻接矩阵:

但是,我们不会实现用于计算特征向量中心性的算法,而是将依赖于 NetworkX 中已经内置的许多图形算法。 在这种情况下,我们有两种选择来计算特征向量中心度:

>>> centrality = nx.eigenvector_centality(graph)
>>> centrality = nx.eigenvector_centrality_numpy(graph)

首选方法是使用幂方法来找到特征向量,并且只会运行到预设的最大迭代次数,无法保证收敛。 第二种选择将使用 NumPy 特征值求解器。 您应尽可能使用第二种算法,以确保获得完整的结果。 您可能会问为什么要选择? NumPy 版本将继续运行直到收敛为止,这意味着它有可能因输入数据而卡住。 也可以制作 NumPy 版本以适合该解决方案。 但是,使用幂法的第一个原因是速度。 由于固定的迭代次数,幂方法通常比 NumPy 方法更快地求解特征值。 但是,求解器的这些属性都完全取决于图形(输入数据)的大小。 对于较大的图,您可能会被迫使用幂法,因为其他任何事情都将变得棘手。

注意,还有相应的pagerankpagerank_numpy模块功能可以计算这些分数。 让我们看看我们的英雄是如何做到的:

>>> nbest_centrality(graph, nx.eigenvector_centrality_numpy)
1\.  CAPTAIN AMERICA:      0.117
2\.  IRON MAN/TONY STARK:  0.103
3\.  SCARLET WITCH/WANDA:  0.101
4\.  THING/BENJAMIN J. GR: 0.101
5\.  SPIDER-MAN/PETER PAR: 0.100
6\.  MR. FANTASTIC/REED R: 0.100
7\.  VISION:               0.099
8\.  HUMAN TORCH/JOHNNY S: 0.099
9\.  WOLVERINE/LOGAN:      0.098
10\. BEAST/HENRY &HANK& P: 0.096

再一次,我们的前 10 名名单中有一些剧变! Captain America再次名列榜首,但Spider-Man输了不止一个地方,跌落了榜单的一半。 ThingScarlet WitchIron Man在列表中上移,并且令人惊讶的是,新演员Vision在列表中移动。

现在,较小的图对节点进行了更强的排名:

现在,BC是排名最高的节点。E的排名也很高,但没有以前那么重要,A比以前更重要。 此外,FDG的中心度得分均已超过零,这在确定阈值时可能很重要。

确定中心性算法

那么,您应该使用哪种集中度度量? 嗯,这取决于您要寻找的内容,因为每个中心化机制都旨在处理社交网络的不同功能。 以下是一些中心性度​​量:

  • 程度:这是衡量受欢迎程度的方法,它在确定可以快速将信息传播到本地区域(例如邻居)的节点时很有用。 想想名人; 这些是可以直接吸引许多人的节点。
  • 中间度:这表明哪些节点可能是信息的路径,并且可以用来确定如果删除该节点,图形将在何处破裂。 它还用于显示通往网络中其他群集或组的直接路径。
  • 接近度:这是一种覆盖范围的度量,即信息将从该特定节点传播到所有其他节点的速度。 具有最紧密中央关系的节点在广播通信期间享有较短的持续时间。
  • 特征向量:这是相关影响的量度。 谁最接近图中的最重要人物? 这可以用来显示幕后的力量,也可以用来显示超越人气的相对影响力。

对于许多分析,所有亲密性度量都可用于单个社交网络实现关键结果!

探索整个网络的特征


在下一组食谱中,我们将描述整个社交网络的特征,而不是从单个参与者的角度。 通常,此任务仅次于感受最重要的节点,但这是鸡和蛋的问题。 确定分析和拆分整个图形的技术可以通过关键参与者的分析来告知,反之亦然。

准备

如果您完成了前面的食谱,则可以继续进行此食谱了。

怎么做...

以下步骤将引导我们完成对整个图形级别的图形特征的首次探索:

  1. 让我们计算整个网络和自我图的密度:
>>> nx.density(graph)
0.00810031232554
>>> ego = nx.ego_graph(graph, "LONGBOW/AMELIA GREER")
>>> nx.density(ego)
0.721014492754

如您所见,我们的英勇社交网络并不十分密集。 总体来说不是很挑剔。 但是,Longbow 的社交网络非常密集且极富挑战性。 通常来说,密度是用来比较整个社交网络的子图(例如自我图),而不是用来作为特定图自身行为的模型。

  1. 还可以根据距离(两个节点之间的最短路径)来分析图。 图中最长的距离称为社交图的直径,它表示沿图的最长信息流。 通常,密度较低(稀疏)的社交网络的直径大于密度较高的社交网络的直径。 此外,平均距离是一个有趣的指标,因为它可以为您提供有关节点之间的距离的信息:
>>> for subgraph in nx.connected_component_subgraphs(graph):
...     print nx.diameter(subgraph)
...     print nx.average_shortest_path_length(subgraph)

diameter: 5
average distance: 2.638

请注意,我们的英雄社交图没有完全连接,有一些孤立的子图,因此,我们使用nx.connected_component_subgraphs生成器捕获每个子图。 您可以测试社交图是否与nx.is_connected(G)连接,并可以通过nx.number_connected_components确定组件数。 在英雄社会图中,有四个组成部分,但只有两个组成部分具有大量节点。

  1. 最后,我们可以计算网络的互惠性,即,往复关系的数量(例如,如果存在双向链接)与社交网络中关系总数的比率。 当前,没有内置的 NetworkX 方法来执行此计算。 但是,使用GraphNetworkX.DiGraph子类,此方法将起作用:
>>> unigraph = digraph.to_undirected()
>>> return len(unigraph.edges()) / len(digraph.edges())

to_undirected方法中的倒数标志可确保仅保留在两个方向上都出现的边缘。

此方法仅适用于有向图。 不幸的是,由于我们使用已知的关系,因此我们的英雄网络是完全互惠的,因此互惠为 1.00。

工作原理...

在本食谱中,我们研究了三种不同的图形特征。 网络的密度是网络中边缘的数量与网络中可能边缘的总数之比。 对于无向图,n顶点的图的可能边数为n(n-1)/2(除去有向图的除法)。 完美连接的网络(每个节点与其他节点共享一条边)的密度为 1,通常称为集团。

还可以根据距离(两个节点之间的最短路径)来分析图。 图中最长的距离称为社交图的直径,它表示沿图的最长信息流。 通常,密度较低(稀疏)的社交网络的直径大于密度较高的社交网络的直径。 此外,平均距离是一个有趣的指标,因为它可以为您提供有关节点彼此之间有多近的信息。

我们将讨论的最后一个社交网络衡量标准是互惠性。 这是往复关系(例如,存在双向链接)的关系数与社交网络中关系总数的比率。 这仅对有向图有意义。 例如,Twitter 社交网络是有向图。 您可以关注其他人,但这并不一定意味着他们也会关注您。 由于我们的英雄社交网络在语义上是无方向性的,因此我们无法执行此计算。

社交网络中的聚类和社区检测


图表现出聚类行为,而社区的识别是社交网络中的一项重要任务。 节点的聚类系数是节点邻域中的三元闭合数(闭合的三元组)。 这是传递性的表达。 具有较高可传递性的节点具有较高的子密度,如果完全封闭,则会形成可以识别为社区的集团。 在本食谱中,我们将研究社交网络中的聚类和社区检测。

准备

您将再次需要 NetworkX,并且在本章中第一次需要 python-louvain 库。

怎么做...

这些步骤将指导您检测社交网络中的社区:

  1. 让我们实际进入一些集群。 python-louvain库使用 NetworkX 通过louvain方法执行社区检测。 这是一个小型的内置社交网络上的集群分区的简单示例:
G = nx.karate_club_graph()

#first compute the best partition
partition = community.best_partition(G)

#drawing
pos = nx.spring_layout(G)
plt.figure(figsize=(12,12))
plt.axis('off')

nx.draw_networkx_nodes(G, pos, node_size=200, cmap=plt.cm.RdYlBu, node_color=partition.values())
nx.draw_networkx_edges(G,pos, alpha=0.5)
plt.savefig("figure/karate_communities.png")

以下是带有不同颜色的灰色和/或彩色阴影的图形,代表不同的分区:

这很整齐! 我们可以看到有黄色,浅蓝色和深红色的集团,但是深蓝色是相当同质的。 稍后,我们将详细讨论如何使用 matplotlib 创建图形可视化。

  1. 要对漫画人物进行分区,我们将其分区添加到它们的每个节点; 然后,我们将查看每个分区的相对大小:
>>> graph = graph_from_csv(HERO_NETWORK)
>>> partition = community.best_partition(graph)
>>> print "%i partitions" % len(set(partition.values()))

25 partitions
>>> nx.set_node_attributes(graph, 'partition', partition)

如您所见,louvain方法发现了 25 个没有我们社交图谱的社区。

  1. 要检查每个社区的相对规模,直方图视图可能会有所帮助。 要创建直方图,请在文件中添加以下功能:
import matplotlib.pyplot as plt

def communities_histogram(graph):
graph, partition = detect_communities(graph)
 numbins = len(partition.values())

 plt.hist(partition.values(), bins=numbins), color="#0f6dbc")
 plt.title("Size of Marvel Communities")
 plt.xlabel("Community")
 plt.ylabel("Nodes")

 plt.show()

这将产生下图:

有三个主要社区,每个社区包含上千个节点。 但是,我们也有大约 400 名演员组成的八个中型社区。 其他社区要小得多,但是 Marvel 社交图谱确实确实是现实世界和小世界图谱,就像人类文化中观察到的自然社交网络一样!

  1. 可视化 Marvel 社区规模的另一种基于区域的方法是使用气泡图。 每个圆圈的面积代表每个社区的大小。 如下所示的图通常用于将大型图折叠为基于社区的子图。 要创建此代码,请在文件中添加以下功能:
def communities_bubblechart(graph):
graph, partition = detect_communities(graph) 
parts = defaultdict(int)
for part in partition.values():
 parts[part] += 1 
bubbles = nx.Graph()
for part in parts.items():
 bubbles.add_node(part[0], size=part[1])
pos = nx.random_layout(bubbles)
plt.figure(figsize=(12,12))
plt.axis('off')

nx.draw_networkx_nodes(bubbles, pos,
alpha=0.6, node_size=map(lambda x: x*6, parts.
    values()),
node_color=[random.random() for x in parts.values()],
cmap=plt.cm.RdYlBu)

plt.show()

运行时,此代码应为我们的 Hero 网络产生以下图形:

工作原理...

NetworkX 具有多种计算群集的机制:

>>> nx.transitivity(graph)
0.194539747093
>>> nx.average_clustering(graph)
0.774654121711

nx.transitivity函数使用nx.triangles函数来计算三角形数量与可能三角形数量之比。 nx.clustering函数计算每个节点的聚类系数,nx_average_clustering函数计算图形的平均系数。 较高的传递性和聚类系数意味着该图表现出较小的世界效应。

小世界是一个看起来随机的网络,但它具有较高的传递性和较短的平均路径长度。 这是社交网络非常常见的结构,因为现代网络的语义具有紧密的联系,因此具有很强的可传递性。 从本质上讲,这意味着存在一系列带有很少桥节点的大型集群。 英雄的社交网络既表现出小世界效应,又表现出优先的依恋; 大多数新边都指向高度已经高的节点,因此创建了长尾,向左偏斜的分布,正如我们在图中看到的那样。

还有更多...

Louvian 方法是一种贪婪的优化方法,用于对网络进行分区,并优化每个网络分区的模块化。 首先,该方法通过尝试优化本地模块化来识别小型社区,然后将属于同一社区的节点聚合在一起,然后再次执行该过程。 这样,将返回社区的分层数据结构。 该方法简单,高效,并且可用于具有数十亿个链接的数百万个节点的大型网络。

最初的方法是由 UCL(Louvain-la-Neuve)的 Etienne Lefebvre 开发的; 然后与 Vincent Blondel,Jean-Loup Guillaume 和 Renaud Lambiotte 共同撰写和完善。 之所以称为“ Louvain 方法”,是因为该方法是在团队的所有成员都在卢瓦尔大学天主教堂学习时才设计的。 作者在一起创建了一个 Python 方法来计算这些分层社区,该方法依赖于 NetworkX。 基本的检测算法如下:

import community
import networkx as nx
def detect_communities(graph):
 partition = community.best_partition(graph)
nx.set_node_attributes(graph, 'partition', partition)
return graph, partition

该函数需要一个nx.Graph,并使用community模块来计算最佳的根分区。 分区是分层的,因此要获得子社区,我们只需遍历分区中的各个部分,并在我们的图中为节点分配一个名为partition的属性,该属性即可识别一个具有其社区的节点。 然后,该函数返回修改后的图形和分区以用于可视化。

可视化图表


在本章中,我们一直在可视化社交网络,以帮助发展我们对图形的理解和直觉。 在本食谱中,我们将更深入地研究图形可视化。

准备

确保已安装 NetworkXmatplotlib

怎么做...

完成以下步骤列表,以更好地理解 Python 中的图形可视化:

  1. NetworkX 使用与上一章相同的图表库包装 matplotlib 或 graphviz 来绘制简单图形。 这对于较小的图形有效,但是对于较大的图形,可以快速消耗内存。 要绘制小图,只需使用networkx.draw函数,然后使用pyplot.show显示它:
>>> import networkx as nx
>>> import matplotlib.pyplot as plt
>>> nx.draw(graph)
>>> plt.show()
  1. 但是,下面有一个丰富的图形库,可让您自定义图形的外观以及使用许多不同的布局算法进行布局的方式。 我们来看一个使用 NetworkX 库随附的社交图之一的示例,该图是戴维斯南方俱乐部女士图:
import networkx as nx
import matplotlib.pyplot as pl

# Generate the Graph
G=nx.davis_southern_women_graph()
# Create a Spring Layout
pos=nx.spring_layout(G)

# Find the center Node
dmin=1
ncenter=0
for n in pos:
 x,y=pos[n]
d=(x-0.5)**2+(y-0.5)**2
if d<dmin:
 ncenter=n
 dmin=d
  1. 接下来,我们将为图表着色。 首先,我们必须确定中心节点,因为我们将其最深地着色。 然后,所有较远的节点将被着色为浅色,直到它们变为白色。 弹簧布局已经确定了每个节点的(x,y)坐标,因此很容易计算每个节点到图形中心的欧几里得距离(在这种情况下为点(0.5, 0.5)),然后找到该节点 距离最短的 确定后,我们将计算每个节点到中心节点的跳数(例如,路径长度)。 nx.single_source_shortest_path_length函数返回节点的字典及其与作为参数提供的节点的距离。 然后,我们将使用这些距离来确定颜色:
p=nx.single_source_shortest_path_length(G,ncenter)
  1. 接下来,是时候绘制图形了。 我们创建一个matplotlib图形,然后使用 NetworkX 绘制功能绘制边缘。 然后,我们绘制节点。 此函数使用颜色图(cmap参数)来确定要使用的颜色范围,该范围由跳距确定:
plt.figure(figsize=(8,8))
nx.draw_networkx_edges(G,pos,nodelist=[ncenter],alpha=0.4)
nx.draw_networkx_nodes(G,pos,nodelist=p.keys(),
 node_size=90,
 node_color=p.values(),
 cmap=plt.cm.Reds_r) 
plt.show()

调用此函数将产生一个图形,如下图所示:

工作原理...

在前面的代码中,我们通过davis_southern_women_graph函数创建了一个图形G,它是 NetworkX 附带的许多内置图形生成函数之一。 然后,我们使用布局查找 G 中每个节点的位置。 布局是一种算法,旨在确定节点在图形上的位置,以创建有效的可视化。 默认情况下,NetworkX 带有五种定位算法。 圆形布局将节点放置在一个圆中,而外壳布局将节点放置在一个同心圆中。 随机布局使用 Laplacian 图的特征向量均匀地分布节点和频谱布局位置。

春天的布局也许是最常见的。 弹簧布局是一个受力控制的布局,这意味着每个节点都会排斥其周围的其他节点,而边将它们保持在一起。 将所有节点都放到该图上,并以递归方式计算排斥/吸引力。 对于每次迭代,节点将相互排斥并相互吸引,形成稳定的布局。 有几种力导向算法,但是 NetworkX 使用 Fruchterman-Reingold 算法。

R 中的社交网络


本简短部分的目的是帮助读者使用 R 统计软件执行本章前面完成的任务。 Luke(2015)是处理 R 中社交网络的全面说明。我们将以一个非常简单随意的社交网络开始,然后我们将详细考虑巴厘岛恐怖分子网络。

使用简单而随意的社交网络,我们设定了与 R 实施相关的目标。 首先介绍简单有用的功能。 下面详细介绍了下一个使用的社交网络。

巴厘岛恐怖组织Stuart Koschade 的博士学位论文可在这个页面处理 ] Jemaah Islamiyah 2002 巴厘岛恐怖主义网络。 这里的联系代表了巴厘岛恐怖组织之间的接触。 数据集由作为边缘特征的序数变量 IC(IC)测量接触的频率和持续时间,其中一个表示弱关系,一个逐渐表示较好的关系,五个表示最强的关系。 从技术上讲,R 网络对象Bali.rda可以从 Luke 的 GitHub 链接获得,他的书籍 R 包 UserNetR 也可以使用。 但是,我们仅向读者提供所需的 rda 文件。

通常用于分析社交网络的 R 包是snastatnetnetworkigraph。 有关更多详细信息,请参阅 Luke(2015)。

准备

执行此配方中的任务所需的 R 软件包为snastatnetnetworkigraph。 在 R 中安装这些软件包的简单方法是运行命令install.packages(c("sna","statnet","network","igraph"))。 还要求阅读器在当前工作目录中具有Bali.rda文件。

怎么做...

  1. 第一项任务是加载所需的库:
library(statnet,quietly=TRUE)
library(network,quietly=TRUE)

指定选项quietly=TRUE的目的是将大量注释打印到控制台,我们可以使用此选项来抵消它。 从操作上讲,我们是否使用此选项都没有区别。

  1. 接下来,我们指定一个由四行和四列组成的矩阵,如预期的那样是一个正方形矩阵,然后将其用于建立网络:
netmat1 <- rbind(c(0,1,1,0),
 c(1,0,1,0),
 c(1,1,0,1),
 c(0,0,1,0))
rownames(netmat1) <- c("A","B","C","D")
colnames(netmat1) <- c("A","B","C","D")
netmat1
  1. 结果是 R 控制台中的以下输出:
> netmat1
A B C D
A 0 1 1 0
B 1 0 1 0
C 1 1 0 1
D 0 0 1 0
  1. 下一步将使用该矩阵来建立社交网络。
  2. 使用相同的命名函数和包将矩阵netmat1转换为网络。 基本功能还应用于网络对象,以获取网络的基本特征:
net1 <- network(netmat1,matrix.type="adjacency")
class(net1)
summary(net1)
network.size(net1)
network.density(net1)
components(net1)
gplot(net1, vertex.col = 2, displaylabels = TRUE)

控制台中的 R 输出如下所示:

> net1 <- network(netmat1,matrix.type="adjacency")
> class(net1)
[1] "network"
> summary(net1)
Network attributes:
vertices = 4
directed = TRUE
hyper = FALSE
loops = FALSE
multiple = FALSE
bipartite = FALSE
total edges = 8 
missing edges = 0 
non-missing edges = 8 
density = 0.6666667 
Vertex attributes:
vertex.names:
character valued attribute
4 valid vertex names
No edge attributes
Network adjacency matrix:
 A B C D
A 0 1 1 0
B 1 0 1 0
C 1 1 0 1
D 0 0 1 0
> network.size(net1)
[1] 4
> network.density(net1)
[1] 0.6666667
> components(net1)
Node 1, Reach 4, Total 4
Node 2, Reach 4, Total 8
Node 3, Reach 4, Total 12
Node 4, Reach 4, Total 16
[1] 1
> gplot(net1, vertex.col = 2, displaylabels = TRUE)

在控制台上执行前面的 R 代码块,我们可以设置和检查网络的各个方面。 函数network帮助我们将矩阵netmat1转换为(社交)网络net1,此确认由class(net1)行提供,该行的输出表明对象的类别确实是network。 接下来,summary函数告诉我们网络告诉我们net1中有4vertices,网络是directed一个,并且总共有 8 个边 netmat1 矩阵中 1 的数目。 网络 net1 的大小和密度分别为40.6666667sna软件包中的gplot功能有助于可视化网络:

接下来,我们将看一个更复杂和实用的网络,并在 R 中进行分析。该网络是巴厘岛恐怖组织,其解释已在前面给出。 Bali.rda 文件来自 Luke 的软件包 UserNetR。 首先,将网络导入到 R 中,然后像对 net1 网络一样进行汇总统计。

  1. 加载Bali网络并执行以下基本摘要:
load("Bali.rda")
class(Bali)
Bali
# Basic Properties
network.size(Bali)
network.density(Bali)
# VIsualization
windows(height=30,width=30)
gplot(Bali,vertex.col=2, displaylabels = TRUE)

为简洁起见,我们仅给出图形输出,因为其余解释与网络net1相同:

  1. Bali 网络的矩阵形式可以如下获得:
Bali_Matrix <- as.matrix(Bali,matrix.type="adjacency")
Bali_Matrix[1:6,1:6]

输出如下:

Muklas Amrozi Imron Samudra Dulmatin Idris
Muklas 0 1 1 1 1 1
Amrozi 1 0 0 1 0 1
Imron 1 0 0 1 1 1
Samudra 1 1 1 0 1 1
Dulmatin 1 0 1 1 0 1
Idris 1 1 1 1 1 0

操作! 能够进行基本操作非常重要,我们将通过解决删除单个顶点的一项简单任务来加以约束。

  1. 首先将图形net1转换为netgigraph,然后使用plot函数将其可视化。 然后删除顶点D,并创建并可视化新的图形对象netg_del_d。 输出遵循以下 R 程序:
netg <- asIgraph(net1)
par(mfrow=c(1,2))
plot(netg,vertex.label=LETTERS[1:5])
netg_del_d <- delete.vertices(netg,v=5)
plot(netg_del_d,vertex.label=LETTERS[c(1:3,5)]) 

可以使用功能度,亲密性和介于两者之间的关系在 R 中识别强者和纽带。

  1. 应用功能degreeclosenessbetweenness,我们可以通过下一个 R 程序段来了解 Bali 网络:
Bali %v% 'vertex.names'
sna::degree(Bali,gmode="graph") 
closeness(Bali,gmode="graph")
betweenness(Bali,gmode="graph")

上一个程序的输出如下:

> Bali %v% 'vertex.names'
[1] "Muklas" "Amrozi" "Imron" "Samudra" "Dulmatin" "Idris"
[7] "Mubarok" "Husin" "Ghoni" "Arnasan" "Rauf" "Octavia"
[13] "Hidayat" "Junaedi" "Patek" "Feri" "Sarijo"
 > degree(Bali,gmode="graph") 
[1] 9 4 9 15 9 10 3 9 9 5 5 5 5 5 9 6 9
> closeness(Bali,gmode="graph")
[1] 0.6957 0.5517 0.6957 0.9412 0.6957 0.7273 0.5333 0.6957 0.6957
[10] 0.5714 0.5714 0.5714 0.5714 0.5714 0.6957 0.4848 0.6957
> betweenness(Bali,gmode="graph")
[1] 2.3333 0.3333 1.6667 61.1667 1.6667 6.1667 0.0000 1.6667
[9] 1.6667 0.0000 0.0000 0.0000 0.0000 0.0000 1.6667 0.0000
[17] 1.6667

网络群集识别是网络分析中的重要任务。 在下一步中,我们将使用适当的功能来获得所需的答案。

  1. 首先将网络转换为igraph功能。 在网络上应用功能clique.numbercliqueslargest.cliques来识别和分离集群:
Balig <- asIgraph(Bali)

clique.number(Balig)
 cliques(Balig,min=8)
 largest.cliques(Balig) 

所需的输出如下:

> Balig <- asIgraph(Bali)
> clique.number(Balig)
[1] 9
> cliques(Balig,min=8)
[[1]]
+ 8/17 vertices:
[1] 1 3 4 5 6 8 9 15
[[2]]
+ 8/17 vertices:
[1] 1 3 4 5 6 8 9 17
[[3]]
+ 8/17 vertices:
[1] 1 3 4 5 6 8 15 17
[[4]]
+ 8/17 vertices:
[1] 1 3 4 5 6 9 15 17
[[5]]
+ 8/17 vertices:
[1] 1 3 4 5 8 9 15 17
[[6]]
+ 8/17 vertices:
[1] 1 3 4 6 8 9 15 17
[[7]]
+ 8/17 vertices:
[1] 1 3 5 6 8 9 15 17
[[8]]
+ 8/17 vertices:
[1] 1 4 5 6 8 9 15 17
[[9]]
+ 8/17 vertices:
[1] 3 4 5 6 8 9 15 17
[[10]]
+ 9/17 vertices:
[1] 1 3 4 5 6 8 9 15 17
> largest.cliques(Balig)
[[1]]
+ 9/17 vertices:
[1] 4 6 1 3 5 8 9 15 17

工作原理...

R 网络软件包简化了大多数任务,并有助于避免编写复杂代码的需要。 我们看到了 R 软件包snaigraphintergraphstatnetnetwork在建立和分析社交网络中如何有用。 我们从可视化一个简单的网络开始,然后获得摘要。 子网络的子集化和可视化的任务为我们提供了很好的早期见解,同时我们也能够理解网络中顶点/边与子网络之间的相互关系。