Crear histogramas con sombreado con Matplotlib

PythonPythonBeginner
Practicar Ahora

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

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, aprenderemos cómo crear histogramas con relleno de sombreado utilizando Matplotlib. Un histograma es una representación gráfica de datos que utiliza barras para mostrar la frecuencia de datos numéricos. Un histograma con relleno de sombreado es un histograma en el que las barras están llenas con un patrón de líneas, puntos u otros símbolos.

Consejos sobre la VM

Una vez finalizada la inicialización de la VM, haga clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.

A veces, es posible que tenga que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no se puede automatizar debido a las limitaciones de Jupyter Notebook.

Si tiene problemas durante el aprendizaje, no dude en preguntar a Labby. Deje sus comentarios después de la sesión y lo resolveremos rápidamente para usted.

Importar las bibliotecas necesarias

Importaremos las bibliotecas necesarias para este laboratorio. Necesitamos las siguientes bibliotecas:

  • numpy para generar datos aleatorios
  • matplotlib.pyplot para crear gráficos
  • matplotlib.ticker para establecer las ubicaciones de las marcas de los ejes
  • cycler para crear ciclos de estilo
  • functools.partial para crear una función parcial
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cycler import cycler
from functools import partial

Definir la función de histograma

Definiremos una función para dibujar un histograma como un parche escalonado. La función tomará los siguientes parámetros:

  • ax: los Ejes en los que se dibujará
  • edges: una matriz de longitud n+1 que da los bordes izquierdos de cada intervalo y el borde derecho del último intervalo
  • values: una matriz de longitud n de conteos o valores de intervalos
  • bottoms: un flotante o matriz, opcional, una matriz de longitud n de la base de las barras. Si es None, se utiliza cero.
  • orientation: una cadena, opcional, la orientación del histograma. 'v' (por defecto) hace que las barras crezcan en la dirección y positiva.
def filled_hist(ax, edges, values, bottoms=None, orientation='v', **kwargs):
    """
    Dibuja un histograma como un parche escalonado.

    Parámetros
    ----------
    ax : Ejes
        Los ejes en los que se dibujará

    edges : matriz
        Una matriz de longitud n+1 que da los bordes izquierdos de cada intervalo y el
        borde derecho del último intervalo.

    values : matriz
        Una matriz de longitud n de conteos o valores de intervalos

    bottoms : flotante o matriz, opcional
        Una matriz de longitud n de la base de las barras.  Si es None, se utiliza cero.

    orientation : {'v', 'h'}
       Orientación del histograma.  'v' (por defecto) hace que
       las barras crezcan en la dirección y positiva.

    **kwargs
        Argumentos de palabras clave adicionales se transmiten a `.fill_between`.

    Devuelve
    -------
    ret : PolyCollection
        Artista agregado a los Ejes
    """
    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 la función de histograma apilado

Definiremos una función para crear un histograma apilado. La función tomará los siguientes parámetros:

  • ax: los ejes a los que se agregarán los artistas
  • stacked_data: una matriz con forma (M, N). La primera dimensión se iterará para calcular los histogramas fila por fila
  • sty_cycle: un Cycler o objeto operable de diccionarios, el estilo que se aplicará a cada conjunto
  • bottoms: una matriz, valor predeterminado: 0, las posiciones iniciales de las bases
  • hist_func: una función llamada, opcional. Debe tener la firma bin_vals, bin_edges = f(data). Se espera que bin_edges sea una unidad más larga que bin_vals
  • labels: una lista de cadenas, opcional, la etiqueta para cada conjunto. Si no se da y los datos apilados son una matriz, el valor predeterminado es 'conjunto predeterminado {n}'. Si stacked_data es un mapeo y labels es None, el valor predeterminado es las claves. Si stacked_data es un mapeo y se dan labels, entonces solo se graficarán las columnas listadas
  • plot_func: una función llamada, opcional, función que se llamará para dibujar el histograma. Debe tener la firma ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **kwargs)
  • plot_kwargs: un diccionario, opcional, cualquier argumento de palabras clave adicional que se transmitirá a la función de trazado. Esto será el mismo para todas las llamadas a la función de trazado y sobrescribirá los valores en sty_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
        Los ejes a los que se agregarán los artistas

    stacked_data : matriz o Mapeo
        Una matriz con forma (M, N).  La primera dimensión se iterará para
        calcular los histogramas fila por fila

    sty_cycle : Cycler o objeto operable de diccionarios
        Estilo que se aplicará a cada conjunto

    bottoms : matriz, valor predeterminado: 0
        Las posiciones iniciales de las bases.

    hist_func : callable, opcional
        Debe tener la firma `bin_vals, bin_edges = f(data)`.
        Se espera que `bin_edges` sea una unidad más larga que `bin_vals`

    labels : lista de str, opcional
        La etiqueta para cada conjunto.

        Si no se da y los datos apilados son una matriz, el valor predeterminado es 'conjunto predeterminado {n}'

        Si *stacked_data* es un mapeo, y *labels* es None, el valor predeterminado es las
        claves.

        Si *stacked_data* es un mapeo y *labels* se da, entonces solo se graficarán las
        columnas listadas.

    plot_func : callable, opcional
        Función que se llamará para dibujar el histograma debe tener la firma:

          ret = plot_func(ax, edges, top, bottoms=bottoms,
                          label=label, **kwargs)

    plot_kwargs : dict, opcional
        Cualquier argumento de palabras clave adicional que se transmitirá a la función de trazado.
        Esto será el mismo para todas las llamadas a la función de trazado y sobrescribirá los valores en *sty_cycle*.

    Devuelve
    -------
    arts : dict
        Diccionario de artistas con sus etiquetas como claves
    """
    ## manejar la función de agrupamiento de intervalos predeterminada
    if hist_func is None:
        hist_func = np.histogram

    ## manejar la función de trazado predeterminada
    if plot_func is None:
        plot_func = filled_hist

    ## manejar el predeterminado
    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 la función de histograma con intervalos fijos

Configuraremos una función de histograma con intervalos fijos utilizando numpy.histogram. Crearemos 20 intervalos que van desde -3 hasta 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 los histogramas utilizando cycler. Crearemos tres ciclos de estilo: uno para el color de fondo, uno para la etiqueta y uno para el patrón de sombreado.

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=['/', '*', '+', '|'])

Generar datos aleatorios

Generaremos datos aleatorios utilizando numpy.random.randn. Generaremos 4 conjuntos de datos con 12250 puntos cada uno.

np.random.seed(19680801)
stack_data = np.random.randn(4, 12250)

Crear el histograma con sombreado

Crearemos un histograma con sombreado utilizando la función stack_hist que definimos anteriormente. Utilizaremos los stack_data, color_cycle y hist_func que definimos anteriormente. También estableceremos plot_kwargs para incluir el color del borde y la orientación.

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('conteos')
ax1.set_xlabel('x')
ax2.set_xlabel('conteos')
ax2.set_ylabel('x')

Crear el histograma con sombreado y etiquetas

Crearemos un histograma con sombreado y etiquetas utilizando la función stack_hist que definimos anteriormente. Utilizaremos el dict_data, color_cycle y hist_func que definimos anteriormente. También estableceremos labels en ['set 0','set 3'] para graficar solo el primer y el ú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('conteos')
ax1.set_ylabel('x')
ax2.set_ylabel('x')

Resumen

En este laboratorio, aprendimos cómo crear histogramas con sombreado utilizando Matplotlib. Definimos dos funciones: filled_hist para dibujar un histograma como un parche escalonado y stack_hist para crear un histograma apilado. También configuramos una función de histograma con bins fijos utilizando numpy.histogram y definimos tres ciclos de estilo para los histogramas utilizando cycler. Finalmente, generamos datos aleatorios y creamos dos histogramas con sombreado utilizando la función stack_hist.