使用 Matplotlib 创建交互式直方图

Beginner

This tutorial is from open-source community. Access the source code

简介

在本实验中,我们将学习如何使用 Matplotlib 创建交互式直方图。交互性通过 ECMAScript(JavaScript)编码,并在后期处理步骤中插入到 SVG 代码中。生成的直方图将具有可以通过点击图例标记来隐藏或显示的条形。

虚拟机提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,请随时向 Labby 提问。课程结束后提供反馈,我们将立即为你解决问题。

创建直方图、图例和标题

首先,我们将使用 Matplotlib 创建直方图、图例和标题。我们还将使用set_gid()方法为每个对象分配 ID。这将有助于关联在 Python 中创建的 Matplotlib 对象和在第二步中解析的相应 SVG 结构。

import matplotlib.pyplot as plt
import numpy as np

## Fixing random state for reproducibility
np.random.seed(19680801)

## Create histogram, legend, and title
plt.figure()
r = np.random.randn(100)
r1 = r + 1
labels = ['Rabbits', 'Frogs']
H = plt.hist([r, r1], label=labels)
containers = H[-1]
leg = plt.legend(frameon=False)
plt.title("From a web browser, click on the legend\n"
          "marker to toggle the corresponding histogram.")

## Assign IDs to the SVG objects we'll modify
hist_patches = {}
for ic, c in enumerate(containers):
    hist_patches[f'hist_{ic}'] = []
    for il, element in enumerate(c):
        element.set_gid(f'hist_{ic}_patch_{il}')
        hist_patches[f'hist_{ic}'].append(f'hist_{ic}_patch_{il}')

## Set IDs for the legend patches
for i, t in enumerate(leg.get_patches()):
    t.set_gid(f'leg_patch_{i}')

## Set IDs for the text patches
for i, t in enumerate(leg.get_texts()):
    t.set_gid(f'leg_text_{i}')

为直方图添加交互性

接下来,我们将通过使用 ECMAScript(JavaScript)修改 SVG 代码,为直方图添加交互性。我们将使用set()方法为补丁和文本对象添加属性。我们还将创建一个全局变量container,用于存储属于每个直方图的补丁 ID。最后,我们将创建一个函数toggle_hist(),用于设置每个直方图的所有补丁的可见性属性以及标记本身的不透明度。

from io import BytesIO
import json
import xml.etree.ElementTree as ET

plt.rcParams['svg.fonttype'] = 'none'

## Save SVG in a fake file object
f = BytesIO()
plt.savefig(f, format="svg")

## Create XML tree from the SVG file
tree, xmlid = ET.XMLID(f.getvalue())

## Add attributes to the patch objects
for i, t in enumerate(leg.get_patches()):
    el = xmlid[f'leg_patch_{i}']
    el.set('cursor', 'pointer')
    el.set('onclick', "toggle_hist(this)")

## Add attributes to the text objects
for i, t in enumerate(leg.get_texts()):
    el = xmlid[f'leg_text_{i}']
    el.set('cursor', 'pointer')
    el.set('onclick', "toggle_hist(this)")

## Create script defining the function `toggle_hist`
script = """
<script type="text/ecmascript">
<![CDATA[
var container = %s

function toggle(oid, attribute, values) {
    /* Toggle the style attribute of an object between two values.

    Parameters
    ----------
    oid : str
      Object identifier.
    attribute : str
      Name of style attribute.
    values : [on state, off state]
      The two values that are switched between.
    */
    var obj = document.getElementById(oid);
    var a = obj.style[attribute];

    a = (a == values[0] || a == "")? values[1] : values[0];
    obj.style[attribute] = a;
    }

function toggle_hist(obj) {

    var num = obj.id.slice(-1);

    toggle('leg_patch_' + num, 'opacity', [1, 0.3]);
    toggle('leg_text_' + num, 'opacity', [1, 0.5]);

    var names = container['hist_'+num]

    for (var i=0; i < names.length; i++) {
        toggle(names[i], 'opacity', [1, 0])
    };
    }
]]>
</script>
""" % json.dumps(hist_patches)

## Add a transition effect
css = tree.find('.//{http://www.w3.org/2000/svg}style')
css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \
    "-moz-transition:opacity 0.4s ease-out;}"

## Insert the script and save to file
tree.insert(0, ET.XML(script))
ET.ElementTree(tree).write("svg_histogram.svg")

查看交互式直方图

最后,我们可以通过在网页浏览器中打开 SVG 文件来查看交互式直方图。要隐藏或显示条形,只需点击图例标记即可。

总结

在本实验中,我们学习了如何使用 Matplotlib 创建交互式直方图。我们通过修改 SVG 代码,使用 ECMAScript(JavaScript)为直方图添加了交互性。生成的直方图具有可以通过点击图例标记来隐藏或显示的条形。