Я застрял на чем-то, что должно быть относительно легким. Код, который я привожу ниже, представляет собой образец, основанный на более крупном проекте, над которым я работаю. Я не видел причин публиковать все подробности, поэтому, пожалуйста, примите структуры данных, которые я привожу, как есть.
По сути, я создаю гистограмму и просто могу понять, как добавлять метки значений на столбцы (в центре или чуть выше). Я просматривал образцы в Интернете, но безуспешно работал с моим собственным кодом. Я считаю, что решение - либо с «текстом», либо с «аннотацией», но я: а) не знаю, какой из них использовать (и, вообще говоря, не понял, когда какой использовать). б) не вижу возможности представить метки значений. Был бы признателен за вашу помощь, мой код ниже. Заранее спасибо!
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)
Ответы:
Во-первых,
freq_series.plot
возвращает ось, а не фигуру, поэтому, чтобы мой ответ был немного более ясным, я изменил ваш код, чтобы ссылаться на него,ax
а неfig
для большей согласованности с другими примерами кода.Вы можете получить список стержней, созданных на графике, от
ax.patches
члена. Затем вы можете использовать технику, продемонстрированную в этомmatplotlib
примере галереи, чтобы добавить метки с помощью этого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')
Это дает помеченный график, который выглядит так:
источник
text
просто печатает текст на графике, аannotate
это помощник, который вы можете использовать, чтобы легко также добавить стрелку из текста, указывающую на конкретную точку на графике, на которую ссылается текст.Основываясь на функции, упомянутой в этом ответе на другой вопрос, я нашел очень универсальное решение для размещения меток на гистограмме.
Другие решения, к сожалению, не работают во многих случаях, потому что расстояние между меткой и полосой либо задается в абсолютных единицах полосок, либо масштабируется по высоте полосы . Первый работает только для узкого диапазона значений, а второй дает несогласованные интервалы в пределах одного графика. Ни то, ни другое не работает с логарифмическими осями.
Предлагаемое мной решение работает независимо от масштаба (то есть для малых и больших чисел) и даже правильно размещает метки для отрицательных значений и с логарифмическими масштабами, поскольку оно использует визуальную единицу
points
для смещений.Я добавил отрицательное число, чтобы продемонстрировать правильное размещение этикеток в таком случае.
Значение высоты каждой полосы используется как метка для нее. Другие ярлыки можно легко использовать с сниппетом Саймона
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")
Изменить: я извлек соответствующие функции из функции, как это было предложено barnhillec .
Это дает следующий результат:
А с логарифмическим масштабом (и некоторой корректировкой входных данных для демонстрации логарифмического масштабирования) вот результат:
источник
Основываясь на приведенном выше (отличном!) Ответе, мы также можем создать горизонтальную полосу с несколькими настройками:
# 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")
источник
freq_series.plot(kind='barh', grid=True)
ax.set_xlim([0, 1.1*max_value])
Если вы хотите просто пометить точки данных над полосой, вы можете использовать plt.annotate ()
Мой код:
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()
Указав горизонтальное и вертикальное выравнивание
'center'
и'bottom'
соответственно можно получить центрированные аннотации.источник
Если вы хотите добавить точки данных только над полосами, вы можете легко сделать это с помощью:
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
источник