Com base em um recurso mencionado nesta resposta a outra pergunta , descobri uma solução muito aplicável para colocar rótulos em um gráfico de barras.
Outras soluções infelizmente não funcionam em muitos casos, porque o espaçamento entre o rótulo e a barra é dado em unidades absolutas das barras ou é escalado pela altura da barra . O primeiro só funciona para uma faixa estreita de valores e o último fornece espaçamento inconsistente dentro de um gráfico. Nenhum dos dois funciona bem com eixos logarítmicos.
A solução que proponho funciona independente da escala (ou seja, para números pequenos e grandes) e até mesmo coloca rótulos corretamente para valores negativos e com escalas logarítmicas porque usa a unidade visual points
para deslocamentos.
Eu adicionei um número negativo para mostrar a colocação correta das etiquetas em tal caso.
O valor da altura de cada barra é usado como um rótulo para ela. Outros rótulos podem ser facilmente usados com o for rect, label in zip(rects, labels)
snippet do Simon .
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")
Edit: Extraí a funcionalidade relevante em uma função, conforme sugerido por barnhillec .
Isso produz a seguinte saída:
E com a escala logarítmica (e alguns ajustes nos dados de entrada para mostrar a escala logarítmica), este é o resultado: