Python matplotlib mehrere Balken

93

So zeichnen Sie mehrere Balken in matplotlib: Wenn ich mehrmals versuchte, die Balkenfunktion aufzurufen, überlappen sie sich, und wie in der folgenden Abbildung dargestellt, ist nur der höchste rote Wert zu sehen. Wie kann ich mehrere Balken mit Datumsangaben auf den x-Achsen zeichnen?

Bisher habe ich Folgendes versucht:

import matplotlib.pyplot as plt
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x, y, width=0.5, color='b', align='center')
ax.bar(x, z, width=0.5, color='g', align='center')
ax.bar(x, k, width=0.5, color='r', align='center')
ax.xaxis_date()

plt.show()

Ich schaff das:

Geben Sie hier die Bildbeschreibung ein

Die Ergebnisse sollten ungefähr so ​​aussehen, aber die Daten befinden sich auf den x-Achsen und die Balken liegen nebeneinander:

Geben Sie hier die Bildbeschreibung ein

John Smith
quelle
Sie müssen die x-Werte ändern
jterrace
2
Was meinst du ? X-Werte sind Daten ...
John Smith
4
warum wird das nicht einfach von matplotlib unterstützt?!
Ihadanny

Antworten:

115
import matplotlib.pyplot as plt
from matplotlib.dates import date2num
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
x = date2num(x)

y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x-0.2, y, width=0.2, color='b', align='center')
ax.bar(x, z, width=0.2, color='g', align='center')
ax.bar(x+0.2, k, width=0.2, color='r', align='center')
ax.xaxis_date()

plt.show()

Geben Sie hier die Bildbeschreibung ein

Ich weiß nicht, was die "y-Werte überlappen sich auch" bedeutet. Löst der folgende Code Ihr Problem?

ax = plt.subplot(111)
w = 0.3
ax.bar(x-w, y, width=w, color='b', align='center')
ax.bar(x, z, width=w, color='g', align='center')
ax.bar(x+w, k, width=w, color='r', align='center')
ax.xaxis_date()
ax.autoscale(tight=True)

plt.show()

Geben Sie hier die Bildbeschreibung ein

HYRY
quelle
Danke, aber wenn ich 3 Bars habe, sieht es gut aus. Wenn ich es mit 40 Takten versuche, ist es durcheinander. Können Sie bitte Ihre Lösung aktualisieren, um sie skalierbarer zu machen?
John Smith
Definieren Sie "vermasselt"? Die Überlappung der X-Beschriftungen kann mithilfe von behoben werden autofmt_xdate(), wodurch die Beschriftungen automatisch gedreht werden.
John Lyon
Das Problem ist, dass sich X-Beschriftungen nicht überlappen. Die y-Werte überlappen sich ebenfalls. Wie man es repariert ?
John Smith
und auch die Breite = 0,2 ist für eine große Zeitspanne zu klein. Wenn ich größere Werte verwende, erhalte ich nicht das gleiche Ergebnis.
John Smith
Eine andere Sache ist, dass die Leerzeichen am Anfang und am Ende. So entfernen Sie Leerzeichen und beginnen direkt mit dem ersten Datum und beenden das letzte Datum auf ähnliche Weise ohne Leerzeichen oder weniger Leerzeichen.
John Smith
61

Das Problem bei der Verwendung von Datumsangaben als x-Werte besteht darin, dass sie falsch sind, wenn Sie ein Balkendiagramm wie in Ihrem zweiten Bild wünschen. Sie sollten entweder ein gestapeltes Balkendiagramm (Farben übereinander) oder eine Gruppierung nach Datum (ein "falsches" Datum auf der x-Achse, bei dem im Grunde nur die Datenpunkte gruppiert werden) verwenden.

import numpy as np
import matplotlib.pyplot as plt

N = 3
ind = np.arange(N)  # the x locations for the groups
width = 0.27       # the width of the bars

fig = plt.figure()
ax = fig.add_subplot(111)

yvals = [4, 9, 2]
rects1 = ax.bar(ind, yvals, width, color='r')
zvals = [1,2,3]
rects2 = ax.bar(ind+width, zvals, width, color='g')
kvals = [11,12,13]
rects3 = ax.bar(ind+width*2, kvals, width, color='b')

ax.set_ylabel('Scores')
ax.set_xticks(ind+width)
ax.set_xticklabels( ('2011-Jan-4', '2011-Jan-5', '2011-Jan-6') )
ax.legend( (rects1[0], rects2[0], rects3[0]), ('y', 'z', 'k') )

def autolabel(rects):
    for rect in rects:
        h = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*h, '%d'%int(h),
                ha='center', va='bottom')

autolabel(rects1)
autolabel(rects2)
autolabel(rects3)

plt.show()

Geben Sie hier die Bildbeschreibung ein

John Lyon
quelle
Wenn ich wie 100 Tage auf den x-Achsen zeigen möchte, wie passen Sie sie an?
John Smith
1
Mit numpy's können Sie leicht die erforderlichen Daten generieren datetime64: zB Ein Monat wert : np.arange('2012-02', '2012-03', dtype='datetime64[D]'). Wenn Sie über 40 Datensätze (gemäß einem anderen Kommentar) verfügen, die sich über 100 Tage erstrecken, müssen Sie möglicherweise genauer überlegen, wie diese Daten am besten dargestellt werden können.
John Lyon
Die Verwendung von ax.xaxis_date () ist sehr vorteilhaft, da Ihre Daten in die x-Achsen passen.
John Smith
3
Warum versuchst du es nicht zuerst? Ich versuche Ihnen beim Lernen zu helfen, nicht Ihren Code für Sie zu schreiben. Ich bin mir sicher, dass Sie dies tun können, xaxis_dateaber Sie müssen das, was ich geschrieben habe, anpassen, um Ihre Datumswerte (z. B. um einige Stunden timedelta) für jede Serie zu versetzen, damit sie sich nicht überschneiden. Die andere Antwort macht genau das, aber Sie müssen möglicherweise danach mit den Etiketten herumspielen.
John Lyon
ok, aber wenn ich np.arange ('2012-02', '2012-03, dtype =' datetime64 [D] ') ausführe, erhalte ich Folgendes: nicht unterstützte Operandentypen für -:' str 'und' str '
John Smith
25

Ich weiß, dass es darum geht matplotlib, aber mit pandasund seabornkann Ihnen viel Zeit sparen:

df = pd.DataFrame(zip(x*3, ["y"]*3+["z"]*3+["k"]*3, y+z+k), columns=["time", "kind", "data"])
plt.figure(figsize=(10, 6))
sns.barplot(x="time", hue="kind", y="data", data=df)
plt.show()

Geben Sie hier die Bildbeschreibung ein

liwt31
quelle
Tolle Antwort, aber aufgrund der x-Achse etwas unvollständig. Können Sie es präsentabler machen?
Spinor8
Sie könnten, nehme ich an, seine auch mit Pandas und Matplotlib machen
Vicki B
19

Nachdem ich nach einer ähnlichen Lösung gesucht und nichts gefunden hatte, das flexibel genug war, beschloss ich, meine eigene Funktion dafür zu schreiben. Sie können so viele Balken pro Gruppe haben, wie Sie möchten, und sowohl die Breite einer Gruppe als auch die einzelnen Breiten der Balken innerhalb der Gruppen angeben.

Genießen:

from matplotlib import pyplot as plt


def bar_plot(ax, data, colors=None, total_width=0.8, single_width=1, legend=True):
    """Draws a bar plot with multiple bars per data point.

    Parameters
    ----------
    ax : matplotlib.pyplot.axis
        The axis we want to draw our plot on.

    data: dictionary
        A dictionary containing the data we want to plot. Keys are the names of the
        data, the items is a list of the values.

        Example:
        data = {
            "x":[1,2,3],
            "y":[1,2,3],
            "z":[1,2,3],
        }

    colors : array-like, optional
        A list of colors which are used for the bars. If None, the colors
        will be the standard matplotlib color cyle. (default: None)

    total_width : float, optional, default: 0.8
        The width of a bar group. 0.8 means that 80% of the x-axis is covered
        by bars and 20% will be spaces between the bars.

    single_width: float, optional, default: 1
        The relative width of a single bar within a group. 1 means the bars
        will touch eachother within a group, values less than 1 will make
        these bars thinner.

    legend: bool, optional, default: True
        If this is set to true, a legend will be added to the axis.
    """

    # Check if colors where provided, otherwhise use the default color cycle
    if colors is None:
        colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Number of bars per group
    n_bars = len(data)

    # The width of a single bar
    bar_width = total_width / n_bars

    # List containing handles for the drawn bars, used for the legend
    bars = []

    # Iterate over all data
    for i, (name, values) in enumerate(data.items()):
        # The offset in x direction of that bar
        x_offset = (i - n_bars / 2) * bar_width + bar_width / 2

        # Draw a bar for every value of that type
        for x, y in enumerate(values):
            bar = ax.bar(x + x_offset, y, width=bar_width * single_width, color=colors[i % len(colors)])

        # Add a handle to the last drawn bar, which we'll need for the legend
        bars.append(bar[0])

    # Draw legend if we need
    if legend:
        ax.legend(bars, data.keys())


if __name__ == "__main__":
    # Usage example:
    data = {
        "a": [1, 2, 3, 2, 1],
        "b": [2, 3, 4, 3, 1],
        "c": [3, 2, 1, 4, 2],
        "d": [5, 9, 2, 1, 8],
        "e": [1, 3, 2, 2, 3],
        "f": [4, 3, 1, 1, 4],
    }

    fig, ax = plt.subplots()
    bar_plot(ax, data, total_width=.8, single_width=.9)
    plt.show()

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

pascscha
quelle
Wie können wir dies ändern, um der x-Achse Beschriftungen hinzuzufügen? Wie in jeder Gruppe von Bars?
x89
Ändern Sie die xticksHandlung, zBplt.xticks(range(5), ["one", "two", "three", "four", "five"])
Pascscha
nette Funktion, sehr hilfreich, danke. Das einzige, was ich geändert habe, ist, dass ich denke, dass die Legende einfacher ist, wenn Sie nur label = data.keys [i] in den Barplot-Aufruf einfügen und dann die Balkenliste nicht erstellen müssen.
Adrian Tompkins
0

Ich habe diese Lösung gewählt: Wenn Sie mehr als ein Diagramm in einer Figur zeichnen möchten, stellen Sie sicher, dass Sie vor dem Zeichnen der nächsten Diagramme, die Sie richtig eingestellt haben, matplotlib.pyplot.hold(True) weitere Diagramme hinzufügen können.

In Bezug auf die Datums- / Uhrzeitwerte auf der X-Achse funktioniert für mich eine Lösung mit der Ausrichtung von Balken. Wenn Sie ein anderes Balkendiagramm mit erstellen matplotlib.pyplot.bar(), verwenden Sie es einfach align='edge|center'und legen Sie es fest width='+|-distance'.

Wenn Sie alle Balken (Diagramme) richtig eingestellt haben, werden die Balken gut angezeigt.

Dave
quelle
Es sieht so aus, matplotlib.pyplot.holdals wäre es seit Version 2.0 veraltet, wie in den Dokumenten erwähnt
engineervix