总结一下图算法库NetworkX和graph-tool的基础用法。参考资料来自于官方文档。
NetworkX文档,graph-tool文档
1.NetworkX
1.1 NetworkX基础
NetworkX包括4中graph类:
Graph:无向图。两条节点之间只允许有一条边。允许节点有边指向自己。
DiGraph:有向图,Graph的子类。
MultiGraph:允许两个节点之间有多条无向边。
MultiDiGraph:MultiGraph有向图版本。
创建空的图对象:
G = nx.Graph()
G = nx.DiGraph()
G = nx.MultiGraph()
G = nx.MultiDiGraph()2.graph-tool
节点可以是任意可哈希对象,包含字符串,元祖,数字等。边等关联属性包括权重、标签等。
1.2图
图的内部数据结构基于临接表表示法,使用python字典实现。图主要由节点和边组成,节点和边都有各自的属性。NetworkX图形对象主要有两种重要属性:
Directed:节点之间是否是有向边。有向图的类用“Di”作为前缀。
Multi-Edge:节点对之间是否允许多条边连接。这种类型的数据结构和接口使用“Multi”前缀。
1.3节点和边
节点:如果只关心图的拓扑接口,可以使用字符串或数字来表示节点。如果已经定义了描述节点的数据结构,可以直接使用该结构作为节点,只要其是可哈希的(hashable)。如果其不是可哈希的,可以使用唯一标识符表示节点,将数据附值为节点属性。
边:任意数据都可以作为边都属性。如果属性是数值型且图形为加权图,那么使用“weight”关键字作为属性。一些图数据算法,比如Dijkstra的最短路径算法,默认情况下使用这个属性来获得每条边的权值。
可以使用关键字来命名属性,然后使用关键字来查询边都属性。
1.4图创建
三种创建图的方式:
1.Graph generator
2.Importing data from pre-existing (usually file) sources.
3.显示创建边和节点
下面举例说明一些图的基本操作:
显示增加节点或边
import networkx as nx
G = nx.Graph()
G.add_edge(1, 2) # default edge data=1
G.add_edge(2, 3, weight=0.9) # specify edge data
边的属性值可以为任意对象
import math
G.add_edge('y', 'x', function=math.cos)
G.add_node(math.cos) # any hashable can be a node
可以同时添加多条边
elist = [(1, 2), (2, 3), (1, 4), (4, 2)]
G.add_edges_from(elist)
elist = [('a', 'b', 5.0), ('b', 'c', 3.0), ('a', 'c', 1.0), ('c', 'd', 7.3)]
G.add_weighted_edges_from(elist)
更多例子参考Turtorial
基本的图操作,比如交集和并集,参考Operators Module
图生成器(graph generator)subpackage中提供了诸如binomial_graph()和erdos_renyi_graph()这样的图生成器。
从GML, GraphML, edge list text files等文件导入数据到图网络中,参考reading and writing graphs
1.5 graph reporting
视图提供节点、邻居、边缘和度的基本报告。视图提供的功能包括属性的迭代,图元素的查询和属性的检索,视图引用图的数据结构,所以改变图可以直接反映到视图中。
视图相关操作:
# 返回集合对象
for e in list(G.edges):
# 属性的检索
G.edges[u, v]['color']
for e, datadict in G.edges.items():
# 返回字典对象
G.edges.items()
G.edges.values()
# 属性迭代
for e, e_color in G.edges.data('color'):
一个边的基本图关系可以通过两种方法得到:通过查找节点的邻居或者直接查找边。NetworkX的设计者倾向于以节点为中心,将边视为节点之间的关系。可以通过查找的表示来看到这一点。比如G[u]用来查找邻接点,G.edges[u, v]用来查找边。稀疏图的大部分数据结构是邻接表。G.edges删除了无向图的重复节点。
具体参考Algorithm
1.6 算法
NetworkX提供了大量的图数据算法。其中包括最短路径、广度优先搜索(见遍历)、聚类和同构算法等。
以下是Dijkstra最短路径算法的一个例子:
>>> G = nx.Graph()
>>> e = [('a', 'b', 0.3), ('b', 'c', 0.9), ('a', 'c', 0.5), ('c', 'd', 1.2)]
>>> G.add_weighted_edges_from(e)
>>> print(nx.dijkstra_path(G, 'a', 'd'))
['a', 'c', 'd']
1.7 画图
NetworkX提供来一些绘图包接口和一些简单的布局算法。绘图包在drawing
画图举例:
>>> import matplotlib.pyplot as plt
>>> G = nx.cubical_graph()
>>> plt.subplot(121)
<matplotlib.axes._subplots.AxesSubplot object at ...>
>>> nx.draw(G) # default spring_layout
>>> plt.subplot(122)
<matplotlib.axes._subplots.AxesSubplot object at ...>
>>> nx.draw(G, pos=nx.circular_layout(G), node_color='r', edge_color='b')
1.8 数据结构
Network使用“dictionary of dictionaries of dictionaries”作为基础图数据结构,方便用于大规模稀疏网络的查找。
G[u][v]:返回边属性。
n in G:检查节点n是否在图G中
for n in G:遍历图节点
for nbr in G[n]:遍历邻接点
# 打印图G中所有邻接信息
>>> G = nx.Graph()
>>> G.add_edge('A', 'B')
>>> G.add_edge('B', 'C')
>>> print(G.adj)
{'A': {'B': {}}, 'B': {'A': {}, 'C': {}}, 'C': {'B': {}}}
G[u][v]['width'] 等价于 G.edges[u, v]['width']. 如:
>>> G = nx.Graph()
>>> G.add_edge(1, 2, color='red', weight=0.84, size=300)
>>> print(G[1][2]['size'])
300
>>> print(G.edges[1, 2]['color'])
red
2. graph-tool
参考quick-start-using-graph_tool
graph-tool类提供了Graph类和相应的图操作方法。大多数算法的机制由C++编写,使用Boost图库来提高性能。
2.1 创建和操作图
g = Graph() # 创建图
默认为有向图,使用directed参数指定无向图。
>>> ug = Graph(directed=False)
有向图转为无向图
>>> ug = Graph()
>>> ug.set_directed(False)
>>> assert ug.is_directed() == False2.2 属性映射
使用已有的图对象创建图
>>> g1 = Graph()
>>> # ... construct g1 ...
>>> g2 = Graph(g1) # g1 and g2 are copies
创建节点,返回节点对象
>>> v1 = g.add_vertex()
>>> v2 = g.add_vertex()
添加边
>>> e = g.add_edge(v1, v2)
图的可视化
>>> graph_draw(g, vertex_text=g.vertex_index, vertex_font_size=18,
... output_size=(200, 200), output="two-nodes.png")
图的出度和入度
>>> print(v1.out_degree())
>>> print(v2.in_degree())
边的source节点和target节点
>>> print(e.source(), e.target())
add_vertex方法添加节点,可选参数为待添加节点的数量。若添加节点的数量大于1,则返回迭代器对象。
>>> vlist = g.add_vertex(10)
>>> print(len(list(vlist)))
10
每个节点在graph-tool中,都有唯一索引,通常标识为0~N-1,N是节点数量。该索引值可以通过vertex_index[v]和int(v)获取。
>>> v = g.add_vertex()
>>> print(g.vertex_index[v])
12
>>> print(int(v))
12
使用remove_vertex和remove_edge方法删除节点和边。
>>> g.remove_edge(e) # e no longer exists
>>> g.remove_vertex(v2) # the second vertex is also gone
vertex方法通过节点索引来获取节点对象
>>> v = g.vertex(8)
edge方法通过给定或source节点和target节点都索引来获取边。
>>> g.add_edge(g.vertex(2), g.vertex(3))
<...>
>>> e = g.edge(2, 3)
同时,获取节点或者边可以通过迭代方法,参考Iterating over vertices and edges
与节点类似,边也有唯一索引。edge_index获取边都索引。如果删除了某条边,其索引为空,其他边都索引不变。
>>> e = g.add_edge(g.vertex(0), g.vertex(1))
>>> print(g.edge_index[e])
1
2.1.1 迭代顶点和边
迭代所有的顶点或边
for v in g.vertices():
print(v)
for e in g.edges():
print(e)
迭代顶点的邻接点。out_edges, out_neighbors, in_edges, in_neighbors方法
for v in g.vertices():
for e in v.out_edges():
print(e)
for w in v.out_neighbors():
print(w)
# the edge and neighbors order always match
for e, w in zip(v.out_edges(), v.out_neighbors()):
assert e.target() == w
快速迭代节点和边
graph-tool提供了基于数组的接口,比如get_vertices(), get_edges(), get_out_edges(), get_in_edges(), get_all_edges(), get_out_neighbors(), get_in_neighbors(), get_all_neighbors(), get_out_degrees(), get_in_degrees() and get_total_degrees()
这些方法返回numpy.ndarray的实例而不是迭代器。
2.2 属性映射
属性映射是将附加信息关联到顶点、边或图本身的一种方式。有三种类型的属性映射:顶点、边和图。它们由类VertexPropertyMap、EdgePropertyMap和GraphPropertyMap处理。每个创建的属性映射都有一个关联的值类型,必须从预定义的集合中选择:
创建新的属性映射的方法:
new_vertex_property: 别名new_vp
new_edge_property: 别名new_ep
new_graph_property: 别名new_gp
属性映射,可以通过顶点、边或图进行赋值。
from numpy.random import randint
g = Graph()
g.add_vertex(100)
# insert some random links
for s,t in zip(randint(0, 100, 100), randint(0, 100, 100)):
g.add_edge(g.vertex(s), g.vertex(t))
vprop_double = g.new_vertex_property("double") # Double-precision floating point
v = g.vertex(10)
vprop_double[v] = 3.1416
vprop_vint = g.new_vertex_property("vector<int>") # Vector of ints
v = g.vertex(40)
vprop_vint[v] = [1, 3, 42, 54]
eprop_dict = g.new_edge_property("object") # Arbitrary Python object.
e = g.edges().next()
eprop_dict[e] = {"foo": "bar", "gnu": 42} # In this case, a dict.
gprop_bool = g.new_graph_property("bool") # Boolean
gprop_bool[g] = True
可以通过numpy.ndarray赋值
from numpy.random import random
# this assigns random values to the vertex properties
vprop_double.get_array()[:] = random(g.num_vertices())
# or more conveniently (this is equivalent to the above)
vprop_double.a = random(g.num_vertices())
通过edge_properties, vertex_properties, graph_properties,将属性映射内化到图中。
>>> eprop = g.new_edge_property("string")
>>> g.edge_properties["some name"] = eprop
>>> g.list_properties()
some name (edge) (type: string)
内部图属性映射略有不同。返回的不是属性映射,而是值本身。
>>> gprop = g.new_graph_property("int")
>>> g.graph_properties["foo"] = gprop # this sets the actual property map
>>> g.graph_properties["foo"] = 42 # this sets its value
>>> print(g.graph_properties["foo"])
42
>>> del g.graph_properties["foo"] # the property map entry is deleted from the dictionary
内部属性映射也可以通过属性访问(下面代码中的foo可能是属性映射名称)
>>> vprop = g.new_vertex_property("double")
>>> g.vp.foo = vprop # equivalent to g.vertex_properties["foo"] = vprop
>>> v = g.vertex(0)
>>> g.vp.foo[v] = 3.14
>>> print(g.vp.foo[v])
3.14
2.3 图IO
图的保存和加载。
g = Graph()
# ... fill the graph ...
g.save("my_graph.xml.gz")
g2 = load_graph("my_graph.xml.gz")
# g and g2 should be copies of each other
2.4 例子:building a price network
略。
2.5 图过滤
略。