­

Matplotlib数据可视化(5):柱状图与直方图

  • 2020 年 3 月 11 日
  • 筆記

 

柱状图和直方图是两种非常类似的统计图,区别在于:

  • 直方图展示数据的分布,柱状图比较数据的大小。

  • 直方图X轴为定量数据,柱状图X轴为分类数据。因此,直方图上的每个条形都是不可移动的,X轴上的区间是连续的、固定的。而柱状图上的每个条形是可以随意排序的,有的情况下需要按照分类数据的名称排列,有的则需要按照数值的大小排列。

  • 直方图柱子无间隔,柱状图条形有间隔

  • 直方图条形宽度可不一,柱状图条形宽度须一致。柱状图条形的宽度因为没有数值含义,所以宽度必须一致。但是在直方图中,条形的宽度代表了区间的长度,根据区间的不同,条形的宽度可以不同,但理论上应为单位长度的倍数。

本篇博客将介绍matplotlib中柱状图和直方图的作图方法。

In [1]:
from matplotlib import pyplot as plt  import numpy as np  import matplotlib as mpl  mpl.rcParams['font.sans-serif'] = ['SimHei']  # 中文字体支持  
 

1 bar()与barh()

 

matplotlib中提供了bar()和barh()两种方法画柱状图,bar()用来画垂直柱状图,barh()画水平柱状图,两者参数大同小异,如下所示:

 

 

2 垂直柱状图与水平柱状图

In [2]:
value= np.arange(6) ** 2  category = range(len(value))    fig = plt.figure(figsize=(8, 4))    # 垂直柱状图  ax1 = fig.add_subplot(121)  ax1.set_title('图1 垂直柱状图')  ax1.bar(x=category, height=value)    # 垂直柱状图  ax2 = fig.add_subplot(122)  ax2.set_title('图2 水平柱状图')  ax2.barh(y=category, width=value)  # 注意这里参数名和值的传递与bar()不同    plt.show()  
 
 

3 颜色、透明度与边框

In [3]:
value= np.arange(6) ** 2  category = range(len(value))    fig = plt.figure(figsize=(8, 4))    # 垂直柱状图  ax1 = fig.add_subplot(121)  ax1.set_title('图1 垂直柱状图')  ax1.bar(x=category, height=value,          alpha=0.5,  # 透明度          width=0.5,   # 每个条形的宽度          color='yellow',  # 填充前景色          edgecolor='red',  # 边框颜色          linewidth=3  # 边框宽度         )    # 垂直柱状图  ax2 = fig.add_subplot(122)  ax2.set_title('图2 水平柱状图')  ax2.barh(y=category, width=value,           alpha=1,  # 透明度           height=0.8,   # 每个条形的宽度          color=['green', 'red', 'yellow', 'blue', 'grey', 'magenta'],  # 填充前景色           linewidth=3  # 不显示边框         )    plt.show()  
 
 

4 刻度标签

In [4]:
value= np.arange(6) ** 2  category = range(len(value))    fig = plt.figure(figsize=(8, 4))    # 垂直柱状图  ax1 = fig.add_subplot(121)  ax1.set_title('图1 垂直柱状图')  ax1.bar(x=category, height=value,          tick_label='类别'         )    # 垂直柱状图  ax2 = fig.add_subplot(122)  ax2.set_title('图2 水平柱状图')  ax2.barh(y=category, width=value,           tick_label=['类1', '类2', '类3', '类4', '类5', '类6']         )    plt.show()  
 
 

5 添加误差线

In [5]:
means = (20, 35, 30, 35, 27)  # 各组平均分  std = (2, 3, 4, 1, 2)  # 组各标准差  label = ('第一组', '第二组', '第三种', '第四组', '第五组')  bar_width = 0.4  bar_x = np.arange(len(label))      fig = plt.figure(figsize=(8, 4))      ax1 = fig.add_subplot(121)  bar1 = ax1.bar(x=bar_x, height=means, width=bar_width, color='green',                yerr=std,  # 添加误差线                ecolor='red',  # 误差线颜色                capsize=5,  # 两端线段长短                 tick_label=label               )    ax2 = fig.add_subplot(122)  bar2 = ax2.barh(y=bar_x, width=means, height=bar_width, color='green',                xerr=std,  # 添加误差线                ecolor='red',  # 误差线颜色                capsize=5,  # 两端线段长短                tick_label=label               )    plt.show()  
 
 

6 添加数据标注

In [6]:
means = (20, 35, 30, 35, 27)  # 各组平均分  std = (2, 3, 4, 1, 2)  # 组各标准差  label = ('第一组', '第二组', '第三种', '第四组', '第五组')  bar_width = 0.5  bar_x = np.arange(len(label))      fig = plt.figure(figsize=(10, 4),tight_layout=True)      ax1 = fig.add_subplot(121)  bar1 = ax1.bar(x=bar_x, height=means, width=bar_width, color='green', tick_label=label               )  for b in bar1:          height = b.get_height()          ax1.annotate('{}'.format(height),                      xy=(b.get_x() + b.get_width() / 2, height),                      xytext=(0, 3),  # 3 points vertical offset                      textcoords="offset points",color='red',                      ha='center', va='bottom')    ax2 = fig.add_subplot(122)  bar2 = ax2.barh(y=bar_x, width=means, height=bar_width, color='green', tick_label=label               )    for b in bar2:          width = b.get_width()          ax2.annotate('{}'.format(width),                      xy=(width, b.get_y() + b.get_height() / 2),                      xytext=(0, 3),  # 3 points vertical offset                      textcoords="offset points",color='red',                      ha='left', va='center')    plt.show()  
 
 

7 分组柱状图

In [7]:
menMeans = (20, 35, 30, 35, 27)  # 男生各组平均分  womenMeans = (25, 32, 34, 20, 25)# 女生各组平均分  menStd = (2, 3, 4, 1, 2)  # 男生组各标准差  womenStd = (3, 5, 2, 3, 3) # 女生组各标准差  label = ('第一组', '第二组', '第三种', '第四组', '第五组')  bar_width = 0.4  bar_x = np.arange(len(label))      fig = plt.figure(figsize=(8, 4))      ax = fig.add_subplot(111)  ax.set_title('图1 垂直柱状图')  bar1 = ax.bar(x=bar_x - bar_width/2,   # 设置不同的x起始位置                height=menMeans, width=bar_width)  bar2 = ax.bar(x=bar_x + bar_width/2,   # 设置不同的x起始位置                height=womenMeans, width=bar_width,          )    ax.set_xlabel('组别')  ax.set_ylabel('分数')  ax.set_title('各组不同性别分数')  ax.set_xticks(range(5))  ax.set_xticklabels(label)  ax.set_yticklabels(np.arange(0, 81, 10))  ax.legend((bar1, bar2), ('男生', '女生'))    plt.show()  
 
 

8 堆叠柱状图

In [8]:
menMeans = (20, 35, 30, 35, 27)  # 男生各组平均分  womenMeans = (25, 32, 34, 20, 25)# 女生各组平均分  menStd = (2, 3, 4, 1, 2)  # 男生组各标准差  womenStd = (3, 5, 2, 3, 3) # 女生组各标准差  label = ('第一组', '第二组', '第三种', '第四组', '第五组')  bar_width = 0.4  bar_x = np.arange(len(label))    fig = plt.figure(figsize=(8, 4))      ax = fig.add_subplot(111)  ax.set_title('图1 垂直柱状图')  bar1 = ax.bar(x=bar_x, height=menMeans, width=bar_width)  bar2 = ax.bar(x=bar_x, height=womenMeans, width=bar_width,          bottom=menMeans # 通过bottom参数设置起始位置, 起始位置就是下半部分(bar1)条形的高度          )    ax.set_xlabel('组别')  ax.set_ylabel('分数')  ax.set_title('各组不同性别分数')  ax.set_xticks(range(5))  ax.set_xticklabels(label)  ax.set_yticklabels(np.arange(0, 81, 10))  ax.legend((bar1, bar2), ('男生', '女生'))    plt.show()  
 
 

9 直方图

 

直方图的绘制是通过hist()方法完成。hist()方法参数很多,来看看主要的参数:

 

In [46]:
data_x = [131, 98, 125, 131, 124, 139, 131, 117, 128, 108, 135, 138, 131, 102, 107, 114, 119, 128, 121, 142, 127, 130, 124,     101, 110, 116, 117, 110, 128, 128, 115, 99, 136, 126, 134, 95, 138, 117, 111, 78, 132, 124, 113, 150, 110, 117, 86,     95, 144, 105, 126, 130, 126, 130, 126, 116, 123, 106, 112, 138, 123, 86, 101, 99, 136, 123, 117, 119, 105, 137,     123, 128, 125, 104, 109, 134, 125, 127, 105, 120, 107, 129, 116, 108, 132, 103, 136, 118, 102, 120, 114, 105, 115,     132, 145, 119, 121, 112, 139, 125, 138, 109, 132, 134, 156, 106, 117, 127, 144, 139, 139, 119, 140, 83, 110, 102,     123, 107, 143, 115, 136, 118, 139, 123, 112, 118, 125, 109, 119, 133, 112, 114, 122, 109, 106, 123, 116, 131, 127,     115, 118, 112, 135, 115, 146, 137, 116, 103, 144, 83, 123, 111, 110, 111, 100, 154, 136, 100, 118, 119, 133, 134,     106, 129, 126, 110, 111, 109, 141, 120, 117, 106, 149, 122, 122, 110, 118, 127, 121, 114, 125, 126, 114, 140, 103,     130, 141, 117, 106, 114, 121, 114, 133, 137, 92, 121, 112, 146, 97, 137, 105, 98, 117, 112, 81, 97, 139, 113, 134,     106, 144, 110, 137, 137, 111, 104, 117, 100, 111, 101, 110, 105, 129, 137, 112, 120, 113, 133, 112, 83, 94, 146,     133, 101, 131, 116, 111, 84, 137, 115, 122, 106, 144, 109, 123, 116, 111, 111, 133, 150]    fig = plt.figure(figsize=(8, 4))    ax1 = fig.add_subplot(121)  hists1 = ax1.hist(x=data_x, bins=5)  # 等距划分    ax2 = fig.add_subplot(122)  hists2 = ax2.hist(x=data_x,bins=[78,90,100,120,140,145,150])    plt.show()  
 
 

hist()方法将会返回一个包含三个元素的数组,第一个元素为每个条形区间中元素的数量,第二个元素为区间的边界,第三个元素为Patch实例化对象。

In [47]:
hists1  
Out[47]:
(array([ 9., 49., 97., 77., 18.]),   array([ 78. ,  93.6, 109.2, 124.8, 140.4, 156. ]),   <a list of 5 Patch objects>)