Basierend auf einer in dieser Antwort auf eine andere Frage erwähnten Funktion habe ich eine sehr allgemein anwendbare Lösung zum Platzieren von Beschriftungen auf einem Balkendiagramm gefunden.
Andere Lösungen funktionieren leider in vielen Fällen nicht, da der Abstand zwischen Etikett und Balken entweder in absoluten Einheiten der Balken angegeben oder durch die Höhe des Balkens skaliert wird . Ersteres funktioniert nur für einen engen Wertebereich und letzteres ergibt einen inkonsistenten Abstand innerhalb eines Diagramms. Beides funktioniert nicht gut mit logarithmischen Achsen.
Die von mir vorgeschlagene Lösung funktioniert unabhängig von der Skalierung (dh für kleine und große Zahlen) und platziert sogar Beschriftungen für negative Werte und mit logarithmischen Skalierungen korrekt, da die visuelle Einheit points
für Offsets verwendet wird.
Ich habe eine negative Zahl hinzugefügt, um die korrekte Platzierung der Etiketten in einem solchen Fall anzuzeigen.
Der Wert der Höhe jedes Balkens wird als Beschriftung dafür verwendet. Andere Labels können problemlos mit Simons for rect, label in zip(rects, labels)
Snippet verwendet werden .
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Bring some raw data.
frequencies = [6, -16, 75, 160, 244, 260, 145, 73, 16, 4, 1]
# In my original code I create a series and run on that,
# so for consistency I create a series from the list.
freq_series = pd.Series.from_array(frequencies)
x_labels = [108300.0, 110540.0, 112780.0, 115020.0, 117260.0, 119500.0,
121740.0, 123980.0, 126220.0, 128460.0, 130700.0]
# Plot the figure.
plt.figure(figsize=(12, 8))
ax = freq_series.plot(kind='bar')
ax.set_title('Amount Frequency')
ax.set_xlabel('Amount ($)')
ax.set_ylabel('Frequency')
ax.set_xticklabels(x_labels)
def add_value_labels(ax, spacing=5):
"""Add labels to the end of each bar in a bar chart.
Arguments:
ax (matplotlib.axes.Axes): The matplotlib object containing the axes
of the plot to annotate.
spacing (int): The distance between the labels and the bars.
"""
# For each bar: Place a label
for rect in ax.patches:
# Get X and Y placement of label from rect.
y_value = rect.get_height()
x_value = rect.get_x() + rect.get_width() / 2
# Number of points between bar and label. Change to your liking.
space = spacing
# Vertical alignment for positive values
va = 'bottom'
# If value of bar is negative: Place label below bar
if y_value < 0:
# Invert space to place label below
space *= -1
# Vertically align label at top
va = 'top'
# Use Y value as label and format number with one decimal place
label = "{:.1f}".format(y_value)
# Create annotation
ax.annotate(
label, # Use `label` as label
(x_value, y_value), # Place label at end of the bar
xytext=(0, space), # Vertically shift label by `space`
textcoords="offset points", # Interpret `xytext` as offset in points
ha='center', # Horizontally center label
va=va) # Vertically align label differently for
# positive and negative values.
# Call the function above. All the magic happens there.
add_value_labels(ax)
plt.savefig("image.png")
Bearbeiten: Ich habe die relevante Funktionalität in einer Funktion extrahiert, wie von Barnhillec vorgeschlagen .
Dies erzeugt die folgende Ausgabe:
Und mit der logarithmischen Skalierung (und einigen Anpassungen an den Eingabedaten, um die logarithmische Skalierung darzustellen) ist dies das Ergebnis: