Introdução
Neste laboratório, aprenderemos como criar histogramas preenchidos com hachuras usando Matplotlib. Um histograma é uma representação gráfica de dados que utiliza barras para mostrar a frequência de dados numéricos. Um histograma preenchido com hachuras é um histograma no qual as barras são preenchidas com um padrão de linhas, pontos ou outros símbolos.
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ê.
Importar as bibliotecas necessárias
Importaremos as bibliotecas necessárias para este laboratório. Precisamos das seguintes bibliotecas:
numpypara gerar dados aleatóriosmatplotlib.pyplotpara criar gráficosmatplotlib.tickerpara definir as localizações dos ticks dos eixoscyclerpara criar ciclos de estilofunctools.partialpara criar uma função parcial
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cycler import cycler
from functools import partial
Definir a função do histograma
Definiremos uma função para desenhar um histograma como um patch em degraus. A função receberá os seguintes parâmetros:
ax: os Axes (eixos) para plotaredges: um array de comprimento n+1 que fornece as bordas esquerdas de cada bin e a borda direita do último binvalues: um array de comprimento n de contagens ou valores de binbottoms: um float ou array, opcional, um array de comprimento n da base das barras. Se None, zero é usado.orientation: uma string, opcional, a orientação do histograma. 'v' (padrão) tem as barras aumentando na direção y positiva.
def filled_hist(ax, edges, values, bottoms=None, orientation='v', **kwargs):
"""
Desenha um histograma como um patch em degraus.
Parâmetros
----------
ax : Axes
Os eixos para plotar
edges : array
Um array de comprimento n+1 que fornece as bordas esquerdas de cada bin e a
borda direita do último bin.
values : array
Um array de comprimento n de contagens ou valores de bin
bottoms : float ou array, opcional
Um array de comprimento n da base das barras. Se None, zero é usado.
orientation : {'v', 'h'}
Orientação do histograma. 'v' (padrão) tem
as barras aumentando na direção y positiva.
**kwargs
Argumentos de palavra-chave extras são passados para `.fill_between`.
Retorna
-------
ret : PolyCollection
Artista adicionado aos Axes
"""
if orientation not in 'hv':
raise ValueError(f"orientation must be in {{'h', 'v'}} not {orientation}")
kwargs.setdefault('step', 'post')
kwargs.setdefault('alpha', 0.7)
edges = np.asarray(edges)
values = np.asarray(values)
if len(edges) - 1 != len(values):
raise ValueError(f'Must provide one more bin edge than value not: {len(edges)=} {len(values)=}')
if bottoms is None:
bottoms = 0
bottoms = np.broadcast_to(bottoms, values.shape)
values = np.append(values, values[-1])
bottoms = np.append(bottoms, bottoms[-1])
if orientation == 'h':
return ax.fill_betweenx(edges, values, bottoms, **kwargs)
elif orientation == 'v':
return ax.fill_between(edges, values, bottoms, **kwargs)
else:
raise AssertionError("you should never be here")
Definir a função do histograma empilhado
Definiremos uma função para criar um histograma empilhado. A função receberá os seguintes parâmetros:
ax: os eixos para adicionar artistasstacked_data: um array com formato (M, N). A primeira dimensão será iterada para calcular histogramas linha a linhasty_cycle: um Cycler (ciclo) ou operável de dict, estilo a ser aplicado a cada conjuntobottoms: um array, padrão: 0, as posições iniciais das bases.hist_func: um callable (chamável), opcional. Deve ter a assinaturabin_vals, bin_edges = f(data). Espera-se quebin_edgesseja um elemento mais longo quebin_valslabels: uma lista de strings, opcional, o rótulo para cada conjunto. Se não for fornecido e os dados empilhados forem um array, o padrão será 'conjunto padrão {n}'. Se stacked_data for um mapeamento e labels for None, o padrão será as chaves. Se stacked_data for um mapeamento e labels for fornecido, apenas as colunas listadas serão plotadas.plot_func: um callable, opcional, função a ser chamada para desenhar o histograma. Deve ter a assinaturaret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **kwargs)plot_kwargs: um dicionário, opcional, quaisquer argumentos de palavra-chave extras para passar para a função de plotagem. Isso será o mesmo para todas as chamadas para a função de plotagem e substituirá os valores emsty_cycle.
def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, hist_func=None, labels=None, plot_func=None, plot_kwargs=None):
"""
Parâmetros
----------
ax : axes.Axes
Os eixos para adicionar artistas
stacked_data : array or Mapping
Um array com formato (M, N). A primeira dimensão será iterada para
calcular histogramas linha a linha
sty_cycle : Cycler ou operável de dict
Estilo a ser aplicado a cada conjunto
bottoms : array, padrão: 0
As posições iniciais das bases.
hist_func : callable, opcional
Deve ter a assinatura `bin_vals, bin_edges = f(data)`.
Espera-se que `bin_edges` seja um elemento mais longo que `bin_vals`
labels : list of str, opcional
O rótulo para cada conjunto.
Se não for fornecido e os dados empilhados forem um array, o padrão será
'conjunto padrão {n}'
Se *stacked_data* for um mapeamento, e *labels* for None, o padrão será as
chaves.
Se *stacked_data* for um mapeamento e *labels* for fornecido, apenas as
colunas listadas serão plotadas.
plot_func : callable, opcional
Função a ser chamada para desenhar o histograma deve ter a assinatura:
ret = plot_func(ax, edges, top, bottoms=bottoms,
label=label, **kwargs)
plot_kwargs : dict, opcional
Quaisquer argumentos de palavra-chave extras para passar para a função de
plotagem. Isso será o mesmo para todas as chamadas para a função de
plotagem e substituirá os valores em *sty_cycle*.
Retorna
-------
arts : dict
Dicionário de artistas indexados em seus rótulos
"""
## lidar com a função de binning padrão
if hist_func is None:
hist_func = np.histogram
## lidar com a função de plotagem padrão
if plot_func is None:
plot_func = filled_hist
## lidar com o padrão
if plot_kwargs is None:
plot_kwargs = {}
try:
l_keys = stacked_data.keys()
label_data = True
if labels is None:
labels = l_keys
except AttributeError:
label_data = False
if labels is None:
labels = itertools.repeat(None)
if label_data:
loop_iter = enumerate((stacked_data[lab], lab, s) for lab, s in zip(labels, sty_cycle))
else:
loop_iter = enumerate(zip(stacked_data, labels, sty_cycle))
arts = {}
for j, (data, label, sty) in loop_iter:
if label is None:
label = f'dflt set {j}'
label = sty.pop('label', label)
vals, edges = hist_func(data)
if bottoms is None:
bottoms = np.zeros_like(vals)
top = bottoms + vals
sty.update(plot_kwargs)
ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **sty)
bottoms = top
arts[label] = ret
ax.legend(fontsize=10)
return arts
Configurar a função do histograma para bins fixos
Configuraremos uma função de histograma com bins fixos usando numpy.histogram. Criaremos 20 bins variando de -3 a 3.
edges = np.linspace(-3, 3, 20, endpoint=True)
hist_func = partial(np.histogram, bins=edges)
Configurar ciclos de estilo
Configuraremos ciclos de estilo para os histogramas usando cycler. Criaremos três ciclos de estilo: um para a cor de preenchimento (facecolor), um para o rótulo (label) e um para o padrão de hachura (hatch).
color_cycle = cycler(facecolor=plt.rcParams['axes.prop_cycle'][:4])
label_cycle = cycler(label=[f'set {n}' for n in range(4)])
hatch_cycle = cycler(hatch=['/', '*', '+', '|'])
Gerar dados aleatórios
Geraremos dados aleatórios usando numpy.random.randn. Geraremos 4 conjuntos de dados com 12250 pontos cada.
np.random.seed(19680801)
stack_data = np.random.randn(4, 12250)
Criar o histograma preenchido com hachuras
Criaremos um histograma preenchido com hachuras usando a função stack_hist que definimos anteriormente. Usaremos os stack_data, color_cycle e hist_func que definimos anteriormente. Também definiremos plot_kwargs para incluir edgecolor e orientation.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True)
arts = stack_hist(ax1, stack_data, color_cycle + label_cycle + hatch_cycle, hist_func=hist_func)
arts = stack_hist(ax2, stack_data, color_cycle, hist_func=hist_func, plot_kwargs=dict(edgecolor='w', orientation='h'))
ax1.set_ylabel('contagens')
ax1.set_xlabel('x')
ax2.set_xlabel('contagens')
ax2.set_ylabel('x')
Criar o histograma preenchido com hachuras e rotulado
Criaremos um histograma preenchido com hachuras e rotulado usando a função stack_hist que definimos anteriormente. Usaremos os dict_data, color_cycle e hist_func que definimos anteriormente. Também definiremos labels para ['set 0', 'set 3'] para plotar apenas o primeiro e o último conjunto.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True, sharey=True)
dict_data = dict(zip((c['label'] for c in label_cycle), stack_data))
arts = stack_hist(ax1, dict_data, color_cycle + hatch_cycle, hist_func=hist_func)
arts = stack_hist(ax2, dict_data, color_cycle + hatch_cycle, hist_func=hist_func, labels=['set 0', 'set 3'])
ax1.xaxis.set_major_locator(mticker.MaxNLocator(5))
ax1.set_xlabel('contagens')
ax1.set_ylabel('x')
ax2.set_ylabel('x')
Resumo
Neste laboratório, aprendemos como criar histogramas preenchidos com hachuras usando Matplotlib. Definimos duas funções: filled_hist para desenhar um histograma como um patch (remendo) em degraus, e stack_hist para criar um histograma empilhado. Também configuramos uma função de histograma com bins (intervalos) fixos usando numpy.histogram, e definimos três ciclos de estilo para os histogramas usando cycler. Finalmente, geramos dados aleatórios e criamos dois histogramas preenchidos com hachuras usando a função stack_hist.