Подтвердить что ты не робот

Добавление меток значений на гистограмме matplotlib

Я застрял на чем-то, что кажется, должно быть относительно легко. Код, который я привожу ниже, является примером, основанным на более крупном проекте, над которым я работаю. Я не видел причин публиковать все детали, поэтому, пожалуйста, примите структуры данных, которые я привел как есть.

По сути, я создаю гистограмму и просто могу понять, как добавить метки значений на столбцы (в центре столбца или чуть выше него). Я просматривал примеры в Интернете, но безуспешно реализовывал свой собственный код. Я полагаю, что решение заключается либо в "тексте", либо в "аннотации", но я: а) не знаю, какой использовать (и вообще говоря, не определился, когда и какой использовать). б) не вижу, чтобы получить либо представить метки значения. Буду признателен за вашу помощь, мой код ниже. Заранее спасибо!

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)
4b9b3361

Ответ 1

Во-первых, freq_series.plot возвращает ось, а не цифру, поэтому, чтобы сделать мой ответ немного более понятным, я изменил ваш код так, чтобы он ax как ax а не fig чтобы быть более совместимым с другими примерами кода.

Вы можете получить список слитков, созданных на графике, от члена ax.patches. Затем вы можете использовать технику, продемонстрированную в этом matplotlib галереи matplotlib чтобы добавить метки с ax.text метода 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')

Это создает помеченный график, который выглядит следующим образом:

enter image description here

Ответ 2

Основываясь на функции, упомянутой в этом ответе на другой вопрос, я нашел очень общее решение для размещения меток на гистограмме.

Другие решения, к сожалению, не работают во многих случаях, потому что расстояние между меткой и полосой либо задается в абсолютных единицах полос, либо масштабируется по высоте полосы. Первый работает только для узкого диапазона значений, а второй дает непоследовательный интервал в пределах одного графика. Ни один из них не работает хорошо с логарифмическими осями.

Решение, которое я предлагаю работу независимо от масштаба (т.е. для малых и больших чисел) и даже правильно ставлю метки для отрицательных значений и логарифмическими масштабы, поскольку он использует визуальные единицы 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.

Это дает следующий вывод:

Bar chart with automatically placed labels on each bar

И с логарифмическим масштабом (и некоторой корректировкой входных данных для демонстрации логарифмического масштабирования), это результат:

Bar chart with logarithmic scale with automatically placed labels on each bar

Ответ 3

Основываясь на вышеприведенном (великолепном!) Ответе, мы также можем сделать горизонтальную линейную диаграмму с помощью нескольких настроек:

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

horizontal bar plot with annotations

Ответ 4

Если вы хотите добавить только точки данных над столбцами, вы можете легко сделать это с помощью:

 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