matrices graficos grafico graficas graficar coordenadas barras python python-2.7 pandas matplotlib data-visualization

graficos - matplotlib python 3



Agregar etiquetas de valor en un gráfico de barras matplotlib (4)

Me quedé atrapado 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 solo puedo descubrir cómo agregar etiquetas de valor en las barras (en el centro de la barra, o justo encima). He estado buscando 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 qué). b) no puedo ver si ninguno de los dos presenta 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)


Basado en una característica mencionada en esta respuesta a otra pregunta , he encontrado una solución muy generalmente aplicable 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 utiliza los points unidad visual para las 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 una etiqueta para ello. Se pueden usar fácilmente otras etiquetas con el fragmento for 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: he extraído 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 en los datos de entrada para mostrar la escala logarítmica), este es el resultado:


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 su código dado para referirme a él como ax lugar de fig para ser más coherente con otros ejemplos de código.

Puede obtener la lista de las barras producidas en el diagrama del miembro ax.patches . Luego puede usar la técnica demostrada en este ejemplo de la galería matplotlib para agregar las etiquetas usando el método ax.text .

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 un diagrama etiquetado que se ve así:


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


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