为分类数据绘制多个条形图

13

我要在matplotlib中找到一种方法来绘制每个值的多个条形图。对于数字数据,可以通过向X数据添加偏移量来实现,例如此处所述:

import numpy as np
import matplotlib.pyplot as plt

X = np.array([1,3,5])
Y = [1,2,3]
Z = [2,3,4]

plt.bar(X - 0.4, Y) # offset of -0.4
plt.bar(X + 0.4, Z) # offset of  0.4
plt.show()

数字数据的多个条形图

plt.bar()(以及 ax.bar())也会自动处理分类数据:

X = ['A','B','C']
Y = [1,2,3]

plt.bar(X, Y)
plt.show()

类别处理

这里,由于类别不直接与坐标轴上的值相关联,因此显然无法添加偏移量。我可以手动为每个类别分配数值并使用plt.xticks()在x轴上设置标签:

X = ['A','B','C']
Y = [1,2,3]
Z = [2,3,4]
_X = np.arange(len(X))

plt.bar(_X - 0.2, Y, 0.4)
plt.bar(_X + 0.2, Z, 0.4)
plt.xticks(_X, X) # set labels manually
plt.show()

手动设置类别标签

然而,我想知道是否有更优雅的方法可以利用bar() 的自动分类处理,特别是在不事先知道类别和每个类别中的条形数量的情况下(这会导致调整条形宽度以避免重叠)。

1个回答

31

在matplotlib中没有自动支持子类别。

使用matplotlib放置柱状图

您可以按照问题中提出的方式,以数字方式放置柱状图。当然,您也可以让代码管理未知数量的子类别。

import numpy as np
import matplotlib.pyplot as plt

X = ['A','B','C']
Y = [1,2,3]
Z = [2,3,4]

def subcategorybar(X, vals, width=0.8):
    n = len(vals)
    _X = np.arange(len(X))
    for i in range(n):
        plt.bar(_X - width/2. + i/float(n)*width, vals[i], 
                width=width/float(n), align="edge")   
    plt.xticks(_X, X)
    
subcategorybar(X, [Y,Z,Y])

plt.show()

enter image description here

使用 Pandas

你也可以使用 Pandas 的绘图包装器,它会自动计算子类别的数量。它将为数据帧中的每一列绘制一个组。

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

X = ['A','B','C']
Y = [1,2,3]
Z = [2,3,4]

df = pd.DataFrame(np.c_[Y,Z,Y], index=X)
df.plot.bar()

plt.show()

输入图片描述


感谢您的回答和代码示例。我知道pandas可以处理子类别(实际上,我的数据已经存储在dataframe中),但为了与其他图表保持一致,我正在寻找使用matplotlib的解决方案。 - tsabsch
3
要在X轴上旋转“A”、“B”、“C”,请使用df.plot.bar(rot=0)。 - Yair Daon
当使用pandas时,上述答案也可以简化为一行代码:df.set_index(X).plot.bar();(这样就不会丢失条形图的分类名称)。 - Kevad
@Kevad 你的评论有点令人困惑,因为 X 已经是索引了,所以不需要再次设置它。 - ImportanceOfBeingErnest
@ImportanceOfBeingErnest 感谢您的纠正。我的意思是 df.plot.bar(); 也会产生相同的输出 :) - Kevad
@Kevad 是的,df.plot.bar() 就是这个答案提出的方法。我有什么遗漏吗?如果没有,我们可能可以删除所有这些评论? - ImportanceOfBeingErnest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接