python matplotlib多个栏


92

如何在matplotlib中绘制多个条形图,当我尝试多次调用bar函数时,它们重叠了,如下图所示,只能看到最高的红色。如何在X轴上绘制带有日期的多个条形图?

到目前为止,我已经尝试过了:

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

我懂了:

在此处输入图片说明

结果应该是类似的,但日期在x轴上,条形彼此相邻:

在此处输入图片说明


您需要更改x值
jterrace 2013年

2
你什么意思 ?X值是日期...
John Smith

4
为什么matplotlib不能简单地支持它?
ihadanny

Answers:


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

在此处输入图片说明

我不知道“ y值也重叠”是什么意思,以下代码可以解决您的问题吗?

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

在此处输入图片说明


谢谢,但是如果我有3个酒吧,它看起来不错。当我尝试40根酒吧时,它就弄糟了。您能否将您的解决方案更新为更具可扩展性?
约翰·史密斯

定义“混乱”?可以使用来固定X标签重叠,该标签会autofmt_xdate()自动旋转标签。
约翰·里昂

问题不在于X标签重叠,而在于y值也重叠。如何解决?
约翰·史密斯

并且width = 0,2对于较大的时间跨度来说太小。如果使用较大的值,则不会得到相同的结果。
约翰·史密斯

另一件事是,开头和结尾的空格。如何消除空格并直接开始第一个日期,然后类似地结束最后一个日期,而没有任何空格或更少的空格。
约翰·史密斯

61

使用日期作为x值的麻烦在于,如果您想要第二张图片所示的条形图,则它们将是错误的。您应该使用堆叠的条形图(彼此顶部的颜色)或按日期分组(x轴上的“假”日期,基本上只是对数据点进行分组)。

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

在此处输入图片说明


如果我想在x轴上显示100天,您如何适应它们?
约翰·史密斯

1
您可以使用numpy轻松生成所需的日期datetime64:例如,一个月的价值:np.arange('2012-02', '2012-03', dtype='datetime64[D]')。如果您有40个跨100天的数据集(根据另一条评论),则可能需要更加认真地考虑表示此数据的最佳方法。
约翰·里昂

而且,使用ax.xaxis_date()非常有优势,因为它使您的日期适合x轴。
约翰·史密斯

3
你为什么不先去呢?我正在尝试帮助您学习,而不是为您编写代码。我敢肯定您可以使用它,xaxis_date但是您需要调整我编写的内容以抵消timedelta每个系列的日期值(例如,使用乘以小时数),以防止它们重叠。另一个答案就是这样做的,但是之后您可能需要弄乱标签。
约翰·里昂

好的,但是当我运行np.arange('2012-02','2012-03,dtype ='datetime64 [D]')时,我得到的是:-不受支持的操作数类型:'str'和' str”
约翰·史密斯,

25

我知道这是关于matplotlib,但是使用pandasseaborn可以节省很多时间:

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

在此处输入图片说明


很好的答案,但是由于x轴的原因,它有些不完整。你能使它更具表现力吗?
Spinor8

你可以,我想,也这样做了他与熊猫和matplotlib
薇薇乙

18

在寻找类似的解决方案并且找不到足够灵活的方法之后,我决定为其编写自己的函数。它使您可以根据需要在每个组中拥有多个条,并指定组的宽度以及组中条的单个宽度。

请享用:

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

输出:

在此处输入图片说明


我们如何修改它以在x轴上添加标签?至于每组酒吧?
x89,

更改xticks剧情,例如plt.xticks(range(5), ["one", "two", "three", "four", "five"])
pascscha

功能不错,非常有帮助,谢谢。我唯一更改的是,我认为,如果仅在barplot调用中放置label = data.keys [i],然后就不需要构建条形列表,则图例会更容易。
Adrian Tompkins

0

我做了这个解决方案:如果您想在一个图形中绘制多个图形,请确保在绘制下一个图形之前,您已经设置 matplotlib.pyplot.hold(True) 了可以添加其他图形的权限。

关于X轴上的日期时间值,使用条形对齐的解决方案对我有用。当您使用创建另一个条形图时matplotlib.pyplot.bar(),只需使用align='edge|center'并设置width='+|-distance'

当您正确设置所有条形图时,您会发现条形图很好。


matplotlib.pyplot.hold正如文档中提到的那样,
Engineervix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.