学习目标★了解常见的高级图表★掌握高级图表的绘制方法matplotlib 除了可以绘制简单的图表,还可以绘制一些常见的高级图表,包括等高线图、矢量场流线图、棉棒图、哑铃图、甘特图、人口金字塔图、漏斗图、桑基图、树状图和华夫饼图。下面将对 matplotlib 中绘制高级图表的相关知识进行详细介绍。
8.1 绘制等高线图等高线图是地形图上高程相等的相邻各点所连成的闭合曲线,它会将地面上海拔高度相同的点连成环线,之后将环线垂直投影到某一水平面上,并按照一定的比例缩绘到图纸上,图 8-1 的等高线中标注的数字代表海拔高度。等高线图包含 3 个主要的信息,分别为坐标点的 x 值、 y 值及高度。假设坐标点的高度为 h ,则 h 、 x 、 y 之间的关系如下所示 :h =(1 − x /2+ x 5 + y 3 )e − x 2 − y 2在 matplotlib 中,pyplot 可以使用 contour()、contourf() 函数分别绘制和填充等高线图。contour() 函数的语法格式如下所示 :contour([X, Y,]Z, [levels,] ** kwargs)该函数的常用参数的含义如下。·X,Y :表示坐标点的网格数据。
·Z :表示坐标点对应的高度数据。·levels :表示等高线的数量。若 levels 为 n ,则 说明绘制 n + 1 条等高线。·colors :表示不同高度的等高线颜色。·cmap :表示颜色映射表。·linewidths :表示等高线的宽度。·linestyles :表示等高线的线型。需要注意的是,参数 X、Y 需要接收网格数据,即以坐标矩阵批量描述点的位置。numpy 模块的 meshgrid() 函数可以生成网格数据。contourf() 与 contour() 函数的参数相似,此处不再赘述。此外,Axes 类的对象也可以使用 contour()、contourf() 方法绘制和填充等高线图。下面使用 numpy 生成一组位于 -2 ~ 2 之间的样本数据,计算出等高线的高度,绘制并填充等高线图,示例代码如下。In [1]:import numpy as npimport matplotlib.pyplot as plt# 计算高度def calcu_elevation(x1, y1):h = (1-x1/2 + x1 ** 5 + y1 ** 3) * np.exp(-x1 ** 2 - y1 ** 2)return hn = 256x = np.linspace(-2, 2, n)y = np.linspace(-2, 2, n)# 利用 meshgrid() 函数生成网格数据x_grid, y_grid = np.meshgrid(x, y)fig = plt.figure()ax = fig.add_subplot(111)# 绘制等高线con = ax.contour(x_grid, y_grid, calcu_elevation(x_grid, y_grid),8, colors='black')# 填充等高线的颜色ax.contourf(x_grid, y_grid, calcu_elevation(x_grid, y_grid),8, alpha=0.75, cmap=plt.cm.copper)# 为等高线添加文字标签ax.clabel(con, inline=True, fmt='%1.1f', fontsize=10)ax.set_xticks([])ax.set_yticks([])plt.show()以上示例首先定义了一个计算等高线高度的 calcu_elevation() 函数,其次生成了一组样本数据,并将这些数据转换为网格数据,然后分别调用 contour() 和 contourf() 方法绘制和填充等高线,最后调用 clabel() 方法为等高线添加标注,以及隐藏 x 轴和 y 轴的刻度。8.2 绘制矢量场流线图
矢量场流线图可以表现矢量场的流态,常见于科学和自然学科中的磁场、万有引力和流
由图 8-3 可知,矢量场流线图包含多条带有箭头的线条,其中线条的长度表示矢量场的强度,箭头的方向表示矢量场的方向。此外,矢量场的强度也可以用线条的密度来表示。在 matplotlib 中,pyplot 可以使用 streamplot() 函数绘制矢量场流线图。streamplot() 函数的语法格式如下所示 :streamplot(x, y, u, v, density=1, linewidth=None, col=None, cmap=None,norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1,transform=None, zorder=None, start_points=None, maxlength=4.0,
integration_direction='both', * , data=None)该函数常用参数的含义如下。·x,y :表示间距均匀的网格数据。·u,v :表示 (x,y) 速率的二维数组。·density :表示流线的密度。·linewidth :表示流线的宽度。·arrowsize :表示箭头的大小。·arrowstyle :表示箭头的类型。·minlength :表示流线的最小长度。·maxlength :表示流线的最大长度。此外,Axes 类的对象也可以使用 streamplot() 方法绘制矢量场流线图。下面根据一组模拟某磁场的网格数据绘制一个矢量场流线图,示例代码如下。In [2]:import numpy as npimport matplotlib.pyplot as plty, x = np.mgrid[0:5:50j, 0:5:50j]u = xv = yfig = plt.figure()ax = fig.add_subplot(111)# 绘制矢量场流线图ax.streamplot(x, y, u, v)plt.show()由图 8-4 可知,右侧的流线密度较大,说明该处磁场较强。
8.3 绘制棉棒图棉棒图亦称为火柴杆图、大头针图或棒棒糖图,由线段(茎)与标记符号(茎头,默认为圆点)连接而成。其中,线段表示数据点到基线的距离,标记符号表示数据点的数值。棉棒图是柱形图或条形图的变形,主要用于比较标记符号的相对位置,而非比较线段的长度。在 matplotlib 中,pyplot 可以使用 stem() 函数绘制棉棒图。stem() 函数的语法格式如下所示 :stem([x,] y, linefmt=None, markerfmt=None, basefmt=None, bottom=0,label=None, use_line_collection=False, data=None)该函数常用参数的含义如下。·x,y :表示茎的 x 值和茎头的 y 值。·linefmt :表示茎属性的字符串。·markerfmt :表示茎头属性的字符串。·basefmt :表示基线属性的字符串。·bottom :表示基线的 y 值。·label :表示应用于图例的标签。·use_line_collection :若设为 True,则将棉棒图的所有线段存储到一个 LineCollection 类对象中 ;若设为 False,则将棉棒图的所有线段存储到列表中。stem() 函数会返回一个形如 (markerline, stemlines, baseline) 的元组,其中元组的第 1 个元素 markerline 为代表棉棒图标记的 Line2D 对象,第 2 个元素 stemlines 为代表棉棒图线段的Line2D 对象,第 3 个元素 baseline 为代表基线的 Line2D 对象。此外,Axes 类的对象也可以使用 stem() 方法绘制棉棒图。假设汽车生产商生产了一批小型轿车,并测试了这批小型轿车的燃料消耗量,结果如注 :表 8-1 中所列轿车的燃料消耗量数据来源于网络,不能确定其真实性。
根据表 8-1 的数据,将“轿车品牌”一列的数据作为 x 轴的标签,将“燃料消耗量”一列的数据作为 y 轴的数据,使用 stem() 绘制不同品牌轿车燃料消耗量的棉棒图,具体代码如下。In [3]:import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = 'SimHei'plt.rcParams['axes.unicode_minus'] = Falsex = np.arange(1, 16)y = np.array([5.9, 6.2, 6.7, 7.0, 7.0, 7.1, 7.2, 7.4,7.5, 7.6, 7.7, 7.7, 7.7, 7.8, 7.9])labels = np.array([' 宝骏 310', ' 宝马 i3', ' 致享 ', ' 焕驰 ', ' 力帆 530',' 派力奥 ', ' 悦翔 V3', ' 乐风 RV', ' 奥迪 A1', ' 威驰 FS',' 夏利 N7', ' 启辰 R30', ' 和悦 A13RS', ' 致炫 ', ' 赛欧 '])fig = plt.figure(figsize=(10, 6), dpi= 80)ax = fig.add_subplot(111)# 绘制棉棒图markerline, stemlines, baseline = ax.stem(x, y, linefmt='--',markerfmt='o', label='TestStem', use_line_collection=True)# 设置棉棒图线段的属性plt.setp(stemlines, lw=1)ax.set_title(' 不同品牌轿车的燃料消耗量 ', fontdict={'size':18})ax.set_ylabel(' 燃料消耗量 (L/km)')ax.set_xticks(x)ax.set_xticklabels(labels, rotation=60)
ax.set_ylim([0, 10])for temp_x, temp_y in zip(x, y):ax.text(temp_x, temp_y + 0.5, s='{}'.format(temp_y), ha= 'center',va='bottom', fontsize=14)plt.show()
图 8-6 中,每个茎头上方都添加了注释文本。由图 8-6 可知,赛欧轿车的燃料消耗量最大,为 7.9 L/km。
8.4 绘制哑铃图哑铃图又名 DNA 图(图表横着看像哑铃,竖着看像 DNA),主要用于展示两个数据点之间的变化。哑铃图可以看作散点图与线形图的组合,适用于比较各种项目“前”与“后”的位置及项目的等级排序等场景。例如,2017 年与 2018 年某公司各部门活动经费的使用情况如图 8-7 所示。已知美国在 2013 年和 2014 年分别对部分城市的人口 PCT 指标进行了统计,并将统计的下面使用 pandas 读取 health.xlsx 文件的数据,并根据读取的数据绘制由散点和线组成的
哑铃图,示例代码如下。In [4]:import pandas as pdimport matplotlib.pyplot as pltimport matplotlib.lines as mlinesplt.rcParams['font.sans-serif'] = 'SimHei'plt.rcParams['axes.unicode_minus'] = Falsedf = pd.read_excel(r"C:\Users\admin\Desktop\health.xlsx")df.sort_values('pct_2014', inplace=True)df.reset_index(inplace=True)df = df.sort_values(by="index")def newline(p1, p2, color='black'):ax = plt.gca() # 获取当前的绘图区域
l = mlines.Line2D([p1[0], p2[0]], [p1[1],p2[1]], color='skyblue')ax.add_line(l)return lfig, ax = plt.subplots(1, 1, figsize=(8, 6))# 绘制散点ax.scatter(y=df['index'], x=df['pct_2013'], s=50,color='#0e668b', alpha=0.7)ax.scatter(y=df['index'], x=df['pct_2014'], s=50,color='#a3c4dc', alpha=0.7)# 绘制线条for i, p1, p2 in zip(df['index'], df['pct_2013'], df['pct_2014']):newline([p1, i], [p2, i])ax.set_title("2013 年与 2014 年美国部分城市人口 PCT 指标的变化率 ", fontdict={'size':12})ax.set_xlim(0, .25)ax.set_xticks([.05, .1, .15, .20])ax.set_xticklabels(['5%', '10%', '15%', '20%'])ax.set_xlabel(' 变化率 ')ax.set_yticks(df['index'])ax.set_yticklabels(df['city'])ax.grid(alpha=0.5, axis='x')plt.show()图 8-9 中,每个杠铃图形左端的圆点代表 2013 年美国部分城市人口 PCT 指标的变化率,右端的圆点代表 2014 年美国部分城市人口 PCT 指标的变化率。由图 8-9 可知,洛杉矶市人口 PCT 指标的变化率最大,圣路易斯市人口 PCT 指标的变化率最小。
8.5 绘制甘特图甘特图亦称为横道图、条状图,它通过活动列表和时间刻度表示特定项目的顺序与持续时间。甘特图一般以时间为横轴、项目为纵轴,可以直观地展示每个项目的进展情况,以便于管理者了解项目的剩余任务及评估工作进度。例如,某公司于 12 月份跟踪了某项目进度,
观察图 8-10 可知,甘特图类似于条形图,它们的图形都是横向的矩形条,但甘特图中每个矩形条的起始位置是不同的。使用 pyplot 模块的 barh() 函数可以绘制一个甘特图,只需要给 left 参数传入值,指定每个矩形条 x 坐标值即可。已知某公司准备开辟一个新项目,为确保项目的可行性,将该项目划分为“项目确定”“问卷设计”“试访”“问卷确定”“实地执行”“数据录入”“数据分析”“报告提交”共 8 个任务,并指定了各任务的周期。下面使用 barh() 绘制一个甘特图,示例代码如下。In [5]:import numpy as npimport matplotlib.pyplot as pltticks = np.array([' 报告提交 ', ' 数据分析 ', ' 数据录入 ', ' 实地执行 ',' 问卷确定 ', ' 试访 ', ' 问卷设计 ', ' 项目确定 '])y_data = np.arange(1, 9)x_data = np.array([0.5, 1.5, 1, 3, 0.5, 1, 1,2])fig,ax = plt.subplots(1, 1)ax.barh(y_data, x_data, tick_label=ticks,left=[7.5, 6, 5.5, 3, 3, 2, 1.5, 0], color='#CD5C5C')[ax.spines[i].set_visible(False) for i in ['top', 'right']]ax.set_title(" 任务甘特图 ")ax.set_xlabel(" 日期 ")ax.grid(alpha=0.5, axis='x')plt.show()运行程序,效果如图 8-11 所示。