在matplotlib条形图上添加值标签

时间:2022-05-11 15:21:35

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!

基本上,我正在创建一个条形图,我只需要知道如何在条形图上添加值标签(在条形图的中间,或者仅仅在它上面)。我一直在网上查看示例,但在我自己的代码上没有成功实现。我认为解决方案要么是使用“text”,要么是“annotate”,但I: 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)

2 个解决方案

#1


73  

Firstly freq_series.plot returns an axis not a figure so to make my answer a little more clear I've changed your given code to refer to it as ax rather than fig to 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.patches member. Then you can use the technique demonstrated in this matplotlib gallery example to add the labels using the ax.text method.

你可以从斧子中得到情节中产生的条形图。补丁成员。然后,您可以使用matplotlib示例中演示的技术,使用ax添加标签。文本的方法。

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:

这产生了一个标记的图,看起来像:

在matplotlib条形图上添加值标签

#2


14  

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

根据这个问题的回答中提到的一个特性,我找到了一个在条形图上放置标签的非常通用的解决方案。

Other solutions unfortunately do not work in many cases, because the spacing between label and bar is either given in absolute units of the bars or 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 points for offsets.

我提出的解决方案是独立于尺度的(例如,小和大的数字),甚至正确地为负数和对数尺度放置标签,因为它使用视觉单位点进行偏移。

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的用于rect,标签在zip(rects,标签)片段。

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

# For each bar: Place a label
for rect in rects:
    # 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 = 5
    # 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
    plt.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.

plt.savefig("image.png")

This produces the following output:

这将产生以下输出:

在matplotlib条形图上添加值标签

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

通过对数尺度(以及对输入数据进行一些调整以显示对数尺度),这就是结果:

在matplotlib条形图上添加值标签

#1


73  

Firstly freq_series.plot returns an axis not a figure so to make my answer a little more clear I've changed your given code to refer to it as ax rather than fig to 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.patches member. Then you can use the technique demonstrated in this matplotlib gallery example to add the labels using the ax.text method.

你可以从斧子中得到情节中产生的条形图。补丁成员。然后,您可以使用matplotlib示例中演示的技术,使用ax添加标签。文本的方法。

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:

这产生了一个标记的图,看起来像:

在matplotlib条形图上添加值标签

#2


14  

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

根据这个问题的回答中提到的一个特性,我找到了一个在条形图上放置标签的非常通用的解决方案。

Other solutions unfortunately do not work in many cases, because the spacing between label and bar is either given in absolute units of the bars or 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 points for offsets.

我提出的解决方案是独立于尺度的(例如,小和大的数字),甚至正确地为负数和对数尺度放置标签,因为它使用视觉单位点进行偏移。

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的用于rect,标签在zip(rects,标签)片段。

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

# For each bar: Place a label
for rect in rects:
    # 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 = 5
    # 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
    plt.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.

plt.savefig("image.png")

This produces the following output:

这将产生以下输出:

在matplotlib条形图上添加值标签

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

通过对数尺度(以及对输入数据进行一些调整以显示对数尺度),这就是结果:

在matplotlib条形图上添加值标签