Cómo agregar etiquetas de valor en un gráfico de barras

13 minutos de lectura

avatar de usuario
Optimismo

Me quedé atascado en algo que parece que debería ser relativamente fácil. El código que traigo a continuación es una muestra basada en un proyecto más grande en el que estoy trabajando. No vi ninguna razón para publicar todos los detalles, así que acepte las estructuras de datos que traigo tal como están.

Básicamente, estoy creando un gráfico de barras y puedo descubrir cómo agregar etiquetas de valor en las barras (en el centro de la barra o justo encima). He estado mirando muestras en la web pero sin éxito implementando en mi propio código. Creo que la solución es con ‘texto’ o ‘anotar’, pero yo: a) no sé cuál usar (y, en términos generales, no he descubierto cuándo usar cuál). b) no puede ver para llegar a presentar las etiquetas de valor. Agradecería su ayuda, mi código a continuación. ¡Gracias por adelantado!

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)

ingrese la descripción de la imagen aquí

  • Matplotlib tiene una demostración: matplotlib.org/examples/api/barchart_demo.html

    – Dan

    19 de noviembre de 2017 a las 1:55

  • Para matplotlib >= 3.4.2 usar .bar_label, como se muestra en esta respuesta. Se aplica a pandas y seabornque utilizan matplotlib.

    –Trenton McKinney

    26 oct 2021 a las 19:59


avatar de usuario
Simón Gibbons

En primer lugar freq_series.plot devuelve un eje no una figura para que mi respuesta sea un poco más clara, he cambiado su código dado para referirme a él como ax más bien que fig para ser más coherente con otros ejemplos de código.

Puede obtener la lista de las barras producidas en el gráfico desde el ax.patches miembro. Entonces puedes usar la técnica demostrada en este matplotlib ejemplo de galeria 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 una trama etiquetada que se parece a:

ingrese la descripción de la imagen aquí

  • Una figura es una colección de uno o más ejes, por ejemplo, en este en este ejemplo matplotlib.org/examples/statistics/… es una figura que se compone de 4 ejes diferentes.

    – Simón Gibbons

    8 de marzo de 2015 a las 21:19


  • Buena solución. Escribí una publicación de blog que se basa en la solución aquí y brinda una versión un poco más robusta que se escala según la altura del eje, por lo que el mismo código funciona para diferentes gráficos que tienen diferentes alturas de eje: composición.al/blog/2015/11/29/…

    – Lindsey Kuper

    5 oct 2016 a las 22:57

avatar de usuario
soloparatherec

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 es escalado por la altura de la barra. El primero solo funciona para un rango estrecho de valores y el segundo proporciona un espaciado inconsistente dentro de una parcela. Tampoco funciona bien con ejes logarítmicos.

La solución que propongo funciona independientemente de la escala (es decir, para números pequeños y grandes) e incluso coloca correctamente etiquetas para valores negativos y con escalas logarítmicas porque utiliza la unidad visual points para compensaciones.

He agregado 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 usar fácilmente otras etiquetas con Simon’s for rect, label in zip(rects, labels) retazo.

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: he extraído la funcionalidad relevante en una función, como lo sugiere barnhillec.

Esto produce la siguiente salida:

Gráfico de barras con etiquetas colocadas automáticamente en cada barra

Y con escala logarítmica (y algunos ajustes a los datos de entrada para mostrar la escala logarítmica), este es el resultado:

Gráfico de barras con escala logarítmica con etiquetas colocadas automáticamente en cada barra

avatar de usuario
oleson

Partiendo de la respuesta anterior (¡excelente!), También podemos hacer un gráfico de barras horizontales con solo unos pocos ajustes:

# 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")

gráfico de barras horizontales con anotaciones

avatar de usuario
Ajay

Si solo desea etiquetar los puntos de datos sobre la barra, puede usar plt.annotate()

Mi código:

import numpy as np
import matplotlib.pyplot as plt

n = [1,2,3,4,5,]
s = [i**2 for i in n]
line = plt.bar(n,s)
plt.xlabel('Number')
plt.ylabel("Square")

for i in range(len(s)):
    plt.annotate(str(s[i]), xy=(n[i],s[i]), ha="center", va="bottom")

plt.show()

Al especificar una alineación horizontal y vertical de 'center' y 'bottom' respectivamente, uno puede obtener anotaciones centradas.

un gráfico de barras etiquetado

avatar de usuario
trenton mckinney

A partir de matplotlib v3.4.2

  • Usar matplotlib.pyplot.bar_label
    • La posición predeterminada de la etiqueta, establecida con el parámetro label_typees 'edge'. Para centrar las etiquetas en el medio de la barra, utilice 'center'
    • Adicional kwargs se pasan a Axes.annotateque acepta Text kwargs.
  • Ver el matplotlib: Demostración de etiqueta de barra página para opciones de formato adicionales.
  • Probado en python 3.10, pandas 1.4.2, matplotlib 3.5.1, seaborn 0.11.2
  • ax.containers es un list de BarContainer artists
    • Con un gráfico de barras de un solo nivel, es una lista de len 1, por lo tanto [0] se usa
    • Para diagramas de barras agrupadas y apiladas habrá más objetos en el list
apilado 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
  • El formato de etiqueta simple se puede hacer con el fmt como se muestra en los ejemplos de demostración y en Cómo anotar un gráfico de barras marino con el valor agregado.
  • Un formato de etiqueta más sofisticado debe usar el label parámetro, como se muestra en los ejemplos de demostración y los siguientes
Ejemplos con label= Ejemplos con label=
apilar diagrama de barra en matplotlib y agregar etiqueta a cada sección Cómo anotar un diagrama de barras apiladas y agregar etiquetas de leyenda
Cómo agregar varias anotaciones a un diagrama de barras Cómo personalizar las anotaciones de la barra para que no muestren 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)

ingrese la descripción de la imagen aquí

ax.bar_label(ax.containers[0], label_type="edge", color="red", rotation=90, fontsize=7, padding=3)

ingrese la descripción de la imagen aquí

Gráfico de nivel de ejes Seaborn

import seaborn as sns

# plot data
fig, ax = plt.subplots(figsize=(12, 8))
g = sns.barplot(x=x_labels, y=frequencies, ax=ax)

# annotate
g.bar_label(ax.containers[0], label_type="edge")

# pad the spacing between the number and the edge of the figure
g.margins(y=0.1)

ingrese la descripción de la imagen aquí

Trama de nivel de figura de Seaborn

  • seaborn.catplot acepta una trama de datos para data
  • Iterar a través de cada eje de la figura para agregar etiquetas de barra
import pandas as pd
import seaborn as sns

# load the data into a dataframe
df = pd.DataFrame({'Frequency': frequencies, 'amount': x_labels})

# 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
    ax.margins(y=0.1)

ingrese la descripción de la imagen aquí

matplotlib.axes.Axes.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)

ingrese la descripción de la imagen aquí

Ejemplos con bar_label

Respuestas SO vinculadas Respuestas SO vinculadas
Cómo crear y anotar un gráfico de barras proporcionales apiladas Cómo envolver etiquetas de ticks largas en una gráfica de nivel de figura nacida en el mar
Cómo calcular el porcentaje por fila y anotar barras apiladas al 100 por ciento Cómo anotar un gráfico de barras con un porcentaje por grupo de tono/leyenda
Las barras apiladas se anotan inesperadamente con la suma de las alturas de las barras Cómo agregar porcentajes en la parte superior de las barras en seaborn
Cómo trazar y anotar barras agrupadas Cómo trazar 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
Pandas bar cómo etiquetar los valores deseados Problema al trazar dos listas con diferentes tamaños usando matplotlib
Cómo mostrar el porcentaje sobre el gráfico de barras agrupadas Cómo anotar solo una categoría de un gráfico de barras apiladas
Cómo configurar la rotación de la etiqueta de tick y agregar anotaciones de barra Cómo aumentar el tamaño del texto de la subparcela y agregar anotaciones de barra personalizadas
Cómo agregar métricas de grupo y trazar datos con pandas Cómo obtener un gráfico de barras agrupadas de datos categóricos
Cómo trazar una barra apilada con anotaciones para varios grupos Cómo crear diagramas de barras agrupadas 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 varias barras La etiqueta del gráfico circular de Matplotlib no coincide con el valor
El parámetro ALPHA de cuadrícula plt no funciona en matplotlib Cómo centrar horizontalmente una anotación de gráfico de barras

avatar de usuario
haramoz

También necesitaba las etiquetas de la barra, tenga en cuenta que mi eje y tiene una vista ampliada usando límites en el eje y. Los cálculos predeterminados para colocar las etiquetas en la parte superior de la barra aún funcionan con la altura (use_global_coordinate=False en el ejemplo). Pero quería mostrar que las etiquetas también se pueden colocar en la parte inferior del gráfico en la vista ampliada usando coordenadas globales en matplotlib 3.0.2. Espero que ayude a alguien.

def autolabel(rects,data):
"""
Attach a text label above each bar displaying its height
"""
c = 0
initial = 0.091
offset = 0.205
use_global_coordinate = True

if use_global_coordinate:
    for i in data:        
        ax.text(initial+offset*c, 0.05, str(i), horizontalalignment="center",
                verticalalignment="center", transform=ax.transAxes,fontsize=8)
        c=c+1
else:
    for rect,i in zip(rects,data):
        height = rect.get_height()
        ax.text(rect.get_x() + rect.get_width()/2., height,str(i),ha="center", va="bottom")

Salida de ejemplo

avatar de usuario
usuario11638654

Si solo desea agregar puntos de datos sobre las barras, puede hacerlo fácilmente con:

 for i in range(len(frequencies)): # your number of bars
    plt.text(x = x_values[i]-0.25, #takes your x values as horizontal positioning argument 
    y = y_values[i]+1, #takes your y values as vertical positioning argument 
    s = data_labels[i], # the labels you want to add to the data
    size = 9) # font size of datalabels

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad