在matplotlib中,如何绘制多个数据集的条形图使最小的条形在前?

4 投票
3 回答
4640 浏览
提问于 2025-04-15 18:51

我想把多个数据集放在一个柱状图上,但不想让小的柱子被大的柱子遮住,也不想把它们错开。比如说,

bar(0, 1.)

bar(0, 2.)

这样只会显示第二根柱子,2.0的高度,第一根柱子就被遮住了。有没有办法让matplotlib把小的柱子画在上面?另外,我不想要堆叠柱状图,也不想在x轴上把柱子错开。

我可以把所有数据集的数据按柱子的高度排序,然后一个一个地画出这些柱子,但我更希望能单独画每根柱子,而不是一个数据集接着一个数据集地画。有没有人知道怎么做到这一点?

非常感谢!

3 个回答

1

翻译:

>>> from matplotlib import pylab
>>> data1 = [0.3, 0.9, 0.1]
>>> data2 = [3.0, 0.2, 0.5]
>>> colors = ['b','magenta','cyan']
>>> data_list = [data1,data2]
>>> num_bars = len(data_list)
>>> for i, d in enumerate(data_list):
...     for j,value in enumerate(sorted(d,reverse=True)):
...         c = colors[j]
...         obj_list = pylab.bar(i*0.4,value,width=0.8/num_bars,color=c)
... 

你可以按顺序画它们,像这样,或者调整它们的层级顺序。

编辑:

我稍微整理了一下。这基本上是说,在调用 bar 之前,先把每个柱子的数据显示从大到小排序。但你也可以稍后再去调整层级顺序等等。实际上,我会保存 bar() 返回的对象,以防你想查看它们。

import numpy as np
from pylab import *

data = [[6.7, 1.5, 4.5], [2.0, 3.25, 5.7]]
w = 0.5
xlocations =  np.array(range(len(data)))+w
colors = ['r','b','cyan']

oL = list()
for x,d in zip(xlocations, data):
    for c,value in zip(colors, sorted(d,reverse=True)):
        b = bar(x, value, width=w, color=c)
        oL.extend(b)
show()
3

这个“bar”方法会返回一个叫做matplotlib.patches.Rectangle的对象。这个对象有一个叫做set_zorder的方法。把第一个对象的zorder设置得比第二个高,就能让它显示在上面。

你可以“轻松”地通过检查它们的x坐标和根据高度来排序元素的zorder。

from matplotlib import pylab
pylab.bar([0, 1], [1.0, 2.0])
pylab.bar([0, 1], [2.0, 1.0])

# loop through all patch objects and collect ones at same x
all_patches = pylab.axes().patches
patch_at_x = {}
for patch in all_patches:
    if patch.get_x() not in patch_at_x: patch_at_x[patch.get_x()] = []
    patch_at_x[patch.get_x()].append(patch)

# custom sort function, in reverse order of height
def yHeightSort(i,j):
    if j.get_height() > i.get_height(): return 1
    else: return -1

# loop through sort assign z-order based on sort
for x_pos, patches in patch_at_x.iteritems():
    if len(patches) == 1: continue
    patches.sort(cmp=yHeightSort)
    [patch.set_zorder(patches.index(patch)) for patch in patches]

pylab.show()

这里有个图片链接 http://img697.imageshack.us/img697/8381/tmpp.png

6

我知道这个问题已经很老了,但我自己在查资料时遇到了它。因为这看起来是我会反复做的事情,所以我为 hist 函数写了一个封装器(我会用这个;对 bar 的修改应该也很简单):

from matplotlib import pyplot as mpl
from numpy import argsort, linspace

def hist_sorted(*args, **kwargs):
    all_ns = []
    all_patches = []

    labels = kwargs.pop('labels', None)
    if not labels:
        labels = ['data %d' % (i+1) for i in range(len(args))]
    elif len(labels) != len(args):
        raise ValueError('length of labels not equal to length of data')

    bins = kwargs.pop('bins', linspace(min(min(a) for a in args),
                                       max(max(a) for a in args),
                                       num = 11))

    for data, label in zip(args, labels):
        ns, bins, patches = mpl.hist(data, bins=bins, label=label, **kwargs)
        all_ns.append(ns)
        all_patches.append(patches)
    z_orders = -argsort(all_ns, axis=0)

    for zrow, patchrow in zip(z_orders, all_patches):
        assert len(zrow) == len(patchrow)
        for z_val, patch in zip(zrow, patchrow): 
            patch.set_zorder(z_val)

    return all_ns, bins, all_patches

这个封装器可以把数据集作为匿名参数传入,同时可以把任何标签作为关键字参数传入(用于图例),还可以使用 hist 函数的其他关键字参数。

撰写回答