Zeichnen Sie zwei Histogramme mit matplotlib auf ein einzelnes Diagramm

233

Ich habe ein Histogramm mit Daten aus einer Datei erstellt und kein Problem. Jetzt wollte ich Daten aus einer anderen Datei im selben Histogramm überlagern, also mache ich so etwas

n,bins,patchs = ax.hist(mydata1,100)
n,bins,patchs = ax.hist(mydata2,100)

Das Problem ist jedoch, dass für jedes Intervall nur der Balken mit dem höchsten Wert angezeigt wird und der andere ausgeblendet wird. Ich frage mich, wie ich beide Histogramme gleichzeitig mit unterschiedlichen Farben zeichnen könnte.

Mach den Weg frei
quelle

Antworten:

417

Hier haben Sie ein Arbeitsbeispiel:

import random
import numpy
from matplotlib import pyplot

x = [random.gauss(3,1) for _ in range(400)]
y = [random.gauss(4,2) for _ in range(400)]

bins = numpy.linspace(-10, 10, 100)

pyplot.hist(x, bins, alpha=0.5, label='x')
pyplot.hist(y, bins, alpha=0.5, label='y')
pyplot.legend(loc='upper right')
pyplot.show()

Geben Sie hier die Bildbeschreibung ein

Joaquin
quelle
1
Wäre es nicht eine gute Idee, pyplot.hold(True)vor dem Plotten zu setzen , nur für den Fall?
JAB
2
Ich bin mir nicht sicher, ob hold (True) in meinen matplotlib-Konfigurationsparametern festgelegt ist, oder pyplot verhält sich standardmäßig so, aber für mich funktioniert der Code so, wie er ist. Der Code wird aus einer größeren Anwendung extrahiert, die bisher keine Probleme bereitet. Wie auch immer, gute Frage, die ich mir schon beim Schreiben des Codes gestellt habe
Joaquin
@joaquin: Wie könnte ich x als blau und y als rot angeben?
Amc
7
Wenn ich das Diagramm mit der Randfarbe der Balken reproduzierte, ist dies Nonestandardmäßig der Fall . Wenn Sie das gleiche Design wie in der Grafik sehen möchten, können Sie den edgecolorParameter in beiden beispielsweise auf k(schwarz) setzen. Die Vorgehensweise ist für die Legende ähnlich.
Also S
2
Noch einfacher : pyplot.hist([x, y], bins, alpha=0.5, label=['x', 'y']).
Augustin
174

Die akzeptierten Antworten geben den Code für ein Histogramm mit überlappenden Balken an. Wenn Sie jedoch möchten, dass jeder Balken nebeneinander liegt (wie ich), versuchen Sie die folgende Variation:

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-deep')

x = np.random.normal(1, 2, 5000)
y = np.random.normal(-1, 3, 2000)
bins = np.linspace(-10, 10, 30)

plt.hist([x, y], bins, label=['x', 'y'])
plt.legend(loc='upper right')
plt.show()

Geben Sie hier die Bildbeschreibung ein

Referenz: http://matplotlib.org/examples/statistics/histogram_demo_multihist.html

BEARBEITEN [2018/03/16]: Aktualisiert, um das Zeichnen von Arrays unterschiedlicher Größe zu ermöglichen, wie von @stochastic_zeitgeist vorgeschlagen

Gustavo Bezerra
quelle
@GustavoBezerra, wie wird plt.histeine PDF-Datei für jedes Histogramm erstellt? Ich habe meine Daten mit geladen pandas.read_csvund die Datei hat 36 Spalten und 100 Zeilen. Ich möchte also 100 PDF-Dateien.
Sigur
2
@Sigur Das ist ziemlich unangebracht. Bitte Google oder eine neue Frage stellen. Dies scheint verwandt zu sein: stackoverflow.com/questions/11328958/…
Gustavo Bezerra
1
@stochastic_zeitgeist Ich stimme @pasbi zu. Ich habe Ihren Kommentar mit einem Pandas-Datenrahmen verwendet, weil ich aufgrund von Nans unterschiedliche Gewichte benötigte. mit x=np.array(df.a)und y=np.array(df.b.dropna())es endete im Grundeplt.hist([x, y], weights=[np.ones_like(x)/len(x), np.ones_like(y)/len(y)])
grinsbaeckchen
1
Falls sich Ihre Stichprobengrößen drastisch unterscheiden, möchten Sie möglicherweise mit zwei Achsen zeichnen, um die Verteilungen besser vergleichen zu können. Siehe unten .
Andrew
1
@ AgapeGal'lo Bitte beziehen Sie sich auf Andrews Antwort.
Gustavo Bezerra
30

Wenn Sie unterschiedliche Stichprobengrößen haben, kann es schwierig sein, die Verteilungen mit einer einzelnen y-Achse zu vergleichen. Beispielsweise:

import numpy as np
import matplotlib.pyplot as plt

#makes the data
y1 = np.random.normal(-2, 2, 1000)
y2 = np.random.normal(2, 2, 5000)
colors = ['b','g']

#plots the histogram
fig, ax1 = plt.subplots()
ax1.hist([y1,y2],color=colors)
ax1.set_xlim(-10,10)
ax1.set_ylabel("Count")
plt.tight_layout()
plt.show()

hist_single_ax

In diesem Fall können Sie Ihre beiden Datensätze auf verschiedenen Achsen zeichnen. Zu diesem Zweck können Sie Ihre Histogrammdaten mit matplotlib abrufen, die Achse löschen und dann auf zwei separaten Achsen neu zeichnen (Verschieben der Bin-Kanten, damit sie sich nicht überlappen):

#sets up the axis and gets histogram data
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.hist([y1, y2], color=colors)
n, bins, patches = ax1.hist([y1,y2])
ax1.cla() #clear the axis

#plots the histogram data
width = (bins[1] - bins[0]) * 0.4
bins_shifted = bins + width
ax1.bar(bins[:-1], n[0], width, align='edge', color=colors[0])
ax2.bar(bins_shifted[:-1], n[1], width, align='edge', color=colors[1])

#finishes the plot
ax1.set_ylabel("Count", color=colors[0])
ax2.set_ylabel("Count", color=colors[1])
ax1.tick_params('y', colors=colors[0])
ax2.tick_params('y', colors=colors[1])
plt.tight_layout()
plt.show()

hist_twin_ax

Andrew
quelle
1
Dies ist eine schöne kurze Antwort, außer dass Sie auch hinzufügen sollten, wie die Balken auf jedem Tick-Label
zentriert werden
11

Als Ergänzung zu Gustavo Bezerras Antwort :

Wenn Sie möchten, dass jedes Histogramm normalisiert wird ( normedfür mpl <= 2.1 und densityfür mpl> = 3.1 ), können Sie nicht nur verwenden normed/density=True, sondern stattdessen die Gewichte für jeden Wert festlegen:

import numpy as np
import matplotlib.pyplot as plt

x = np.random.normal(1, 2, 5000)
y = np.random.normal(-1, 3, 2000)
x_w = np.empty(x.shape)
x_w.fill(1/x.shape[0])
y_w = np.empty(y.shape)
y_w.fill(1/y.shape[0])
bins = np.linspace(-10, 10, 30)

plt.hist([x, y], bins, weights=[x_w, y_w], label=['x', 'y'])
plt.legend(loc='upper right')
plt.show()

Geben Sie hier die Bildbeschreibung ein

Zum Vergleich die gleichen xund yVektoren mit Standardgewichten und density=True:

Geben Sie hier die Bildbeschreibung ein

Jojo
quelle
9

Sie sollten binsaus den zurückgegebenen Werten Folgendes verwenden hist:

import numpy as np
import matplotlib.pyplot as plt

foo = np.random.normal(loc=1, size=100) # a normal distribution
bar = np.random.normal(loc=-1, size=10000) # a normal distribution

_, bins, _ = plt.hist(foo, bins=50, range=[-6, 6], normed=True)
_ = plt.hist(bar, bins=bins, alpha=0.5, normed=True)

Zwei Matplotlib-Histogramme mit gleichem Binning

Adrien Renaud
quelle
7

Hier ist eine einfache Methode, um zwei Histogramme mit ihren Balken nebeneinander auf demselben Diagramm zu zeichnen, wenn die Daten unterschiedliche Größen haben:

def plotHistogram(p, o):
    """
    p and o are iterables with the values you want to 
    plot the histogram of
    """
    plt.hist([p, o], color=['g','r'], alpha=0.8, bins=50)
    plt.show()
stochastic_zeitgeist
quelle
2

Nur für den Fall, dass Sie pandas ( import pandas as pd) haben oder damit einverstanden sind:

test = pd.DataFrame([[random.gauss(3,1) for _ in range(400)], 
                     [random.gauss(4,2) for _ in range(400)]])
plt.hist(test.values.T)
plt.show()
serv-inc
quelle
Ich glaube, die Verwendung von Pandas funktioniert nicht, wenn die zu vergleichenden Histogramme unterschiedliche Stichprobengrößen haben. Dies ist auch häufig der Kontext, in dem normalisierte Histogramme verwendet werden.
Solomon Vimal
2

Es gibt eine Einschränkung, wenn Sie das Histogramm aus einem 2D-Numpy-Array zeichnen möchten. Sie müssen die 2 Achsen tauschen.

import numpy as np
import matplotlib.pyplot as plt

data = np.random.normal(size=(2, 300))
# swapped_data.shape == (300, 2)
swapped_data = np.swapaxes(x, axis1=0, axis2=1)
plt.hist(swapped_data, bins=30, label=['x', 'y'])
plt.legend()
plt.show()

Geben Sie hier die Bildbeschreibung ein

黄锐铭
quelle
0

Diese Frage wurde bereits zuvor beantwortet, wollte jedoch eine weitere schnelle und einfache Problemumgehung hinzufügen, die anderen Besuchern bei dieser Frage helfen könnte.

import seasborn as sns 
sns.kdeplot(mydata1)
sns.kdeplot(mydata2)

Einige hilfreiche Beispiele sind hier für kde vs Histogramm Vergleich.

Solomon Vimal
quelle
0

Inspiriert von Solomons Antwort, aber um bei der Frage zu bleiben, die sich auf das Histogramm bezieht, ist eine saubere Lösung:

sns.distplot(bar)
sns.distplot(foo)
plt.show()

Stellen Sie sicher, dass Sie zuerst das größere zeichnen, da Sie sonst plt.ylim (0,0.45) einstellen müssen, damit das größere Histogramm nicht abgeschnitten wird.

Sarah
quelle
0

Auch eine Option, die Joaquin Antwort ziemlich ähnlich ist:

import random
from matplotlib import pyplot

#random data
x = [random.gauss(3,1) for _ in range(400)]
y = [random.gauss(4,2) for _ in range(400)]

#plot both histograms(range from -10 to 10), bins set to 100
pyplot.hist([x,y], bins= 100, range=[-10,10], alpha=0.5, label=['x', 'y'])
#plot legend
pyplot.legend(loc='upper right')
#show it
pyplot.show()

Gibt die folgende Ausgabe:

Geben Sie hier die Bildbeschreibung ein

PV8
quelle