一个图G = (V, E)由一些点及点之间的连线(称为边)构成,V、E分别计G的点集合和边集合。在图的概念中,点的空间位置,边的区直长短都无关紧要,重要的是其中有几个点以及那些点之间有变相连。
图1:图示例
最基本的图通常被定义为“无向图”,与之对应的则被称为“有向图”。两者唯一的区别在于,有向图中的边是有方向性的。
图2:有向图和无向图
注:上图左边为无向图,右边为有向图。黑色加粗部分表示边的方向。比如:1—>2便是边是1到2这个方向。
度是相对于图中点的概念,图中任意一点v的度是指:与v相连的边的条数。在有向图中与顶点v出关联的边的数目称为出度,与顶点v入关联的边的数目称为入度。比如上图2:左边无向图顶点2的度是3.右边有向图点点2的出度是2,入度是1.
在图G中,若顶点u,v之间有路(即找到有u到v之间相连的边)则称u,v连通。若G的任何两点之间有路,则称G是连通图。G的极大连通子图称为连通分支。如果连通图是有向图则称G是强连通的。
在图上任取两顶点,分别作为起点和终点,我们可以规划许多条由起点到终点的路线。不会来来回回绕圈子、不会重复经过同一个点和同一条边的路线,就是一条“路径”,这些路径中经过顶点最少的那个路径就是最短路径。
如果路径上的各顶点均不互相重复,称这样的路径为简单路径。如果路径上的第一个顶点与最后一个顶点重合,这样的路径称为回路(cycle)或环或圈。比如下图中:(1,2,3,4,5,1),(1,2,3,1),(1,3,4,5,1)等都是简单路径。
图3:简单路径
一个节点的偏心距就是这个节点到其他节点的所有节点的最短路径的最大值。
图的所有节点偏心距的最大值就是图的直径,最小值就是半径。
在图论中,紧密度是图中一个节点的中心性度量。比其他节点更“浅”(也就是说,有更短的测地距离)的节点有更高的紧密度。在网络分析中,紧密度倾向于表示最短路径长度,因为这样会有更多的中心节点赋予更高的值,而且通常与其他度量(比如:度)相联系。紧密度是中心性的一种复杂度量。它被定义为节点v到其它可达节点的平均测地距离(比如:最短路径):
其中当n>=2是从v出发在网络中连通部分V的大小。接近中心性需要考量每个结点到其它结点的最短路的平均长度。也就是说,对于一个结点而言,它距离其它结点越近,那么它的中心度越高。一般来说,那种需要让尽可能多的人使用的设施,它的接近中心度一般是比较高的。
对于n各节点的图G=(V, E),节点v的介数CB(v)按如下方式计算:
公式表示为:
其中:σst是s到t的最短路径数,σst()是s到t的最短路径中经过v的数量。它可以除以不包括节点v的节点数量(对于无向图是(n-1)(n-2)/2有向图是(n-1)(n-2)类归一化。)中介中心性指的是一个结点担任其它两个结点之间最短路的桥梁的次数。一个结点充当“中介”的次数越高,它的中介中心度就越大。
度中心性(Degree Centrality)是在网络分析中刻画节点中心性(Centrality)的最直接度量指标。一个节点的节点度越大就意味着这个节点的度中心性越高,该节点在网络中就越重要。
算法步骤:
例如:下图
图:BFS搜索
算法步骤:
实例:用下图作为说明
图:DFS搜索
class Mygraph(object):
def __init__(self, *args, **kwargs):
self.node_neighbors = {}
self.visited = {}
def nodes(self):
return self.node_neighbors
def add_node(self, node):
if not node in self.nodes():
self.node_neighbors = []
def add_nodes(self, nodelist):
for node in nodelist:
self.add_node(node)
def add_edge(self, edge):
u, v = edge
if u not in self.nodes():
self.node_neighbors[u] = []
if v not in self.nodes():
self.node_neighbors[v] = []
if (v not in self.node_neighbors[u]) and (u not in self.node_neighbors[v]):
self.node_neighbors[u].append(v)
if u != v:
self.node_neighbors[v].append(u)
def add_edges(self, edges):
for edge in edges:
self.add_edge(edge)
def depth_first_search(self, root=None):
order = []
def dfs(node):
self.visited[node] = True
order.append(node)
for n in self.node_neighbors[node]:
if not n in self.visited:
dfs(n)
if root:
dfs(root)
for node in self.nodes():
if not node in self.visited:
dfs(node)
return order
def breath_first_search(self, root=None):
queue = []
order = []
def bfs():
while len(queue)>0:
node = queue.pop(0)
self.visited[node] = True
for n in self.node_neighbors[node]:
if (not n in self.visited) and (not n in queue):
queue.append(n)
order.append(n)
if root:
queue.append(root)
order.append(root)
bfs()
for node in self.nodes():
if not node in self.visited:
queue.append(node)
order.append(node)
bfs()
return order
if __name__=='__main__':
edges = [(1, 2), (1, 3), (2, 3),
(2, 5), (3, 4), (4, 2),
(4, 5), (5, 3)]
G = Mygraph()
G.add_edges(edges)
print(G.nodes())
print("广度优先遍历:")
bfs_order = G.breath_first_search(1)
print(bfs_order)
G = Mygraph()
G.add_edges(edges)
dfs_order = G.depth_first_search(1)
print("深度优先遍历:")
print(dfs_order)
注:为什么创建图写了两遍,因为只写一次,进行广度优先访问之后,有些会被标记为TRUE,会影响后面深度访问结果。
NetworkX是一款Python的软件包,用于创造、操作复杂网络,以及学习复杂网络的结构、动力学及其功能。 有了NetworkX你就可以用标准或者不标准的数据格式加载或者存储网络,它可以产生许多种类的随机网络或经典网络,也可以分析网络结构,建立网络模型,设计新的网络算法,绘制网络等等
方式一:pip install networkx就行
方式二:安装Anaconda,本身已经集成了这个包,十分方便。
G = nx.Graph() # 创建无向图(nx.DiGraph() 创建有向图)
G.add_node(0) # 添加一个节点
G.add_nodes_from([1, 2])# 一次添加多个节点
G.add_edge(0, 1) # 添加一条边
G.add_edge(2, 3) # 如果边的节点已经存在,直接覆盖
G.add_edge(4, 5) # 如果边的节点不存在,则添加新节点
G.add_edges_from([(2, 1), (5, 1), (0, 4), (3, 4)]) #添加多条边基于上面添加的节点和边绘制有向图和无向图如下:
注:左图表示有向图,黑色加粗部分表示边的方向。右图表示无向图没有方向之分。图的显示还有两个比较好用的工具就是Cytoscape和Gephi也比较好用,显示图像方便又美观,其中Cytoscape可以读取CSV文件,可以对图进行拖拽。感兴趣的朋友可以研究一下。
部分原始数据如图:
图:整体关系图
图:各个节点的度
图:各个节点的偏心距
上代码:
import re
import networkx as nx
import matplotlib.pyplot as plt
def create_graph():
G = nx.Graph()
# G = nx.DiGraph()
G.add_node(0) # 添加一个节点
G.add_nodes_from([1, 2])# 一次添加多个节点
G.add_edge(0, 1) # 添加一条边
G.add_edge(2, 3) # 如果边的节点已经存在,直接覆盖
G.add_edge(4, 5) # 如果边的节点不存在,则添加新节点
G.add_edges_from([(2, 1), (5, 1), (0, 4), (3, 4)]) #添加多条边
nx.draw(G, pos=nx.spring_layout(G), with_labels=True)
plt.show()
def read_nodes(filename):
'''读取文件,获取边列表'''
edges_list = []
with open(filename, 'r') as f:
while True:
line = f.readline()
if line:
line = re.sub('\r\n', '', line)
lines = line.split(',')
edges_list.append((lines[0], lines[1]))
edges_list.append((lines[1], lines[0]))
else:
break
return edges_list
‘’’注:因为networkx中求最大连通子图的实现都是基于有向图的,所以在读取数据的时候,添加边的时候都是双向的,这样保证求出来的最大连通子图和无向图是一样的。’’’
def get_graph_attr(edges):
# 1根据边的列表创建无向图
G = nx.DiGraph()
G.add_edges_from(edges)
# 2 查看图中的节点有多少个
nodes = G.nodes()
print(len(nodes)) # 107
# 2 求无向图的最大连通子图
max_component = max(nx.strongly_connected_component_subgraphs(G), key=len)# 最大连通子图
print(len(max_component.nodes())) # 107最大连通子图就是本身
# 3 将图转换为无向图
G = nx.to_undirected(max_component)
# 4 计算图中节点的度,按大小排序
degrees = G.degree() # 所有节点的度
print(sorted(degrees, key=lambda x:x[1], reverse=True))
# 5 计算图的偏心距和直径以及半径
eccdis = nx.eccentricity(G) # 偏心距
print(eccdis)
diamter = max(eccdis.values()) # 直径
radius = min(eccdis.values()) # 半径
print(diamter, radius) # 6, 3
#5 计算图中节点的最短路径
path = nx.shortest_path(G, source='Jon')# 查看谁到谁的最短路径
print(path)
path_length = nx.shortest_path_length(G, source='Jon')# 查看最短路径的长度
print(path_length)
#6 计算图中节点的紧密中心性
close = nx.closeness_centrality(G)#紧密中心性
print(close)
# 7 介数中心性
jie = nx.betweenness_centrality(G)
print(jie)
if __name__=='__main__':
filename = './inputdata/stormofswords.csv'
edges = read_nodes(filename)
get_graph_attr(edges)
Louvain算法是基于模块度的社区发现算法,该算法在效率和效果上都表现较好,并且能够发现层次性的社区结构,其优化目标是最大化整个社区网络的模块度。
模块度是评估一个社区网络划分好坏的度量方法,它的物理含义是社区内节点的连边数与随机情况下的边数只差,它的取值范围是 [−1/2,1)其公式如下:
其中,Aij节点i和节点j之间边的权重,网络不是带权图时,所有边的权重可以看做是1;ki=∑jAij表示所有与节点i相连的边的权重之和(度数);ci表示节点i所属的社区;m=12∑ijAij表示所有边的权重之和(边的数目)。公式中Aij−kikj2m=Aij−kikj2m,节点j连接到任意一个节点的概率是kj2m,现在节点i有ki的度数,因此在随机情况下节点i与j的边为kikj2m.
1)将图中的每个节点看成一个独立的社区,次数社区的数目与节点个数相同;
2)对每个节点i,依次尝试把节点i分配到其每个邻居节点所在的社区,计算分配前与分配后的模块度变化ΔQ,并记录ΔQ最大的那个邻居节点,如果maxΔQ>0,则把节点i分配ΔQ最大的那个邻居节点所在的社区,否则保持不变;
3)重复2),直到所有节点的所属社区不再变化;
4)对图进行压缩,将所有在同一个社区的节点压缩成一个新节点,社区内节点之间的边的权重转化为新节点的环的权重,社区间的边权重转化为新节点间的边权重;
5)重复1)直到整个图的模块度不再发生变化。
图:算法过程图
基于2.2权利的游戏的任务关系网络进行Louvain算法社团划分。算法源码参考2可以找到。这里就直接用了看下效果。
总共107个角色,划分了6个社团。
参考: