Criando um Histograma Interativo com Matplotlib

Beginner

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

Introdução

Neste laboratório, aprenderemos como criar um histograma interativo usando Matplotlib. A interatividade é codificada em ECMAScript (JavaScript) e inserida no código SVG em uma etapa de pós-processamento. O histograma resultante terá barras que podem ser ocultadas ou exibidas clicando nos marcadores da legenda.

Dicas para a VM

Após a inicialização da VM, clique no canto superior esquerdo para mudar para a aba Notebook e acessar o Jupyter Notebook para praticar.

Às vezes, pode ser necessário aguardar alguns segundos para que o Jupyter Notebook termine de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.

Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão, e resolveremos o problema prontamente para você.

Criar o Histograma, Legenda e Título

Primeiramente, criaremos o histograma, a legenda e o título usando Matplotlib. Também atribuiremos IDs a cada objeto usando o método set_gid(). Isso ajudará a relacionar os objetos Matplotlib criados em Python e as construções SVG correspondentes que são analisadas na segunda etapa.

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}')

Adicionar Interatividade ao Histograma

Em seguida, adicionaremos interatividade ao histograma modificando o código SVG usando ECMAScript (JavaScript). Adicionaremos atributos aos objetos patch e texto usando o método set(). Também criaremos uma variável global container que armazena os IDs dos patches pertencentes a cada histograma. Finalmente, criaremos uma função toggle_hist() que define o atributo de visibilidade de todos os patches de cada histograma e a opacidade do próprio marcador.

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")

Visualizar o Histograma Interativo

Finalmente, podemos visualizar o histograma interativo abrindo o arquivo SVG em um navegador web. Para ocultar ou mostrar as barras, basta clicar nos marcadores da legenda.

Resumo

Neste laboratório, aprendemos como criar um histograma interativo usando Matplotlib. Usamos ECMAScript (JavaScript) para adicionar interatividade ao histograma, modificando o código SVG. O histograma resultante possui barras que podem ser ocultadas ou exibidas clicando nos marcadores da legenda.