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 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 de ella). He estado buscando ejemplos en la web, pero no he podido implementarlo 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 general, no he descubierto cuándo usar cuál). b) no puedo ver para que ninguno de los dos presente 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.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))
fig = freq_series.plot(kind='bar')
fig.set_title('Amount Frequency')
fig.set_xlabel('Amount ($)')
fig.set_ylabel('Frequency')
fig.set_xticklabels(x_labels)
fuente
Respuestas:
En primer lugar,
freq_series.plot
devuelve un eje, no una figura, por lo que para que mi respuesta sea un poco más clara, he cambiado el código dado para referirme a él enax
lugar defig
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 usar la técnica demostrada en estematplotlib
ejemplo de galería para agregar las etiquetas usando elax.text
método.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) rects = ax.patches # Make some labels. labels = ["label%d" % i for i in xrange(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')
Esto produce una trama etiquetada que se parece a:
fuente
text
simplemente imprime algo de texto en la trama, mientras queannotate
es un ayudante que puede usar para agregar fácilmente también una flecha desde el texto que apunta a un punto específico en la trama al que se refiere el texto.Basado en una característica mencionada en esta respuesta a otra pregunta , he encontrado 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 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 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 pequeños y grandes) e incluso coloca correctamente etiquetas para valores negativos y con escalas logarítmicas porque usa la unidad visual
points
para 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. Se pueden usar fácilmente otras etiquetas con el
for rect, label in zip(rects, labels)
fragmento de 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")
Editar: he extraído la funcionalidad relevante en una función, como sugirió barnhillec .
Esto produce la siguiente salida:
Y con la escala logarítmica (y algunos ajustes a los datos de entrada para mostrar la escala logarítmica), este es el resultado:
fuente
Partiendo de la respuesta anterior (¡genial!), También podemos hacer un diagrama 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")
fuente
freq_series.plot(kind='barh', grid=True)
ax.set_xlim([0, 1.1*max_value])
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, se pueden obtener anotaciones centradas.fuente
Si solo desea agregar puntos de datos encima de 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
fuente