背景

在逛 github 时突然发现另一个眼前一亮的可视化库 —— bqplot,同样提供了网络图的可视化 Python 接口,而且功能更加强大更好看,因此学习下 bqplot 中较为关注的 network graph 网络图可视化方法。

bqplot

「bqplot」 是基于图形语法构建的用于 Jupyter 的交互式 2D 绘图库,具有以下特点:

  • 用 Python 语言提供统一的可视化框架;
  • bqplot 利用 widget 基础提供第一个在 Python 和 JAVAScript 代码之间通信的绘图库;
  • bqplot 的可视化是基于 D3.js 和 SVG 的,支持快速交互和漂亮的动画;

源码仓库:https://github.com/bqplot/bqplot

官方文档:https://bqplot.readthedocs.io/

bqplot 提供非常多的交互图绘制接口,支持的图表格式如下所示:

  • Bars: Bar mark
  • Bins: Backend histogram mark
  • Boxplot: Boxplot mark
  • Candles: OHLC mark
  • FlexLine: Flexible lines mark
  • 「Graph: Network mark」
  • GridHeatMap: Grid heatmap mark
  • HeatMap: Heatmap mark
  • Hist: Histogram mark
  • Image: Image mark
  • Label: Label mark
  • Lines: Lines mark
  • Map: Geographical map mark
  • Market Map: Tile map mark
  • Pie: Pie mark
  • Scatter: Scatter mark
  • Mega Scatter: webgl-based Scatter mark

其它类型图表形式暂不关注,本文主要学习下其中的 「Network graph」 绘制方法。

Graph:Network mark 中需要提供节点和边数据。

节点属性如下

Attribute

Type

Description

Default

label

str

node label

mandatory attribute

label_display

{center, outside, none}

label display options

center

shape

{circle, ellipse, rect}

node shape

circle

shape_attrs

dict

node SVG attributes

{'r': 15}

边属性如下

Attribute

Type

Description

Default

source

int

source node index

mandatory attribute

target

int

target node index

mandatory attribute

value

float

value of the link. Use np.nan if you do not want a link

-

绘图实践

安装方式:pip install bqplot

首先导入必要的库

import numpy as np
from bqplot import Graph, LinearScale, ColorScale, Figure, Tooltip
from ipywidgets import Layout

fig_layout = Layout(width='600px', height='600px')

👇🏻以下学习下图各个属性的设置。

有向图

如果不指定节点位置默认为 force layout

node_data = [
    dict(label='A', shape='rect'),
    dict(label='B', shape='ellipse'),
    dict(label='C', shape='ellipse'),
    dict(label='D', shape='rect'),
    dict(label='E', shape='ellipse'),
    dict(label='F', shape='circle'),
    dict(label='G', shape='ellipse'),
]
link_data = [{'source': s, 'target': t} for s, t in np.random.randint(0, 7, (10, 2)) if s != t]
graph = Graph(node_data=node_data, link_data=link_data, charge=-600, colors=['lightblue'] * 7)
graph.link_type = 'arc' # arc, line, slant_line
Figure(marks=[graph], layout=fig_layout)

效果图如下:



python画网络部署图 python 网络图_python

固定位置

可以根据 x, y 坐标设定每个节点的绝对位置,注意设定位置后节点不可拖动。

node_data = list('ABCDEFG')

#using link matrix to set links
link_matrix = np.zeros((7, 7))
xs = LinearScale()
ys = LinearScale()
x = [80, 150, 200, 250, 250, 250, 300]
y = [3, 1.5, 5, 9, 7, 5, 2]
graph = Graph(node_data=node_data, link_matrix=link_matrix, link_type='arc', 
              colors=['lightblue'] * 7,
              scales={'x': xs, 'y': ys, }, x=x, y=y, 
              directed=True)

Figure(marks=[graph], layout=fig_layout)

效果图如下:

python画网络部署图 python 网络图_可视化_02

颜色模式

「点颜色模式」:设定图 color 属性值。

node_data = list('ABCDEFG')

#using link matrix to set links
link_matrix = np.zeros((7, 7))
xs = LinearScale(min=0, max=500)
ys = LinearScale(min=0, max=10)
cs = ColorScale(scheme='Reds')
x = [100, 200, 200, 300, 300, 300, 300]
y = [2, .5, 4, 8, 6, 4, 1]
graph3 = Graph(node_data=node_data, link_matrix=link_matrix, link_type='line', 
              color=np.random.rand(7),
              scales={'x': xs, 'y': ys, 'color': cs}, x=x, y=y, 
              directed=False)
Figure(marks=[graph3], layout=fig_layout)

效果图如下:



python画网络部署图 python 网络图_python画网络部署图_03

「边颜色模式」:设定图 link_color 属性值。

node_data = list('ABCDEFG')

link_data = [{'source': s, 'target': t, 'value': np.random.rand()} for s, t in np.random.randint(0, 7, (20, 2))]

xs = LinearScale()
ys = LinearScale()
lcs = ColorScale(scheme='Reds')
x = [100, 200, 200, 300, 300, 300, 300]
y = [2, .5, 4, 8, 6, 4, 1]
graph4 = Graph(node_data=node_data, link_data=link_data, link_type='line',
              colors=['lightblue'], directed=False, 
              scales={'x': xs, 'y': ys, 'link_color': lcs}, 
              x=x, y=y, color=np.random.rand(7))
Figure(marks=[graph4], layout=fig_layout)

效果图如下:



python画网络部署图 python 网络图_可视化_04

定制节点

可以自定义节点属性及其节点悬浮和点击的 Action。

node_data = [
    {'label': 'A', 'shape': 'circle', 'shape_attrs': {'r': 20}, 'foo': 1},
    {'label': 'B', 'shape': 'rect', 'shape_attrs': {'rx': 10, 'ry': 10, 'width': 40}, 'foo': 2},
    {'label': 'C', 'shape': 'ellipse', 'foo': 4},
    {'label': 'D', 'shape': 'rect', 'shape_attrs': {'width': 30, 'height': 30}, 'foo': 100},
]

link_data = [{'source': s, 'target': t, 'value': np.random.rand()} for s, t in np.random.randint(0, 4, (8, 2))]

graph5 = Graph(node_data=node_data, link_data=link_data, link_distance=150)
Figure(marks=[graph5], layout=fig_layout)

节点添加 tooltips:

tooltip = Tooltip(fields=['label', 'foo'], formats=['', '', ''])
graph5.tooltip = tooltip

为 tooltip 添加一个折线图:

import bqplot.pyplot as plt
plt.clear()
line = plt.plot(np.cumsum(np.random.randn(20)))
# hover on nodes to see the plot
graph5.tooltip = plt.current_figure()

设置选中、悬浮操作:

graph5.hovered_style = {'stroke': 'red'}
graph5.unhovered_style = {'opacity': '0.4'}

graph5.selected_style = {'opacity': '1', 'stroke': 'white', 'stroke-width': '2.5'}
graph5.selected

设置点击事件:

def print_event(self, target):
    print(target)

graph5.on_background_click(print_event)
graph5.on_element_click(print_event)

效果如下:

python画网络部署图 python 网络图_机器学习_05

总结

整体而言,bqplot 符合大部分绘图需求,存在的问题是网络图只能在 Jupyter 中交互,保存时格式只有 png 或者 svg,期待后续功能迭代...