Cómo agregar etiquetas de valor en un gráfico de barras
Estoy creando un gráfico de barras y no sé cómo agregar etiquetas de valor en las barras (en el centro de la barra o justo encima).
Creo que la solución es 'texto' o 'anotar', pero yo: a) no sé cuál usar (y, en general, no he descubierto cuándo usar cuál). b) no puedo ver cómo presentar las etiquetas de valor.
Aquí está mi código:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
pd.set_option('display.mpl_style', 'default')
%matplotlib inline
# 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(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))
fig = freq_series.plot(kind='bar')
fig.set_title('Amount Frequency')
fig.set_xlabel('Amount ($)')
fig.set_ylabel('Frequency')
fig.set_xticklabels(x_labels)
¿Cómo puedo agregar etiquetas de valor en las barras (en el centro de la barra o justo encima)?
En primer lugar freq_series.plot
, devuelve un eje, no una figura, así que para que mi respuesta sea un poco más clara, cambié el código proporcionado para referirme a él ax
en lugar de fig
ser más consistente con otros ejemplos de código.
Puede obtener la lista de las barras producidas en la trama del ax.patches
miembro. Luego puede utilizar la técnica demostrada en este matplotlib
ejemplo de galería para agregar las etiquetas usando el ax.text
método.
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(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)
rects = ax.patches
# Make some labels.
labels = [f"label{i}" for i in range(len(rects))]
for rect, label in zip(rects, labels):
height = rect.get_height()
ax.text(
rect.get_x() + rect.get_width() / 2, height + 5, label, ha="center", va="bottom"
)
plt.show()
Esto produce un gráfico etiquetado que se parece a:
Basado en una característica mencionada en esta respuesta a otra pregunta, encontré una solución de aplicación muy general para colocar etiquetas en un gráfico de barras.
Desafortunadamente, otras soluciones no funcionan en muchos casos, porque el espacio entre la etiqueta y la barra se da en unidades absolutas de las barras o se escala según la altura de la barra . El primero sólo funciona para un rango estrecho de valores y el segundo proporciona un espaciado inconsistente dentro de un gráfico. Ninguno de los dos funciona bien con ejes logarítmicos.
La solución que propongo funciona independientemente de la escala (es decir, para números grandes y pequeños) e incluso coloca correctamente etiquetas para valores negativos y con escalas logarítmicas porque utiliza la unidad visual points
para las compensaciones.
Agregué un número negativo para mostrar la ubicación correcta de las etiquetas en tal caso.
El valor de la altura de cada barra se utiliza como etiqueta para la misma. Se pueden utilizar fácilmente otras etiquetas con el fragmento de Simonfor rect, label in zip(rects, labels)
.
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")
Editar: extraje la funcionalidad relevante en una función, como lo sugiere barnhillec .
Esto produce el siguiente resultado:
Y con la escala logarítmica (y algunos ajustes a los datos de entrada para mostrar la escala logarítmica), este es el resultado:
A partir dematplotlib v3.4.0
- Usar
matplotlib.pyplot.bar_label
- La posición predeterminada de la etiqueta, configurada con el parámetro
label_type
, es'edge'
. Para centrar las etiquetas en el medio de la barra, utilice'center'
kwargs
Se pasan adicionales aAxes.annotate
, que acepta .Text
kwargs
- Se pueden utilizar propiedades como
color
,rotation
,fontsize
, etc.
- Se pueden utilizar propiedades como
- La posición predeterminada de la etiqueta, configurada con el parámetro
- Consulte la página matplotlib: demostración de etiqueta de barra para obtener opciones de formato adicionales.
- Probado en
python 3.11.2
,pandas 2.0.0
,matplotlib 3.7.1
,seaborn 0.12.2
ax.containers
es unlist
deBarContainer artists
- Con un gráfico de barras de un solo nivel, es una lista de len 1, por lo que
[0]
se utiliza. - Para gráficos de barras agrupadas y apiladas, habrá más objetos en el
list
- Con un gráfico de barras de un solo nivel, es una lista de len 1, por lo que
apilados | Agrupados |
---|---|
Cómo anotar cada segmento de un gráfico de barras apiladas | Cómo trazar y anotar barras agrupadas en seaborn |
Gráfico de barras apiladas con etiquetas centradas | Cómo trazar y anotar un gráfico de barras agrupadas |
- Se puede realizar un formato de etiqueta simple con el
fmt
parámetro, como se muestra en los ejemplos de demostración y en Cómo anotar un diagrama de barras seaborn con el valor agregado . - Con
matplotlib 3.7 Update
, elfmt
argumento debar_label
ahora acepta cadenas de formato de estilo {}. - Los valores excluidos se pueden implementar con
fmt
.ax.bar_label(ax.containers[0], fmt=lambda x: x if x > 0 else '', label_type='edge')
ax.bar_label(ax.containers[0], fmt=lambda x: f'{x:0.0f}' if x > 0 else '', label_type='edge')
ax.bar_label(ax.containers[0], fmt=lambda x: np.where(x > 0, x, ''), label_type='center')
connp.where
.- Si itera a través de varios contenedores, en el caso de barras agrupadas o apiladas, utilice
c
en lugar deax.containers[0]
, confor c in ax.containers:
- Para cambios extensos en las etiquetas de las barras, aún puede ser mejor usar el
labels
parámetro, como se muestra en los ejemplos de demostración y lo siguiente:- Como se muestra aquí ,
labels = [f'{h:.1f}%' if (h := v.get_height()) > 0 else '' for v in ax.containers[0]]
yax.bar_label(ax.containers[0], labels=labels, label_type='center')
para eliminar etiquetas menores que 0. Úselo.get_width()
para barras horizontales, como se muestra aquí .
- Como se muestra aquí ,
Ejemplos conlabel= |
Ejemplos conlabel= |
---|---|
apilar el gráfico de barras en matplotlib y agregar una etiqueta a cada sección | Cómo anotar un diagrama de barras apiladas y agregar etiquetas de leyenda |
Cómo agregar múltiples anotaciones a un diagrama de barras | Cómo personalizar las anotaciones de la barra para no mostrar los valores seleccionados |
Cómo trazar una barra apilada horizontal con anotaciones | Cómo anotar gráficos de barras al agregar barras de error |
Cómo alinear anotaciones al final de un gráfico de barras horizontales |
import pandas as pd
# dataframe using frequencies and x_labels from the OP
df = pd.DataFrame({'Frequency': frequencies}, index=x_labels)
# display(df)
Frequency
108300.0 6
110540.0 16
112780.0 75
115020.0 160
117260.0 244
# plot
ax = df.plot(kind='bar', figsize=(12, 8), title='Amount Frequency',
xlabel='Amount ($)', ylabel='Frequency', legend=False)
# annotate
ax.bar_label(ax.containers[0], label_type='edge')
# pad the spacing between the number and the edge of the figure
ax.margins(y=0.1)
- Especifique adicional
kwargs
para una personalización adicional- Acepta parámetros de
matplotlib.axes.Axes.text
- Acepta parámetros de
ax.bar_label(ax.containers[0], label_type='edge', color='red', rotation=90, fontsize=7, padding=3)
Gráfico a nivel de ejes de Seaborn
- Como se puede observar, es exactamente igual que con
ax.bar(...)
,plt.bar(...)
, ydf.plot(kind='bar',...)
import seaborn as sns
# plot data
fig, ax = plt.subplots(figsize=(12, 8))
sns.barplot(x=x_labels, y=frequencies, ax=ax)
# annotate
ax.bar_label(ax.containers[0], label_type='edge')
# pad the spacing between the number and the edge of the figure
ax.margins(y=0.1)
Trama a nivel de figura de Seaborn
seaborn.catplot
acepta un marco de datos paradata
.- Dado que
.catplot
es un FacetGrid (subtramas), la única diferencia es iterar a través de cada eje de la figura para usar.bar_labels
.
import pandas as pd
import seaborn as sns
# load the data into a dataframe
df = pd.DataFrame({'Frequency': frequencies, 'amount': x_labels})
# plot
g = sns.catplot(kind='bar', data=df, x='amount', y='Frequency', height=6, aspect=1.5)
# iterate through the axes
for ax in g.axes.flat:
# annotate
ax.bar_label(ax.containers[0], label_type='edge')
# pad the spacing between the number and the edge of the figure; should be in the loop, otherwise only the last subplot would be adjusted
ax.margins(y=0.1)
matplotlib.axes.Axes.bar
- Será similar si solo usas
matplotlib.pyplot.bar
import matplotlib.pyplot as plt
# create the xticks beginning a index 0
xticks = range(len(frequencies))
# plot
fig, ax = plt.subplots(figsize=(12, 8))
ax.bar(x=xticks, height=frequencies)
# label the xticks
ax.set_xticks(xticks, x_labels)
# annotate
ax.bar_label(ax.containers[0], label_type='edge')
# pad the spacing between the number and the edge of the figure
ax.margins(y=0.1)
Otros ejemplos usandobar_label
Respuestas SO vinculadas | Respuestas SO vinculadas |
---|---|
Cómo crear y anotar un gráfico de barras proporcionales apiladas | Cómo envolver etiquetas de marca largas en un gráfico a nivel de figura nacido en el mar |
Cómo calcular el porcentaje por fila y anotar barras apiladas al 100 por ciento | Cómo anotar un diagrama de barras con porcentaje por tono/grupo de leyenda |
Las barras apiladas se anotan inesperadamente con la suma de las alturas de las barras. | Cómo agregar porcentajes encima de las barras en seaborn |
Cómo trazar y anotar barras agrupadas | Cómo trazar el porcentaje con seaborn distplot / histplot / displot |
Cómo anotar un gráfico de barras con valores diferentes a los de get_height() | Cómo trazar barras agrupadas en el orden correcto |
Barra de pandas cómo etiquetar los valores deseados | Problema al trazar dos listas con diferentes tamaños usando matplotlib |
Cómo mostrar el porcentaje encima del gráfico de barras agrupadas | Cómo anotar solo una categoría de un diagrama de barras apiladas |
Cómo configurar la rotación de etiquetas y agregar anotaciones de barra | Cómo aumentar el tamaño del texto de la subtrama y agregar anotaciones de diagrama de barras personalizadas |
Cómo agregar métricas de grupo y trazar datos con pandas | Cómo obtener un diagrama de barras agrupadas de datos categóricos |
Cómo trazar una barra apilada con anotaciones para múltiples grupos | Cómo crear diagramas de barras agrupados en una sola figura a partir de un marco de datos amplio |
Cómo anotar un diagrama de pila o un diagrama de área | Cómo determinar si el último valor en todas las columnas es mayor que n |
Cómo trazar barras agrupadas | Cómo trazar el recuento de elementos y agregar anotaciones |
Cómo agregar múltiples etiquetas de datos en un gráfico de barras en matplotlib | Seaborn Catplot establece valores sobre las barras |
Python matplotlib múltiples barras | La etiqueta del gráfico circular de Matplotlib no coincide con el valor |
El parámetro plt grid ALPHA no funciona en matplotlib | Cómo centrar horizontalmente una anotación de diagrama de barras |
Building off the above (great!) answer, we can also make a horizontal bar plot with just a few adjustments:
# Bring some raw data.
frequencies = [6, -16, 75, 160, 244, 260, 145, 73, 16, 4, 1]
freq_series = pd.Series(frequencies)
y_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='barh')
ax.set_title('Amount Frequency')
ax.set_xlabel('Frequency')
ax.set_ylabel('Amount ($)')
ax.set_yticklabels(y_labels)
ax.set_xlim(-40, 300) # expand xlim to make labels easier to read
rects = ax.patches
# For each bar: Place a label
for rect in rects:
# Get X and Y placement of label from rect.
x_value = rect.get_width()
y_value = rect.get_y() + rect.get_height() / 2
# Number of points between bar and label. Change to your liking.
space = 5
# Vertical alignment for positive values
ha = 'left'
# If value of bar is negative: Place label left of bar
if x_value < 0:
# Invert space to place label to the left
space *= -1
# Horizontally align label at right
ha = 'right'
# Use X value as label and format number with one decimal place
label = "{:.1f}".format(x_value)
# Create annotation
plt.annotate(
label, # Use `label` as label
(x_value, y_value), # Place label at end of the bar
xytext=(space, 0), # Horizontally shift label by `space`
textcoords="offset points", # Interpret `xytext` as offset in points
va='center', # Vertically center label
ha=ha) # Horizontally align label differently for
# positive and negative values.
plt.savefig("image.png")