Matplotlib - beschriften Sie jeden Behälter

74

Ich verwende derzeit Matplotlib, um ein Histogramm zu erstellen:

Geben Sie hier die Bildbeschreibung ein

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as pyplot
...
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1,)
n, bins, patches = ax.hist(measurements, bins=50, range=(graph_minimum, graph_maximum), histtype='bar')

#ax.set_xticklabels([n], rotation='vertical')

for patch in patches:
    patch.set_facecolor('r')

pyplot.title('Spam and Ham')
pyplot.xlabel('Time (in seconds)')
pyplot.ylabel('Bits of Ham')
pyplot.savefig(output_filename)

Ich möchte die Beschriftungen der x-Achse etwas aussagekräftiger machen.

Erstens scheinen die x-Achsen-Ticks hier auf fünf Ticks beschränkt zu sein. Egal was ich mache, ich kann das scheinbar nicht ändern - selbst wenn ich mehr xticklabels hinzufüge, werden nur die ersten fünf verwendet. Ich bin nicht sicher, wie Matplotlib dies berechnet, aber ich gehe davon aus, dass es automatisch aus dem Bereich / den Daten berechnet wird.

Gibt es eine Möglichkeit, die Auflösung von X-Tick-Beschriftungen zu erhöhen - sogar bis zu einem Punkt für jeden Balken / Behälter?

(Idealerweise möchte ich auch, dass die Sekunden in Mikrosekunden / Millisekunden neu formatiert werden, aber das ist eine Frage für einen anderen Tag).

Zweitens möchte ich, dass jeder einzelne Balken beschriftet wird - mit der tatsächlichen Nummer in diesem Behälter sowie dem Prozentsatz der Gesamtzahl aller Behälter.

Die endgültige Ausgabe könnte ungefähr so ​​aussehen:

Geben Sie hier die Bildbeschreibung ein

Ist so etwas mit Matplotlib möglich?

Prost, Victor

victorhooi
quelle

Antworten:

122

Sicher! Um die Zecken zu setzen, einfach, gut ... Setzen Sie die Zecken (siehe matplotlib.pyplot.xticksoder ax.set_xticks). (Außerdem müssen Sie die Gesichtsfarbe der Patches nicht manuell festlegen. Sie können einfach ein Schlüsselwortargument übergeben.)

Im Übrigen müssen Sie mit der Beschriftung etwas ausgefallenere Dinge tun, aber matplotlib macht es ziemlich einfach.

Als Beispiel:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import FormatStrFormatter

data = np.random.randn(82)
fig, ax = plt.subplots()
counts, bins, patches = ax.hist(data, facecolor='yellow', edgecolor='gray')

# Set the ticks to be at the edges of the bins.
ax.set_xticks(bins)
# Set the xaxis's tick labels to be formatted with 1 decimal place...
ax.xaxis.set_major_formatter(FormatStrFormatter('%0.1f'))

# Change the colors of bars at the edges...
twentyfifth, seventyfifth = np.percentile(data, [25, 75])
for patch, rightside, leftside in zip(patches, bins[1:], bins[:-1]):
    if rightside < twentyfifth:
        patch.set_facecolor('green')
    elif leftside > seventyfifth:
        patch.set_facecolor('red')

# Label the raw counts and the percentages below the x-axis...
bin_centers = 0.5 * np.diff(bins) + bins[:-1]
for count, x in zip(counts, bin_centers):
    # Label the raw counts
    ax.annotate(str(count), xy=(x, 0), xycoords=('data', 'axes fraction'),
        xytext=(0, -18), textcoords='offset points', va='top', ha='center')

    # Label the percentages
    percent = '%0.0f%%' % (100 * float(count) / counts.sum())
    ax.annotate(percent, xy=(x, 0), xycoords=('data', 'axes fraction'),
        xytext=(0, -32), textcoords='offset points', va='top', ha='center')


# Give ourselves some more room at the bottom of the plot
plt.subplots_adjust(bottom=0.15)
plt.show()

Geben Sie hier die Bildbeschreibung ein

Joe Kington
quelle
Aha, großartig =). Ein weiterer Hinweis - ursprünglich habe ich "fig = pyplot.figure (figsize = (32,24),)" und "ax = fig.add_subplot (1,1,1,)" verwendet, um die Größe der Figur festzulegen. Wenn ich jedoch das zweite Pfandrecht gegen Ihr "fig, ax = pyplot.subplots ()" austausche, scheint es meine Feigengröße jetzt zu ignorieren? Irgendeine Idee warum?
Victorhooi
@victorhooi - Es sollte funktionieren, wenn Sie nur die figsize als kwarg angeben subplots. ZB fig, ax = plt.subplots(figsize=(32, 34)) Wenn es nicht, vielleicht ist es ein Bug? subplotswurde nur 1.0als Komfortfunktion hinzugefügt .
Joe Kington
Kingston: Aha, großartig, yup, diese Zeile funktioniert =). Du bist großartig, Alter. Es gibt einen letzten Fehler, den ich nicht verstehe - den xlabel-Text direkt unter dem Annotationstext - ich bin mir nicht sicher, wie ich ihn versetzen soll. Ich habe versucht "ax.xaxis.LABELPAD = 30", aber es schien das zu ignorieren.
Victorhooi
@victorhooi - Es gibt verschiedene Möglichkeiten, die Zeckenauffüllung festzulegen, aber die einfachste ist ax.tick_params(axis='x', pad=30)(was etwas eingängig ist). Ich hoffe, das hilft!
Joe Kington
@ Joe Kingston: Hmm, habe das versucht, aber es verschiebt sowohl das x-Achsen-Label als auch die Häkchen. Verdammt noch mal. Lol. Wie auch immer, ich denke, dies verdient eine weitere Frage, deshalb habe ich sie hier erneut veröffentlicht: stackoverflow.com/questions/6406368/…
victorhooi
0

Um Ihren Achsenbeschriftungen SI-Präfixe hinzuzufügen, möchten Sie QuantiPhy verwenden . Tatsächlich enthält die Dokumentation ein Beispiel, das genau zeigt, wie dies zu tun ist: MatPlotLib-Beispiel .

Ich denke, Sie würden Ihrem Code so etwas hinzufügen:

from matplotlib.ticker import FuncFormatter
from quantiphy import Quantity

time_fmtr = FuncFormatter(lambda v, p: Quantity(v, 's').render(prec=2))
ax.xaxis.set_major_formatter(time_fmtr)
August West
quelle
0

Eine Sache, die ich mit "Dichte = Wahr" zu den Plots im Histogramm hinzufügen wollte, waren die relativen Frequenzwerte für jeden Bin, Suche, aber ich konnte keine Funktion finden, die das tun würde. Eine Lösung, die ich gemacht habe, folgt als Bild:

BEISPIEL-PLOT-BILD

Die Funktion:

def label_densityHist(ax, n, bins, x=4, y=0.01, r=2, **kwargs):
"""
Add labels,relative value of bin, to each bin in a density histogram .
:param ax: Object axe of matplotlib
        The axis to plot.
:param n: list, array of int, float
        The values of the histogram bins.
:param bins: list, array of int, float
        The edges of the bins.
:param x: int, float
        Related the x position of the bin labels. The higher, the lower the value on the x-axis.
        Default: 4
:param y: int, float
        Related the y position of the bin labels. The higher, the greater the value on the y-axis.
        Default: 0.01
:param r: int
        Number of decimal places.
        Default: 2
:param **kwargs: Text properties in matplotlib
:return: None


Example

import matplotlib.pyplot as plt
import numpy as np

dados = np.random.randn(100)

axe = plt.gca()
n, bins, _ = axe.hist(x=dados, edgecolor='black')
label_densityHist(axe,n, bins)
plt.show()

Example:
import matplotlib.pyplot as plt
import numpy as np


dados = np.random.randn(100)

axe = plt.gca()
n, bins, _ = axe.hist(x=dados, edgecolor='black')
label_densityHist(axe,n, bins, x=6, fontsize='large')
plt.show()


Reference:
[1]https://matplotlib.org/3.1.1/api/text_api.html#matplotlib.text.Text

"""

k = []
# calculate the relative frequency of each bin
for i in range(0,len(n)):
    k.append((bins[i+1]-bins[i])*n[i])

# rounded
k = around(k,r); #print(k)

# plot the label/text to each bin
for i in range(0, len(n)):
    x_pos = (bins[i + 1] - bins[i]) / x + bins[i]
    y_pos = n[i] + (n[i] * y)
    label = str(k[i]) # relative frequency of each bin
    ax.text(x_pos, y_pos, label, kwargs)
Robert Garcia
quelle