Erstelle mit Matplotlib Histogramme mit Schraffierungen

PythonPythonBeginner
Jetzt üben

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

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab lernen wir, wie man gefüllte Histogramme mit Schraffierungen mit Matplotlib erstellt. Ein Histogramm ist eine grafische Darstellung von Daten, bei der Balken verwendet werden, um die Häufigkeit numerischer Daten anzuzeigen. Ein gefülltes Histogramm mit Schraffierungen ist ein Histogramm, bei dem die Balken mit einem Muster aus Linien, Punkten oder anderen Symbolen gefüllt sind.

Tipps für die VM

Nachdem der Start der VM abgeschlossen ist, klicken Sie in der oberen linken Ecke, um zur Registerkarte Notebook zu wechseln und Jupyter Notebook für die Übung zu öffnen.

Manchmal müssen Sie einige Sekunden warten, bis Jupyter Notebook vollständig geladen ist. Die Validierung von Vorgängen kann aufgrund der Einschränkungen von Jupyter Notebook nicht automatisiert werden.

Wenn Sie bei der Lernphase Probleme haben, können Sie Labby gerne fragen. Geben Sie nach der Sitzung Feedback, und wir werden das Problem für Sie prompt beheben.

Importiere erforderliche Bibliotheken

Wir werden die erforderlichen Bibliotheken für dieses Lab importieren. Wir benötigen die folgenden Bibliotheken:

  • numpy zum Generieren von Zufallsdaten
  • matplotlib.pyplot zum Erstellen von Diagrammen
  • matplotlib.ticker zum Festlegen der Achsenmarkierungsstellen
  • cycler zum Erstellen von Stilzyklen
  • functools.partial zum Erstellen einer partielle Funktion
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cycler import cycler
from functools import partial

Definiere die Histogrammfunktion

Wir werden eine Funktion definieren, um ein Histogramm als gestuftes Patch zu zeichnen. Die Funktion nimmt die folgenden Parameter:

  • ax: Die Achsen, auf denen geplottet werden soll
  • edges: Ein Array der Länge n+1, das die linken Kanten jedes Bins und die rechte Kante des letzten Bins angibt
  • values: Ein Array der Länge n mit den Häufigkeiten oder Werten der Bins
  • bottoms: Ein Float oder Array, optional, ein Array der Länge n mit der unteren Grenze der Balken. Wenn None, wird Null verwendet.
  • orientation: Ein String, optional, die Ausrichtung des Histogramms. 'v' (Standard) hat die Balken in die positive y-Richtung zunehmend.
def filled_hist(ax, edges, values, bottoms=None, orientation='v', **kwargs):
    """
    Zeichnet ein Histogramm als gestuftes Patch.

    Parameter
    ----------
    ax : Achsen
        Die Achsen, auf denen geplottet werden soll

    Kanten : Array
        Ein Array der Länge n+1, das die linken Kanten jedes Bins und die
        rechte Kante des letzten Bins angibt.

    Werte : Array
        Ein Array der Länge n mit den Häufigkeiten oder Werten der Bins

    Unterkanten : float oder Array, optional
        Ein Array der Länge n mit der unteren Grenze der Balken. Wenn None, wird Null verwendet.

    Orientierung : {'v', 'h'}
        Ausrichtung des Histogramms. 'v' (Standard) hat
        die Balken in die positive y-Richtung zunehmend.

    **kwargs
        Zusätzliche Schlüsselwortargumente werden an `.fill_between` weitergeleitet.

    Rückgabe
    -------
    ret : PolyCollection
        Künstler, der zur Achse hinzugefügt wird
    """
    if orientation not in 'hv':
        raise ValueError(f"Orientierung muss in {{'h', 'v'}} sein, nicht {orientation}")

    kwargs.setdefault('step', 'post')
    kwargs.setdefault('alpha', 0.7)
    Kanten = np.asarray(Kanten)
    Werte = np.asarray(Werte)
    if len(Kanten) - 1!= len(Werte):
        raise ValueError(f'Muss eine Bin-Kante mehr als Wert angeben, nicht: {len(Kanten)=} {len(Werte)=}')

    if Unterkanten ist None:
        Unterkanten = 0
    Unterkanten = np.broadcast_to(Unterkanten, Werte.shape)

    Werte = np.append(Werte, Werte[-1])
    Unterkanten = np.append(Unterkanten, Unterkanten[-1])
    if Orientierung == 'h':
        return ax.fill_betweenx(Kanten, Werte, Unterkanten, **kwargs)
    elif Orientierung == 'v':
        return ax.fill_between(Kanten, Werte, Unterkanten, **kwargs)
    else:
        raise AssertionError("Du solltest nie hier sein")

Definiere die gestapelte Histogrammfunktion

Wir werden eine Funktion definieren, um ein gestapeltes Histogramm zu erstellen. Die Funktion nimmt die folgenden Parameter:

  • ax: Die Achsen, denen Künstler hinzugefügt werden sollen
  • stacked_data: Ein Array der Form (M, N). Die erste Dimension wird iteriert, um die Histogramme zeilenweise zu berechnen
  • sty_cycle: Ein Cycler oder ein dict-fähiges Objekt, der Stil, der auf jede Menge angewendet werden soll
  • bottoms: Ein Array, Standardwert: 0, die Anfangspositionen der Unterkanten
  • hist_func: Ein aufrufbares Objekt, optional. Muss die Signatur bin_vals, bin_edges = f(data) haben. bin_edges sollte um eins länger als bin_vals sein
  • labels: Eine Liste von Strings, optional, das Label für jede Menge. Wenn nicht angegeben und stacked_data ein Array ist, standardmäßig 'default set {n}'. Wenn stacked_data ein Mapping ist und labels None ist, standardmäßig die Schlüssel. Wenn stacked_data ein Mapping ist und labels angegeben ist, werden nur die aufgelisteten Spalten geplottet
  • plot_func: Ein aufrufbares Objekt, optional, Funktion, die aufgerufen wird, um das Histogramm zu zeichnen. Muss die Signatur ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **kwargs) haben
  • plot_kwargs: Ein Dictionary, optional, alle zusätzlichen Schlüsselwortargumente, die an die Plotfunktion weitergeleitet werden sollen. Dies wird für alle Aufrufe der Plotfunktion gleich sein und wird die Werte in sty_cycle überschreiben
def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, hist_func=None, labels=None, plot_func=None, plot_kwargs=None):
    """
    Parameter
    ----------
    ax : axes.Axes
        Die Achsen, denen Künstler hinzugefügt werden sollen

    stacked_data : array oder Mapping
        Ein Array der Form (M, N).  Die erste Dimension wird iteriert, um
        die Histogramme zeilenweise zu berechnen

    sty_cycle : Cycler oder operable of dict
        Stil, der auf jede Menge angewendet werden soll

    bottoms : array, default: 0
        Die Anfangspositionen der Unterkanten.

    hist_func : callable, optional
        Muss die Signatur `bin_vals, bin_edges = f(data)` haben.
        `bin_edges` sollte um eins länger als `bin_vals` sein

    labels : liste von str, optional
        Das Label für jede Menge.

        Wenn nicht angegeben und `stacked_data` ein Array ist, standardmäßig 'default set {n}'

        Wenn *stacked_data* ein Mapping ist, und *labels* ist None, standardmäßig die
        Schlüssel.

        Wenn *stacked_data* ein Mapping ist und *labels* angegeben ist, werden nur die
        aufgelisteten Spalten geplottet.

    plot_func : callable, optional
        Funktion, die aufgerufen wird, um das Histogramm zu zeichnen, muss die Signatur haben:

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

    plot_kwargs : dict, optional
        Alle zusätzlichen Schlüsselwortargumente, die an die Plotfunktion weitergeleitet werden sollen.
        Dies wird für alle Aufrufe der Plotfunktion gleich sein und wird
        die Werte in *sty_cycle* überschreiben.

    Rückgabe
    -------
    arts : dict
        Dictionary von Künstlern, die nach ihren Labels indiziert sind
    """
    ## Gehe mit der Standard-Binning-Funktion um
    if hist_func is None:
        hist_func = np.histogram

    ## Gehe mit der Standard-Plotfunktion um
    if plot_func is None:
        plot_func = filled_hist

    ## Gehe mit dem Standard um
    if plot_kwargs is None:
        plot_kwargs = {}

    versuche:
        l_keys = stacked_data.keys()
        label_data = True
        if labels is None:
            labels = l_keys

    außer AttributeError:
        label_data = False
        if labels is None:
            labels = itertools.repeat(None)

    if label_data:
        loop_iter = enumerate((stacked_data[lab], lab, s) für lab, s in zip(labels, sty_cycle))
    sonst:
        loop_iter = enumerate(zip(stacked_data, labels, sty_cycle))

    arts = {}
    für j, (data, label, sty) in loop_iter:
        wenn label ist None:
            label = f'dflt set {j}'
        label = sty.pop('label', label)
        vals, edges = hist_func(data)
        wenn bottoms ist 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

Einrichten der Histogrammfunktion mit fester Binung

Wir werden eine Histogrammfunktion mit fester Binung mithilfe von numpy.histogram einrichten. Wir werden 20 Bins erstellen, die von -3 bis 3 reichen.

edges = np.linspace(-3, 3, 20, endpoint=True)
hist_func = partial(np.histogram, bins=edges)

Einrichten von Stilzyklen

Wir werden Stilzyklen für die Histogramme mithilfe von cycler einrichten. Wir werden drei Stilzyklen erstellen: einen für die Flächeneigenschaft, einen für das Label und einen für das Schraffierungsmuster.

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

Generiere zufällige Daten

Wir werden zufällige Daten mithilfe von numpy.random.randn generieren. Wir werden 4 Datensätze mit jeweils 12250 Punkten generieren.

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

Erstelle das mit Schraffierungen gefüllte Histogramm

Wir werden ein mit Schraffierungen gefülltes Histogramm erstellen, indem wir die zuvor definierte Funktion stack_hist verwenden. Wir werden die zuvor definierten Variablen stack_data, color_cycle und hist_func nutzen. Wir werden auch plot_kwargs so einstellen, dass die Kantenfarbe und die Orientierung eingeschlossen sind.

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

Erstelle das mit Bezeichnung und Schraffierungen gefüllte Histogramm

Wir werden ein mit Bezeichnung und Schraffierungen gefülltes Histogramm erstellen, indem wir die zuvor definierte Funktion stack_hist verwenden. Wir werden die zuvor definierten Variablen dict_data, color_cycle und hist_func nutzen. Wir werden auch labels auf ['set 0','set 3'] setzen, um nur den ersten und den letzten Datensatz zu plotten.

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

Zusammenfassung

In diesem Lab haben wir gelernt, wie man mit Matplotlib mit Schraffierungen gefüllte Histogramme erstellt. Wir haben zwei Funktionen definiert: filled_hist, um ein Histogramm als gestuftes Patch zu zeichnen, und stack_hist, um ein gestapeltes Histogramm zu erstellen. Wir haben auch eine Histogrammfunktion mit fester Anzahl von Bins mithilfe von numpy.histogram eingerichtet und drei Stilzyklen für die Histogramme mit cycler definiert. Schließlich haben wir zufällige Daten generiert und zwei mit Schraffierungen gefüllte Histogramme mit der stack_hist-Funktion erstellt.