Thêm nhãn giá trị trên biểu đồ thanh matplotlib


95

Tôi bị mắc kẹt vào một cái gì đó mà tôi cảm thấy tương đối dễ dàng. Đoạn mã tôi mang đến dưới đây là một mẫu dựa trên một dự án lớn hơn mà tôi đang thực hiện. Tôi thấy không có lý do gì để đăng tất cả các chi tiết, vì vậy hãy chấp nhận cấu trúc dữ liệu mà tôi mang theo.

Về cơ bản, tôi đang tạo một biểu đồ thanh và tôi chỉ có thể tìm ra cách thêm nhãn giá trị trên các thanh (ở giữa thanh hoặc ngay trên nó). Tôi đã xem xét các mẫu trên web nhưng không triển khai thành công trên mã của riêng tôi. Tôi tin rằng giải pháp là với 'văn bản' hoặc 'chú thích', nhưng tôi: a) không biết nên sử dụng cái nào (và nói chung là chưa tìm ra khi nào nên dùng cái nào). b) không thể nhìn thấy để hiển thị các nhãn giá trị. Đánh giá cao sự giúp đỡ của bạn, mã của tôi dưới đây. Cảm ơn trước!

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
Matplotlib có một bản demo: matplotlib.org/examples/api/barchart_demo.html
Dan

Câu trả lời:


119

Đầu tiên freq_series.plottrả về một trục không phải là một hình, vì vậy để làm cho câu trả lời của tôi rõ ràng hơn một chút, tôi đã thay đổi mã đã cho của bạn để tham chiếu đến nó axthay vì figphù hợp hơn với các ví dụ mã khác.

Bạn có thể lấy danh sách các thanh được sản xuất trong cốt truyện từ ax.patchesthành viên. Sau đó, bạn có thể sử dụng kỹ thuật được trình bày trong ví dụ thư viện nàymatplotlib để thêm các nhãn bằng ax.textphương pháp này.

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

Điều này tạo ra một âm mưu được gắn nhãn trông giống như:

nhập mô tả hình ảnh ở đây


Chào Simon! Đầu tiên, cảm ơn rất nhiều vì đã trả lời! Thứ hai, tôi đoán là tôi không rõ - tôi muốn hiển thị giá trị y. Tôi vừa thay thế các nhãn trong zip (,) bằng các tần số. Bây giờ, bạn có thể vui lòng làm sáng tỏ thêm về cái rìu của Fig Vs không? Làm tôi bối rối. Một cụm từ / tài nguyên tìm kiếm tốt cũng sẽ rất tốt, vì nó hơi chung chung cho một tìm kiếm trên goog. Nhiều đánh giá cao!
Optimesh

Một hình là tập hợp của một hoặc nhiều trục, ví dụ như trong ví dụ này matplotlib.org/examples/stosystem/… nó là một hình được tạo thành từ 4 trục khác nhau.
Simon Gibbons

Cảm ơn một lần nữa. Bạn có thể vui lòng giúp tôi hiểu sự khác biệt giữa chú thích và văn bản không? Cảm ơn!
Optimesh

2
Cả hai đều có thể được sử dụng để thêm văn bản vào một cốt truyện. textchỉ cần in một số văn bản lên cốt truyện, trong khi đó annotatelà một trình trợ giúp mà bạn có thể sử dụng để dễ dàng thêm một mũi tên từ văn bản trỏ đến một điểm cụ thể trên cốt truyện được văn bản đề cập đến.
Simon Gibbons

10
Giải pháp tốt. Tôi đã viết một bài đăng trên blog được xây dựng dựa trên giải pháp ở đây và cung cấp một phiên bản mạnh mẽ hơn một chút có tỷ lệ theo chiều cao của trục, do đó, cùng một mã hoạt động cho các ô khác nhau có chiều cao trục khác nhau: composit.al/blog/2015/ 29/11 /…
Lindsey Kuper

65

Dựa trên một tính năng được đề cập trong câu trả lời này cho một câu hỏi khác, tôi đã tìm thấy một giải pháp áp dụng rất phổ biến để đặt nhãn trên biểu đồ thanh.

Rất tiếc, các giải pháp khác không hoạt động trong nhiều trường hợp, vì khoảng cách giữa nhãn và thanh được tính theo đơn vị tuyệt đối của các thanh hoặc được chia tỷ lệ theo chiều cao của thanh . Cái trước chỉ hoạt động cho một phạm vi giá trị hẹp và cái sau cho khoảng cách không nhất quán trong một ô. Cả hai đều không hoạt động tốt với các trục logarit.

Giải pháp mà tôi đề xuất hoạt động độc lập với tỷ lệ (tức là đối với số lượng nhỏ và lớn) và thậm chí đặt đúng nhãn cho các giá trị âm và với thang đo logarit vì nó sử dụng đơn vị trực quan pointscho các hiệu số.

Tôi đã thêm một số âm để giới thiệu vị trí chính xác của các nhãn trong trường hợp như vậy.

Giá trị chiều cao của mỗi thanh được sử dụng làm nhãn cho nó. Các nhãn khác có thể dễ dàng được sử dụng với đoạn mã của Simonfor 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")

Chỉnh sửa: Tôi đã trích xuất chức năng có liên quan trong một chức năng, như được đề xuất bởibarhillec .

Điều này tạo ra kết quả sau:

Biểu đồ thanh với các nhãn được đặt tự động trên mỗi thanh

Và với thang đo logarit (và một số điều chỉnh đối với dữ liệu đầu vào để hiển thị tỉ lệ logarit), đây là kết quả:

Biểu đồ thanh với thang đo logarit với các nhãn được đặt tự động trên mỗi thanh


1
Câu trả lời tuyệt vời! Cảm ơn. Điều này hoạt động hoàn hảo với gấu trúc trong âm mưu thanh được xây dựng.
m4p85r

1
Đề xuất cải tiến: sử dụng ax.annotate thay vì plt.annotate. Thay đổi này sẽ cho phép toàn bộ quy trình được gói gọn trong một hàm được thông qua một trục trục, sau đó có thể được tính toán thành một hàm tiện ích biểu đồ độc lập hữu ích.
barnhillec

@barnhillec, cảm ơn vì đề xuất. Tôi đã làm chính xác điều đó trong bản chỉnh sửa của mình. Lưu ý rằng điều này hiện chỉ hoạt động với biểu đồ thanh dọc và không hoạt động với bất kỳ loại biểu đồ nào khác (có thể với biểu đồ). Làm cho hàm chung chung hơn cũng sẽ làm cho nó khó hiểu hơn và do đó ít phù hợp hơn cho một câu trả lời ở đây.
justfortherec

Câu trả lời rất mạnh mẽ hơn những người khác mà tôi đã tìm thấy. Giải thích độc đáo từng dòng bằng nhận xét giúp tôi đồng hóa toàn bộ khái niệm.
code_conundrum,

34

Dựa trên câu trả lời (tuyệt vời!) Ở trên, chúng tôi cũng có thể tạo biểu đồ thanh ngang chỉ với một vài điều chỉnh:

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

biểu đồ thanh ngang có chú thích


1
Để lưới hiển thị:freq_series.plot(kind='barh', grid=True)
sinapan,

Hoạt động hoàn hảo ngay cả với biểu đồ thanh Nhóm. cảm ơn.
Prabah

Hoàn thành xuất sắc với biểu đồ thanh ngang!
code_conundrum,

Đối với tôi, các con số đang giao nhau với hộp bao quanh biểu đồ thanh. Có cách nào để ngăn chặn điều này?
bweber 13

Đã giải quyết vấn đề của riêng tôi bằng cách sử dụngax.set_xlim([0, 1.1*max_value])
bweber13

14

Nếu bạn chỉ muốn gắn nhãn các điểm dữ liệu phía trên thanh, bạn có thể sử dụng plt.annotate ()

Mã của tôi:

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

Bằng cách chỉ định căn chỉnh theo chiều ngang và dọc của 'center''bottom'tương ứng, người ta có thể nhận được các chú thích ở giữa.

biểu đồ thanh có nhãn


1
sạch sẽ và đơn giản
Ethan Yanjia Li

Bạn có thể thêm cách chúng tôi có thể đặt nhãn ở chính giữa không?
x89

@ x89 Bạn có thể chỉ định căn chỉnh ngang và dọc của văn bản để căn giữa. - Tôi đã chỉnh sửa câu trả lời để cải thiện câu trả lời đó.
Simon Gibbons

0

Nếu bạn chỉ muốn thêm Datapoints phía trên các thanh, bạn có thể dễ dàng làm điều đó với:

 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.