使用 Matplotlib 提供的bar()函数来绘制柱状图。与前面介绍的 plot() 函数类似,程序每次调用 bar() 函数时都会生成一组柱状图, 如果希望生成多组柱状图,则可通过多次调用 bar() 函数来实现。

下面程序使用柱状图来展示《C语言基础》和《Java基础》两套教程历年的销量数据。

import matplotlib.pyplot as plt import numpy as np # 构建数据 x_data = ['2012', '2013', '2014', '2015', '2016', '2017', '2018'] y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000] y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700] # 绘图 plt.bar(x=x_data, height=y_data, label='C语言基础', color='steelblue', alpha=0.8) plt.bar(x=x_data, height=y_data2, label='Java基础', color='indianred', alpha=0.8) # 在柱状图上显示具体数值, ha参数控制水平对齐方式, va控制垂直对齐方式 for x, y in enumerate(y_data): plt.text(x, y + 100, '%s' % y, ha='center', va='bottom') for x, y in enumerate(y_data2): plt.text(x, y + 100, '%s' % y, ha='center', va='top') # 设置标题 plt.title("Java与Android图书对比") # 为两条坐标轴设置名称 plt.xlabel("年份") plt.ylabel("销量") # 显示图例 plt.legend() plt.show()

上面程序中,第 9、10 两行代码用于在数据图上生成两组柱状图,程序设置了这两组柱状图的颜色和透明度。

在使用 bar() 函数绘制柱状图时,默认不会在柱状图上显示具体的数值。为了能在柱状图上显示具体的数值,程序可以调用 text() 函数在数据图上输出文字,如上面程序中第 10 行代码所示。

在使用 text() 函数输出文字时,该函数的前两个参数控制输出文字的 X、Y 坐标,第三个参数则控制输出的内容。其中 va 参数控制文字的垂直对齐方式,ha 参数控制文字的水平对齐方式。

对于上面的程序来说,由于 X 轴数据是一个字符串列表,因此 X 轴实际上是以列表元素的索引作为刻度值的。因此,当程序指定输出文字的 X 坐标为 0 时,表明将该文字输出到第一个条柱处;对于 Y 坐标而言,条柱的数值正好在条柱高度所在处,如果指定 Y 坐标为条柱的数值 +100,就是控制将文字输出到条柱略上一点的位置。

运行上面程序,可以看到如图 1 所示的效果。


图 1 两组柱状图

从图 1 所示的显示效果来看,第二次绘制的性状图完全与第一次绘制的柱状图重叠,这并不是我们期望的结果,我们希望每组数据的条柱能并列显示。

为了实现条柱井列显示的效果,首先分析条柱重叠在一起的原因。使用 Matplotlib 绘制柱状图时同样也需要 X 轴数据,本程序的 X 轴数据是元素为字符串的 list 列表,因此程序实际上使用各字符串的索引作为 X 轴数据。比如 '2011' 字符串位于列表的第一个位置,因此代表该条柱的数据就被绘制在 X 轴的刻度值1处(由于两个柱状图使用了相同的 X 轴数据,因此它们的条柱完全重合在一起)。

为了将多个柱状图的条柱并列显示,程序需要为这些柱状图重新计算不同的 X 轴数据。为了精确控制条柱的宽度,程序可以在调用 bar() 函数时传入 width 参数,这样可以更好地计算条柱的并列方式。将上面程序改为如下形式:

import matplotlib.pyplot as plt import numpy as np # 构建数据 x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017'] y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000] y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700] bar_width=0.3 # 将X轴数据改为使用range(len(x_data), 就是0、1、2... plt.bar(x=range(len(x_data)), height=y_data, label='C语言基础', color='steelblue', alpha=0.8, width=bar_width) # 将X轴数据改为使用np.arange(len(x_data))+bar_width, # 就是bar_width、1+bar_width、2+bar_width...这样就和第一个柱状图并列了 plt.bar(x=np.arange(len(x_data))+bar_width, height=y_data2, label='Java基础', color='indianred', alpha=0.8, width=bar_width) # 在柱状图上显示具体数值, ha参数控制水平对齐方式, va控制垂直对齐方式 for x, y in enumerate(y_data): plt.text(x, y + 100, '%s' % y, ha='center', va='bottom') for x, y in enumerate(y_data2): plt.text(x+bar_width, y + 100, '%s' % y, ha='center', va='top') # 设置标题 plt.title("C与Java对比") # 为两条坐标轴设置名称 plt.xlabel("年份") plt.ylabel("销量") # 显示图例 plt.legend() plt.show()

该程序与前一个程序的区别就在于第 10、14 两行代码,这两行代码使用了不同的 x 参数,其中第一个柱状图的 X 轴数据为 range(len(x_data)),也就是 0、1、2…,这样第一个柱状图的各条柱恰好位于 0、1、2… 刻度值处;第二个柱状图的 X 轴数据为 np.arange(len(x_data))+bar_width,也就是 bar_width、1+bar_width、2+bar_width···,这样第二个柱状图的各条柱位于 0、1、2…刻度值的偏右一点 bar_width 处,这样就恰好与第一个柱状图的各条柱并列了。

运行上面程序,将会发现该柱状图的 X 轴的刻度值变成 0、1、2 等值,不再显示年份。为了让柱状图的 X 轴的刻度值显示年份,程序可以调用 xticks() 函数重新设置 X 轴的刻度值。

例如,在程序中添加如下代码:

# 为X轴设置刻度值

plt.xticks(np.arange(len(x_data))+bar_width/2, x_data)

上面代码使用 x_data 为 X 轴设置刻度值,第一个参数用于控制各刻度值的位置,该参数是 np.arange(len(x_data))+bar_width/2,也就是 bar_width/2、1+bar_width/2、2+bar_width/2 等,这样这些刻度值将被恰好添加在两个条柱之间。

运行上面程序可看到如图 2 所示的运行结果:


图 2 并列的柱状图

有些时候,可能希望两个条柱之间有一点缝隙,那么程序只要对第二个条柱的 X 轴数据略做修改即可。例如,将上面程序中第 14 行代码改为如下形式:

plt.bar(x=np.arange(len(x_data))+bar_width+0.05, height=y_data2,
label='Java基础', color='indianred', alpha=0.8, width=bar_width)

上面代码重新计算了 X 轴数据,使用 np.arange(len(x_data))+bar_width+0.05 作为 X 轴数据,因此两组柱状图的条柱之间会有 0.05 的距离。

Matplotlib 绘制水平柱状图

调用 Matplotlib 的 barh() 函数可以生成水平柱状图。barh() 函数的用法与 bar() 函数的用法基本一样,只是在调用 barh() 函数时使用 y参数传入 Y 轴数据,使用 width 参数传入代表条柱宽度的数据。

例如,如下程序调用 barh() 函数生成两组并列的水平柱状图,来展示两套教程历年的销量统计数据:

import matplotlib.pyplot as plt import numpy as np # 构建数据 x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017'] y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000] y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700] bar_width=0.3 # Y轴数据使用range(len(x_data), 就是0、1、2... plt.barh(y=range(len(x_data)), width=y_data, label='Java基础教程', color='steelblue', alpha=0.8, height=bar_width) # Y轴数据使用np.arange(len(x_data))+bar_width, # 就是bar_width、1+bar_width、2+bar_width...这样就和第一个柱状图并列了 plt.barh(y=np.arange(len(x_data))+bar_width, width=y_data2, label='C语言基础', color='indianred', alpha=0.8, height=bar_width) # 在柱状图上显示具体数值, ha参数控制水平对齐方式, va控制垂直对齐方式 for y, x in enumerate(y_data): plt.text(x+5000, y-bar_width/2, '%s' % x, ha='center', va='bottom') for y, x in enumerate(y_data2): plt.text(x+5000, y+bar_width/2, '%s' % x, ha='center', va='bottom') # 为Y轴设置刻度值 plt.yticks(np.arange(len(x_data))+bar_width/2, x_data) # 设置标题 plt.title("Java与C对比") # 为两条坐标轴设置名称 plt.xlabel("销量") plt.ylabel("年份") # 显示图例 plt.legend() plt.show()

上面程序中,第 10 行代码使用 barh() 函数来创建水平柱状图,其中 y 参数为 range(len(x_data)),这意味着这些条柱将会沿着 Y 轴均匀分布;而 width 参数为 y_data,这意味着 y_data 列表所包含的数值会决定各条柱的宽度。第 14 行代码的控制方式与此类似。

运行上面程序,可以看到如图 3 所示的效果。


图 3 水平柱状图