现在各种商业和开源的网络可视化的工具非常多,NetworkX - NetworkX是一个非常重要的网络分析工具。当然它侧重于对网络结构的分析,也包含了网络可视化部分。我最近由于要画一些张量网络图,所以找到了这个工具。另一个值得推荐的是Latex中基于tikz实现的hackl/tikz-network工具,这个更侧重于画图,效果非常漂亮。这里重点介绍NetworkX如何可视化。至于网络拓扑结构的分析,专家可以仔细看相应文档,我只是为了画图,所以只用到关于图的最基本属性。本文共分三个部分,分别是:NetworkX中的基本对象:节点和边;
NetworkX中的可视化函数;
存在的一些问题。
NetworkX中的基本对象:节点和边
NetworkX主要是分析图的结果的,因此可以创建的图的种类很多,这里只利用Graph。在画图前,首先要建立一个网络图,代码如下。
import networkx as nx
G = nx.Graph()
这个时候的图是空的,其中没有任何节点(node)和连接或者说是边(edge)。所以接下来要给G中添加一些节点和边。方法非常多,我们介绍最基本的,add_node和add_edge。
G.add_node(1)
G.add_edge(2,3)
上面两段代码的意思分别是:
1行:增加一个节点1
2行:增加一条边从节点2到节点3,由于没有手动添加节点2,3。程序会自己自动添加2和3。
加入说要画四个原子形成的一维链,那么完整的代码为:
import networkx as nx
G = nx.Graph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
如果这里不用1,2,3,4,标记,用其它任何可hashable的对象都可以,我这里只是画图就不说了。
NetworkX中的可视化函数
如果要把刚才的网络画出来,那么直接调用draw函数即可代码和图如下:
import networkx as nx
from pylab import show
G = nx.Graph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
nx.draw(G)
show()
在默认的情况下,节点的位置是随机的,因此画出来的图并不是一条链,为了让确定的节点出现在特定的位置上,有两种做法。这里由于只是画图,我们利用draw中的pos参数修改,代码如下。注意这里pos是一个字典,key对应于节点的名字,后面是节点的坐标。
import networkx as nx
from pylab import show
G = nx.Graph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
position={1:(1,0),2:(2,0),3:(3,0),4:(4,0)}
nx.draw(G,pos=position)
show()
除了可以控制节点的位置,颜色大小,形状等属性都可以任意设置,比方下面的代码把上面的链条节点改成不同的颜色,大小。
import networkx as nx
from pylab import show
G = nx.Graph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
position={1:(1,0),2:(2,0),3:(3,0),4:(4,0)}
ncolor=['r','b','k','g']
nsize=[600,400,200,100]
nx.draw(G,pos=position,node_color=ncolor,node_shape='>',node_size=nsize)
show()
还可以给每一个节点添加一个名字等等这些通过参数可以设定就不一一介绍了。
如果我们只想画一些节点和一些边,并不想让所有的节点出现,那么可以通过nodelist这个参数控制。比方上面的图,我们不想出现最后一个节点,那么只需要把代码改为:
import networkx as nx
from pylab import show
G = nx.Graph()
G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(3,4)
position={1:(1,0),2:(2,0),3:(3,0),4:(4,0)}
ncolor=['r','b','k','g']
#nshape=['o','d','>','v']
nsize=[600,400,200,100]
NL=[1,2,3]
nx.draw(G,nodelist=NL,pos=position,node_color=ncolor,node_shape='>',node_size=nsize)
show()
存在的一些问题
如果仔细阅读NetworkX源代码会发现,可视化的方法利用了matplotlib散点图的画法。比方说所有的节点都是用散点图函数scatter来实现的。因此它的使用受限于scatter的能力。并且,在draw当中,接口的参数并没有覆盖scatter接口中所有参数。
比方scatter函数可以修改每一个marker的edgecolor,但是在draw当中就没有。因此如果我们想实现一个facecolor为白色,edgecolor为黑色的node,就必须修改NetworkX的代码。而这一点恰好是我需要的,因为你要给每个node标记一个名字的话,显然白底黑边比较合适。
再比如散点图本身默认的功能,只能实现所有的marker形状是相同的,因此在画散点图的时候,要控制不同的点对应不同的marker,需要使用mask函数过滤。例如Scatter Masked - Matplotlib 2.1.1 documentation。在NetworkX中也不能直接实现,不过可以通过另外两个函数draw_networkx_nodes和draw_networkx_edges分别画节点和边。或者在Graph中添加node和edge的时候,顺便加上相应的属性。