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

Resuelto Optimesh asked hace 9 años • 0 respuestas

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)

ingrese la descripción de la imagen aquí

¿Cómo puedo agregar etiquetas de valor en las barras (en el centro de la barra o justo encima)?

Optimesh avatar Mar 09 '15 03:03 Optimesh
Aceptado

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 axen lugar de figser más consistente con otros ejemplos de código.

Puede obtener la lista de las barras producidas en la trama del ax.patchesmiembro. Luego puede utilizar la técnica demostrada en este matplotlibejemplo de galería para agregar las etiquetas usando el ax.textmé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:

ingrese la descripción de la imagen aquí

Simon Gibbons avatar Mar 08 '2015 20:03 Simon Gibbons

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 pointspara 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:

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

Y con la 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

justfortherec avatar Jan 21 '2018 23:01 justfortherec

A partir dematplotlib v3.4.0

  • Usarmatplotlib.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'
    • kwargsSe pasan adicionales a Axes.annotate, que acepta . Text kwargs
      • Se pueden utilizar propiedades como color, rotation, fontsize, etc.
  • 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.containerses un listdeBarContainer 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 ellist
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 fmtpará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, el fmtargumento de bar_labelahora 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')con np.where.
    • Si itera a través de varios contenedores, en el caso de barras agrupadas o apiladas, utilice cen lugar de ax.containers[0], confor c in ax.containers:
  • Para cambios extensos en las etiquetas de las barras, aún puede ser mejor usar el labelspará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]]y ax.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í .
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)

ingrese la descripción de la imagen aquí

  • Especifique adicional kwargspara una personalización adicional
    • Acepta parámetros dematplotlib.axes.Axes.text
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 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)

ingrese la descripción de la imagen aquí

Trama a nivel de figura de Seaborn

  • seaborn.catplotacepta un marco de datos para data.
  • Dado que .catplotes 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)

ingrese la descripción de la imagen aquí

matplotlib.axes.Axes.bar

  • Será similar si solo usasmatplotlib.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)

ingrese la descripción de la imagen aquí

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
Trenton McKinney avatar May 16 '2021 22:05 Trenton McKinney

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

diagrama de barras horizontales con anotaciones

oleson avatar Jul 18 '2018 20:07 oleson