Python 在 matplotlib 条形图上添加值标签

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28931224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 03:55:01  来源:igfitidea点击:

Adding value labels on a matplotlib bar chart

pythonpython-2.7pandasmatplotlibdata-visualization

提问by Optimesh

I got stuck on something that feels like should be relatively easy. The code I bring below is a sample based on a larger project I'm working on. I saw no reason to post all the details, so please accept the data structures I bring as is.

我被困在感觉应该相对容易的事情上。我在下面带来的代码是基于我正在处理的一个更大项目的示例。我认为没有理由发布所有详细信息,因此请按原样接受我带来的数据结构。

Basically, I'm creating a bar chart, and I just can figure out how to add value labels on the bars (in the center of the bar, or just above it). Been looking at samples around the web but with no success implementing on my own code. I believe the solution is either with 'text' or 'annotate', but I: a) don't know which one to use (and generally speaking, haven't figured out when to use which). b) can't see to get either to present the value labels. Would appreciate your help, my code below. Thanks in advance!

基本上,我正在创建一个条形图,我可以弄清楚如何在条形上添加值标签(在条形的中心,或在它的正上方)。一直在网上查看示例,但在我自己的代码上没有成功实现。我相信解决方案是使用“文本”或“注释”,但我:a) 不知道该使用哪个(一般来说,还没有弄清楚何时使用哪个)。b) 看不到显示值标签。感谢您的帮助,我的代码如下。提前致谢!

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)

回答by Simon Gibbons

Firstly freq_series.plotreturns an axis nota figure so to make my answer a little more clear I've changed your given code to refer to it as axrather than figto be more consistent with other code examples.

首先freq_series.plot返回一个轴而不是一个数字,以便让我的答案更清楚一些我已经更改了您给定的代码以将其引用为ax而不是fig与其他代码示例更加一致。

You can get the list of the bars produced in the plot from the ax.patchesmember. Then you can use the technique demonstrated in this matplotlibgallery exampleto add the labels using the ax.textmethod.

您可以从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')

This produces a labeled plot that looks like:

这会生成一个标记图,如下所示:

enter image description here

在此处输入图片说明

回答by justfortherec

Based on a feature mentioned in this answer to another questionI have found a very generally applicable solution for placing labels on a bar chart.

基于this answer to another question中提到的一个功能,我找到了一个非常普遍适用的解决方案,用于在条形图上放置标签。

Other solutions unfortunately do not work in many cases, because the spacing between label and bar is either given in absolute units of the barsor is scaled by the height of the bar. The former only works for a narrow range of values and the latter gives inconsistent spacing within one plot. Neither works well with logarithmic axes.

其它解决方案很遗憾没有在许多情况下,工作,因为标签栏之间的间距或者在酒吧的绝对单位给出由条的高度成比例。前者仅适用于窄范围的值,而后者在一个图中给出了不一致的间距。两者都不适用于对数轴。

The solution I propose works independent of scale (i.e. for small and large numbers) and even correctly places labels for negative values and with logarithmic scales because it uses the visual unit pointsfor offsets.

我提出的解决方案与比例无关(即适用于小数和大数),甚至可以正确放置负值和对数刻度的标签,因为它使用视觉单位points进行偏移。

I have added a negative number to showcase the correct placement of labels in such a case.

在这种情况下,我添加了一个负数来展示标签的正确放置。

The value of the height of each bar is used as a label for it. Other labels can easily be used with Simon's for rect, label in zip(rects, labels)snippet.

每个条形的高度值用作它的标签。其他标签可以很容易地与Simon 的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")

Edit: I have extracted the relevant functionality in a function, as suggested by barnhillec.

编辑:我已经按照barnhillec 的建议提取了一个函数中的相关功能。

This produces the following output:

这会产生以下输出:

Bar chart with automatically placed labels on each bar

在每个条形上自动放置标签的条形图

And with logarithmic scale (and some adjustment to the input data to showcase logarithmic scaling), this is the result:

使用对数标度(并对输入数据进行一些调整以展示对数标度),结果如下:

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

带对数刻度的条形图,每个条形上自动放置标签

回答by oleson

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

horizontal bar plot with annotations

带注释的水平条形图

回答by user11638654

If you only want to add Datapoints above the bars, you could easily do it with:

如果您只想在条形上方添加数据点,您可以使用以下方法轻松完成:

 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

回答by Ajay

If you want to just label the data points above the bar, you could use plt.annotate()

如果只想标记条形上方的数据点,可以使用 plt.annotate()

My code:

我的代码:

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

plt.show()

output

输出

Well, text with multiple characters may be displayed slightly off-centre. But this can be overcome by slightly decreasing the x co-ordinate in the xy parameter depending on the size of the text

好吧,带有多个字符的文本可能会稍微偏离中心显示。但这可以通过根据文本大小稍微减小 xy 参数中的 x 坐标来克服