Erstelle gepackte Blasen-Diagramme mit Python

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 werden wir lernen, wie man mithilfe von Matplotlib in Python einen gepackten Blasen-Diagramm erstellt. Ein gepacktes Blasen-Diagramm ist ein Diagrammtyp, der Daten in Blasen unterschiedlicher Größen darstellt, wobei die Größe der Blase die Größe der Daten repräsentiert. Dieses Diagramm eignet sich gut zur Darstellung von skalaren Daten, wobei die Daten ein einzelner numerischer Wert sind, der einem Element zugeordnet ist.

VM-Tipps

Nachdem der VM-Start 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 von Einschränkungen in 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.

Importieren der erforderlichen Bibliotheken

Um ein gepacktes Blasen-Diagramm zu erstellen, müssen wir die Bibliotheken matplotlib.pyplot und numpy importieren. Die numpy-Bibliothek wird verwendet, um mathematische Operationen auf Arrays durchzuführen, was für die Berechnung der Blasengrößen nützlich ist.

import matplotlib.pyplot as plt
import numpy as np

Definieren der Daten

Die Daten, die wir für dieses Beispiel verwenden werden, sind die Marktanteile verschiedener Desktop-Browser. Wir werden die Daten als Dictionary definieren, das die Browser-Namen, den Marktanteil und die Farbe für jede Blase enthält.

browser_market_share = {
    'browsers': ['firefox', 'chrome', 'safari', 'edge', 'ie', 'opera'],
    'market_share': [8.61, 69.55, 8.36, 4.12, 2.76, 2.43],
    'color': ['#5A69AF', '#579E65', '#F9C784', '#FC944A', '#F24C00', '#00B825']
}

Definieren der BubbleChart-Klasse

Die BubbleChart-Klasse wird verwendet, um das gepackte Blasen-Diagramm zu erstellen. Die Klasse nimmt ein Array von Blasenflächen und einen Blasenabstandswert entgegen. Die __init__-Methode setzt die Anfangspositionen der Blasen und berechnet die maximale Schrittweite, die die Entfernung ist, die jede Blase in einer einzelnen Iteration bewegen kann.

class BubbleChart:
    def __init__(self, area, bubble_spacing=0):
        """
        Setup for bubble collapse.

        Parameters
        ----------
        area : array-like
            Fläche der Blasen.
        bubble_spacing : float, Standardwert: 0
            Minimaler Abstand zwischen den Blasen nach dem Zusammenfallen.

        Notes
        -----
        Wenn "area" sortiert ist, können die Ergebnisse merkwürdig aussehen.
        """
        area = np.asarray(area)
        r = np.sqrt(area / np.pi)

        self.bubble_spacing = bubble_spacing
        self.bubbles = np.ones((len(area), 4))
        self.bubbles[:, 2] = r
        self.bubbles[:, 3] = area
        self.maxstep = 2 * self.bubbles[:, 2].max() + self.bubble_spacing
        self.step_dist = self.maxstep / 2

        ## berechne die Anfangs-Gitterlayout für die Blasen
        length = np.ceil(np.sqrt(len(self.bubbles)))
        grid = np.arange(length) * self.maxstep
        gx, gy = np.meshgrid(grid, grid)
        self.bubbles[:, 0] = gx.flatten()[:len(self.bubbles)]
        self.bubbles[:, 1] = gy.flatten()[:len(self.bubbles)]

        self.com = self.center_of_mass()

Definieren von Blasenbewegungs-Methoden

Die BubbleChart-Klasse enthält auch Methoden, um die Blasen zum Massenmittelpunkt zu bewegen und auf Kollisionen mit anderen Blasen zu prüfen. Die center_of_mass-Methode berechnet den Massenmittelpunkt aller Blasen, und die center_distance-Methode berechnet die Entfernung zwischen einer Blase und dem Massenmittelpunkt. Die outline_distance-Methode berechnet die Entfernung zwischen der Kontur einer Blase und den Konturen anderer Blasen, und die check_collisions-Methode prüft, ob eine neue Blasenposition mit anderen Blasen kollidiert.

    def center_of_mass(self):
        return np.average(
            self.bubbles[:, :2], axis=0, weights=self.bubbles[:, 3]
        )

    def center_distance(self, bubble, bubbles):
        return np.hypot(bubble[0] - bubbles[:, 0],
                        bubble[1] - bubbles[:, 1])

    def outline_distance(self, bubble, bubbles):
        center_distance = self.center_distance(bubble, bubbles)
        return center_distance - bubble[2] - \
            bubbles[:, 2] - self.bubble_spacing

    def check_collisions(self, bubble, bubbles):
        distance = self.outline_distance(bubble, bubbles)
        return len(distance[distance < 0])

Definieren der Blasen-Kollisions-Methode

Die BubbleChart-Klasse enthält auch eine Methode, um Blasen-Kollisionen zu prüfen und kollidierende Blasen umzugehen. Die collides_with-Methode berechnet die Entfernung zwischen einer neuen Blasenposition und den Positionen anderer Blasen. Wenn die Entfernung kleiner als Null ist, bedeutet dies, dass es eine Kollision gibt, und die Methode gibt den Index der kollidierenden Blase zurück. Die collapse-Methode bewegt die Blasen zum Massenmittelpunkt und um kollidierende Blasen herum, und die plot-Methode zeichnet die Blasen auf dem Diagramm.

    def collides_with(self, bubble, bubbles):
        distance = self.outline_distance(bubble, bubbles)
        idx_min = np.argmin(distance)
        return idx_min if type(idx_min) == np.ndarray else [idx_min]

    def collapse(self, n_iterations=50):
        """
        Bewege Blasen zum Massenmittelpunkt.

        Parameters
        ----------
        n_iterations : int, Standardwert: 50
            Anzahl der Bewegungen, die ausgeführt werden sollen.
        """
        for _i in range(n_iterations):
            moves = 0
            for i in range(len(self.bubbles)):
                rest_bub = np.delete(self.bubbles, i, 0)
                ## versuche, direkt zum Massenmittelpunkt zu bewegen
                ## Richtungsvektor von der Blase zum Massenmittelpunkt
                dir_vec = self.com - self.bubbles[i, :2]

                ## Verkürze den Richtungsvektor, um die Länge 1 zu haben
                dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))

                ## Berechne die neue Blasenposition
                new_point = self.bubbles[i, :2] + dir_vec * self.step_dist
                new_bubble = np.append(new_point, self.bubbles[i, 2:4])

                ## Prüfe, ob die neue Blase mit anderen Blasen kollidiert
                if not self.check_collisions(new_bubble, rest_bub):
                    self.bubbles[i, :] = new_bubble
                    self.com = self.center_of_mass()
                    moves += 1
                else:
                    ## Versuche, um eine Blase zu bewegen, mit der du kollidierst
                    ## finde die kollidierende Blase
                    for colliding in self.collides_with(new_bubble, rest_bub):
                        ## Berechne den Richtungsvektor
                        dir_vec = rest_bub[colliding, :2] - self.bubbles[i, :2]
                        dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
                        ## Berechne den orthogonalen Vektor
                        orth = np.array([dir_vec[1], -dir_vec[0]])
                        ## Teste, in welche Richtung zu gehen
                        new_point1 = (self.bubbles[i, :2] + orth *
                                      self.step_dist)
                        new_point2 = (self.bubbles[i, :2] - orth *
                                      self.step_dist)
                        dist1 = self.center_distance(
                            self.com, np.array([new_point1]))
                        dist2 = self.center_distance(
                            self.com, np.array([new_point2]))
                        new_point = new_point1 if dist1 < dist2 else new_point2
                        new_bubble = np.append(new_point, self.bubbles[i, 2:4])
                        if not self.check_collisions(new_bubble, rest_bub):
                            self.bubbles[i, :] = new_bubble
                            self.com = self.center_of_mass()

            if moves / len(self.bubbles) < 0.1:
                self.step_dist = self.step_dist / 2

    def plot(self, ax, labels, colors):
        """
        Zeichne das Blasen-Diagramm.

        Parameters
        ----------
        ax : matplotlib.axes.Axes
        labels : Liste
            Labels der Blasen.
        colors : Liste
            Farben der Blasen.
        """
        for i in range(len(self.bubbles)):
            circ = plt.Circle(
                self.bubbles[i, :2], self.bubbles[i, 2], color=colors[i])
            ax.add_patch(circ)
            ax.text(*self.bubbles[i, :2], labels[i],
                    horizontalalignment='center', verticalalignment='center')

Erstellen eines BubbleChart-Objekts und Zeichnen des Diagramms

Um das gepackte Blasen-Diagramm zu erstellen, müssen wir ein BubbleChart-Objekt erstellen und die collapse-Methode aufrufen, um die Blasen zum Massenmittelpunkt zu bewegen. Anschließend können wir eine matplotlib-Figur erstellen und einem Axes-Objekt hinzufügen. Schließlich rufen wir die plot-Methode auf, um die Blasen auf dem Diagramm zu zeichnen.

bubble_chart = BubbleChart(area=browser_market_share['market_share'],
                           bubble_spacing=0.1)

bubble_chart.collapse()

fig, ax = plt.subplots(subplot_kw=dict(aspect="equal"))
bubble_chart.plot(
    ax, browser_market_share['browsers'], browser_market_share['color'])
ax.axis("off")
ax.relim()
ax.autoscale_view()
ax.set_title('Browser market share')

plt.show()

Zusammenfassung

In diesem Lab haben wir gelernt, wie man mit Matplotlib in Python ein gepacktes Blasen-Diagramm erstellt. Wir haben die Daten als Dictionary definiert, das die Browser-Namen, die Marktanteile und die Farbe für jede Blase enthält. Anschließend haben wir eine BubbleChart-Klasse definiert, um die Anfangspositionen der Blasen einzurichten, die maximale Schrittweite zu berechnen, die Blasen zum Massenmittelpunkt zu bewegen und auf Kollisionen mit anderen Blasen zu prüfen. Schließlich haben wir das Diagramm mit matplotlib geplottet.