Python matplotlib nhiều thanh


92

Cách vẽ nhiều thanh trong matplotlib, khi tôi cố gắng gọi hàm thanh nhiều lần, chúng chồng lên nhau và như hình bên dưới chỉ có thể nhìn thấy giá trị cao nhất màu đỏ. Làm cách nào để vẽ nhiều thanh có ngày tháng trên trục x?

Cho đến nay, tôi đã thử điều này:

import matplotlib.pyplot as plt
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x, y, width=0.5, color='b', align='center')
ax.bar(x, z, width=0.5, color='g', align='center')
ax.bar(x, k, width=0.5, color='r', align='center')
ax.xaxis_date()

plt.show()

Tôi hiểu rồi:

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

Kết quả sẽ giống như vậy, nhưng với ngày tháng nằm trên trục x và các thanh nằm cạnh nhau:

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


bạn cần thay đổi các giá trị x
jterrace

2
Ý anh là gì ? Giá trị X là ngày ...
John Smith

4
tại sao điều này không được hỗ trợ đơn giản bởi matplotlib ?!
ihadanny

Câu trả lời:


115
import matplotlib.pyplot as plt
from matplotlib.dates import date2num
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
x = date2num(x)

y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x-0.2, y, width=0.2, color='b', align='center')
ax.bar(x, z, width=0.2, color='g', align='center')
ax.bar(x+0.2, k, width=0.2, color='r', align='center')
ax.xaxis_date()

plt.show()

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

Tôi không biết "các giá trị y cũng chồng chéo" có nghĩa là gì, đoạn mã sau có giải quyết được vấn đề của bạn không?

ax = plt.subplot(111)
w = 0.3
ax.bar(x-w, y, width=w, color='b', align='center')
ax.bar(x, z, width=w, color='g', align='center')
ax.bar(x+w, k, width=w, color='r', align='center')
ax.xaxis_date()
ax.autoscale(tight=True)

plt.show()

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


Cảm ơn, nhưng nếu tôi có 3 vạch, nó có vẻ tốt. Khi tôi thử 40 vạch, nó rối tung lên. Bạn có thể vui lòng cập nhật giải pháp của mình để có thể mở rộng hơn không?
John Smith

Định nghĩa "lộn xộn"? Các nhãn X chồng chéo có thể được khắc phục bằng cách sử dụng autofmt_xdate(), tự động xoay các nhãn.
John Lyon

Vấn đề không phải là các nhãn X chồng lên nhau, vấn đề là các giá trị y cũng trùng nhau. Làm thế nào để sửa chữa nó?
John Smith

và width = 0,2 cũng quá nhỏ đối với khoảng thời gian lớn. Nếu tôi sử dụng các giá trị lớn hơn, tôi không nhận được kết quả tương tự.
John Smith

Một điều nữa là, các khoảng trắng ở đầu và cuối. Làm thế nào để loại bỏ khoảng trống và bắt đầu trực tiếp ngày đầu tiên và tương tự kết thúc ngày cuối cùng mà không có khoảng trống hoặc ít khoảng trống.
John Smith

61

Vấn đề với việc sử dụng ngày làm giá trị x, là nếu bạn muốn có một biểu đồ thanh như trong hình thứ hai của mình, chúng sẽ sai. Bạn nên sử dụng biểu đồ thanh xếp chồng lên nhau (các màu chồng lên nhau) hoặc nhóm theo ngày (ngày "giả" trên trục x, về cơ bản chỉ nhóm các điểm dữ liệu).

import numpy as np
import matplotlib.pyplot as plt

N = 3
ind = np.arange(N)  # the x locations for the groups
width = 0.27       # the width of the bars

fig = plt.figure()
ax = fig.add_subplot(111)

yvals = [4, 9, 2]
rects1 = ax.bar(ind, yvals, width, color='r')
zvals = [1,2,3]
rects2 = ax.bar(ind+width, zvals, width, color='g')
kvals = [11,12,13]
rects3 = ax.bar(ind+width*2, kvals, width, color='b')

ax.set_ylabel('Scores')
ax.set_xticks(ind+width)
ax.set_xticklabels( ('2011-Jan-4', '2011-Jan-5', '2011-Jan-6') )
ax.legend( (rects1[0], rects2[0], rects3[0]), ('y', 'z', 'k') )

def autolabel(rects):
    for rect in rects:
        h = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*h, '%d'%int(h),
                ha='center', va='bottom')

autolabel(rects1)
autolabel(rects2)
autolabel(rects3)

plt.show()

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


nếu tôi muốn hiển thị 100 ngày như vậy trên trục x, làm thế nào để bạn phù hợp với chúng?
John Smith

1
Bạn có thể dễ dàng tạo ra số ngày cần với NumPy của datetime64: ví dụ một tháng có giá trị: np.arange('2012-02', '2012-03', dtype='datetime64[D]'). Bạn có thể cần phải suy nghĩ kỹ hơn về cách tốt nhất để biểu diễn dữ liệu này nếu bạn có 40 bộ dữ liệu (theo một nhận xét khác) kéo dài hơn 100 ngày.
John Lyon

Ngoài ra, sử dụng ax.xaxis_date () là rất thuận lợi, vì nó khớp với ngày của bạn theo trục x.
John Smith

3
Tại sao bạn không đi trước? Tôi đang cố giúp bạn học chứ không phải viết mã cho bạn. Tôi chắc rằng bạn có thể làm điều đó xaxis_datenhưng bạn sẽ cần phải điều chỉnh những gì tôi đã viết để bù đắp các giá trị ngày của bạn (ví dụ: theo số giờ sử dụng timedelta) cho mỗi chuỗi để ngăn chúng chồng chéo. Câu trả lời khác chỉ thực hiện điều này, nhưng bạn có thể cần phải kết hợp với các nhãn sau đó.
John Lyon

được, nhưng khi tôi chạy np.arange ('2012-02', '2012-03, dtype =' datetime64 [D] '), tôi nhận được điều này: (các) loại toán hạng không được hỗ trợ cho -:' str 'và' str '
John Smith

25

Tôi biết rằng điều này là về matplotlib, nhưng sử dụng pandasseaborncó thể giúp bạn tiết kiệm rất nhiều thời gian:

df = pd.DataFrame(zip(x*3, ["y"]*3+["z"]*3+["k"]*3, y+z+k), columns=["time", "kind", "data"])
plt.figure(figsize=(10, 6))
sns.barplot(x="time", hue="kind", y="data", data=df)
plt.show()

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


Câu trả lời tuyệt vời nhưng nó hơi không đầy đủ về trục x. Bạn có thể làm cho nó đẹp hơn không?
Spinor 8

Bạn có thể, tôi đoán, cũng làm mình với gấu trúc và matplotlib
Vicki B

18

sau khi tìm kiếm một giải pháp tương tự và không tìm thấy bất cứ điều gì đủ linh hoạt, tôi quyết định viết hàm của riêng mình cho nó. Nó cho phép bạn có bao nhiêu thanh cho mỗi nhóm tùy thích và chỉ định cả chiều rộng của một nhóm cũng như chiều rộng riêng lẻ của các thanh trong nhóm.

Thưởng thức:

from matplotlib import pyplot as plt


def bar_plot(ax, data, colors=None, total_width=0.8, single_width=1, legend=True):
    """Draws a bar plot with multiple bars per data point.

    Parameters
    ----------
    ax : matplotlib.pyplot.axis
        The axis we want to draw our plot on.

    data: dictionary
        A dictionary containing the data we want to plot. Keys are the names of the
        data, the items is a list of the values.

        Example:
        data = {
            "x":[1,2,3],
            "y":[1,2,3],
            "z":[1,2,3],
        }

    colors : array-like, optional
        A list of colors which are used for the bars. If None, the colors
        will be the standard matplotlib color cyle. (default: None)

    total_width : float, optional, default: 0.8
        The width of a bar group. 0.8 means that 80% of the x-axis is covered
        by bars and 20% will be spaces between the bars.

    single_width: float, optional, default: 1
        The relative width of a single bar within a group. 1 means the bars
        will touch eachother within a group, values less than 1 will make
        these bars thinner.

    legend: bool, optional, default: True
        If this is set to true, a legend will be added to the axis.
    """

    # Check if colors where provided, otherwhise use the default color cycle
    if colors is None:
        colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Number of bars per group
    n_bars = len(data)

    # The width of a single bar
    bar_width = total_width / n_bars

    # List containing handles for the drawn bars, used for the legend
    bars = []

    # Iterate over all data
    for i, (name, values) in enumerate(data.items()):
        # The offset in x direction of that bar
        x_offset = (i - n_bars / 2) * bar_width + bar_width / 2

        # Draw a bar for every value of that type
        for x, y in enumerate(values):
            bar = ax.bar(x + x_offset, y, width=bar_width * single_width, color=colors[i % len(colors)])

        # Add a handle to the last drawn bar, which we'll need for the legend
        bars.append(bar[0])

    # Draw legend if we need
    if legend:
        ax.legend(bars, data.keys())


if __name__ == "__main__":
    # Usage example:
    data = {
        "a": [1, 2, 3, 2, 1],
        "b": [2, 3, 4, 3, 1],
        "c": [3, 2, 1, 4, 2],
        "d": [5, 9, 2, 1, 8],
        "e": [1, 3, 2, 2, 3],
        "f": [4, 3, 1, 1, 4],
    }

    fig, ax = plt.subplots()
    bar_plot(ax, data, total_width=.8, single_width=.9)
    plt.show()

Đầu ra:

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


Làm cách nào chúng ta có thể sửa đổi điều này để thêm nhãn vào trục x? Như trong mỗi nhóm thanh?
x89

thay đổi xtickscốt truyện, ví dụplt.xticks(range(5), ["one", "two", "three", "four", "five"])
pascscha

chức năng tốt đẹp, rất hữu ích, cảm ơn. Điều duy nhất tôi thay đổi là tôi nghĩ rằng chú giải sẽ dễ dàng hơn nếu bạn chỉ cần đặt label = data.keys [i] trong lệnh gọi barplot và sau đó bạn không cần phải xây dựng danh sách thanh.
Adrian Tompkins

0

Tôi đã thực hiện giải pháp này: nếu bạn muốn vẽ nhiều hơn một ô trong một hình, hãy đảm bảo trước khi vẽ các ô tiếp theo, bạn đã đặt quyền matplotlib.pyplot.hold(True) để có thể thêm các ô khác.

Liên quan đến các giá trị ngày giờ trên trục X, một giải pháp sử dụng sự liên kết của các thanh phù hợp với tôi. Khi bạn tạo một biểu đồ thanh khác matplotlib.pyplot.bar(), chỉ cần sử dụng align='edge|center'và đặt width='+|-distance'.

Khi bạn đặt tất cả các thanh (ô) bên phải, bạn sẽ thấy các thanh tốt.


Có vẻ như nó matplotlib.pyplot.holdđã không được dùng nữa kể từ phiên bản 2.0, như đã đề cập trong tài liệu
kỹ
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.