前言1.多图布局
1.1 子视图1.2 嵌套1.3 多图布局分格显示
1.3.1 均匀布局1.3.2 不均匀分布 1.4 双轴显示 2.文本、注释、箭头
2.1 文本2.2 箭头2.3 注释2.4 注释箭头连接形状 3.训练场
3.1 绘制如下子图
前言
本文其实属于:Python的进阶之道【AIoT阶段一】的一部分内容,本篇把这部分内容单独截取出来,方便大家的观看,本文介绍Matplotlib数据可视化高级,读本文之前,如果没有 Matplotlib基础建议先看博客:Matplotlib数据可视化入门,后续还会单独发一篇Matplotlib数据可视化进阶内容供读者学习。
学习本文之前,需要先自修:NumPy从入门到进阶,pandas从入门到进阶本文中很多的 *** 作在 NumPy从入门到进阶 ,pandas从入门到进阶二文中有详细的介绍,包含一些软件以及扩展库,图片的安装和下载流程,本文会直接进行使用。
下载 M a t p l o t l i b Matplotlib Matplotlib 见博客:matplotlib的安装教程以及简单调用,这里不再赘述
1.多图布局 1.1 子视图创建子视图可以一个视图一个视图的创建,也可以多个视图一起创建:
单图创建:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 2 * np.pi) # 创建子视图:2行1列第1个视图 ax = plt.subplot(2, 1, 1) ax.plot(x, np.sin(x)) # 创建子视图:2行1列第2个视图 ax = plt.subplot(2, 1, 2) ax.plot(x, np.cos(x))
多图一起创建:
# 一次创造多个视图 fig, axes = plt.subplots(2, 2) # 2行2列:四个图 # 索引从0开始 axes[0, 0].plot(x, np.sin(x), color = 'red') axes[0, 1].plot(x, np.sin(x), color = 'green') axes[1, 0].plot(x, np.cos(x), color = 'purple') axes[1, 1].plot(x, np.cos(x))
下面附上一个完整的代码供读者理解:
遇到无法理解的地方可以看后面的代码解释,还是不理解的地方可以评论区留言(有问必答)
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) # 子视图1 plt.figure(figsize = (9, 6)) ax = plt.subplot(221) # 两行两列第一个子视图 ax.plot(x, y, color = 'red') ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性 # 子视图2 ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图 line, = ax.plot(x, -y) # 返回绘制对象 line.set_marker('*') # 调用对象设置方法,设置属性 line.set_markerfacecolor('red') line.set_markeredgecolor('green') line.set_markersize(10) # 子视图3 ax = plt.subplot(2, 1, 2) # 两行一列第二行视图 plt.sca(ax) # 设置当前视图 x = np.linspace(-np.pi, np.pi, 200) plt.plot(x, np.sin(x * x), color = 'red')
对上述代码的部分进行讲解:
# 第15行 # line, = ax.plot(x, -y) # 返回绘制对象 import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) # 子视图1 plt.figure(figsize = (9, 6)) ax = plt.subplot(221) # 两行两列第一个子视图 ax.plot(x, y, color = 'red') ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性 # 子视图2 ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图 ax.plot(x, -y) # 返回绘制对象
ax.plot(x, -y)其实就是返回了一句话:[
我们可以发现这句话使用的是列表,我们取出这句话可以用:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) # 子视图1 plt.figure(figsize = (9, 6)) ax = plt.subplot(221) # 两行两列第一个子视图 ax.plot(x, y, color = 'red') ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性 # 子视图2 ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图 line = ax.plot(x, -y) # 返回绘制对象 line[0]
我们也可以使用 , 去取:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) # 子视图1 plt.figure(figsize = (9, 6)) ax = plt.subplot(221) # 两行两列第一个子视图 ax.plot(x, y, color = 'red') ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性 # 子视图2 ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图 line, = ax.plot(x, -y) # 返回绘制对象 line
还有一个很容易晕掉的细节:就是我们在绘图的时候几行几列第几个是什么意识:
我们就拿上述代码去说明:
我们假想有一块空白的板子是供我们绘图的,下面来看上述代码:
ax = plt.subplot(221) # 两行两列第一个子视图
这行代码的意思是把我们假想的这块白板,划分称为两行两列的板子:
然后这个图片占据的是第一个板子的位置,对于板子位置我们有如下规定:
板子的编号从1开始,而非从0开始,从左至右,从上至下依次命名板子
所以对上面这个板子,板子的编号为:
所以我们最后图像所显示的其实就是左上角的位置。
接着来看我们的第二个图:
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
这下就好理解啦,还是把之前的空白板子分成两行两列,然后编号为2,即右上角。
接着我们来看第三个图:
ax = plt.subplot(2, 1, 2) # 两行一列第二行视图
把我们的空白板子分成两行一列,那么就是下图的形式:
然后我们把第三个图片放到第二个位置,不难想到,该图的第二个位置其实就是对应的我们分成两行两列的 3,4 位置,所以三个图像最终绘制的结果就是:
所谓嵌套,其实就是在图形中继续画图:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 25) y = np.sin(x) fig = plt.figure(figsize = (9, 6)) # 创建视图 plt.plot(x,y) # 嵌套方式一,axes轴域(横纵坐标范围),子视图 # x,y,width,height ax = plt.axes([0.2, 0.55, 0.3, 0.3]) # 参数含义[left, bottom, width, height] ax.plot(x, y, color = 'g') # 嵌套方式二 ax = fig.add_axes([0.55, 0.2, 0.3, 0.3]) # 使用视图对象添加子视图 ax.plot(x, y, color = 'r')1.3 多图布局分格显示 1.3.1 均匀布局
每张图片都是均匀展示的
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,2*np.pi) # sharex:所有小图共享x轴 sharey:表示所有小图共享y轴 坐标轴以所有小图中范围最大的进行显示 fig, ((ax11,ax12,ax13), (ax21,ax22,ax23),(ax31,ax32,ax33)) = plt.subplots(3, 3) # 也可通过plt.subplot() 一个个添加子视图 fig.set_figwidth(9) fig.set_figheight(6) ax11.plot(x,np.sin(x)) ax12.plot(x,np.cos(x)) ax13.plot(x,np.tanh(x)) ax21.plot(x,np.tan(x)) ax22.plot(x,np.cosh(x)) ax23.plot(x,np.sinh(x)) ax31.plot(x,np.sin(x) + np.cos(x)) ax32.plot(x,np.sin(x * x) + np.cos(x * x)) ax33.plot(x,np.sin(x) * np.cos(x)) # 紧凑显示,边框会比较小,可以注释掉该行查看效果 plt.tight_layout() plt.show()1.3.2 不均匀分布
上图中代码运行所展示的就是均匀分布的结果,可以看出每张图片所占的空间大小都是均等的,如下面图片的展示结果,就是不均匀分布:
为显示如上图的不均匀分布,我们有三种方法
方法一:
import numpy as np import matplotlib.pyplot as plt # 需要导入gridspec模块 x = np.linspace(0, 2 * np.pi, 200) fig = plt.figure(figsize = (12, 9)) # 使用切片方式设置子视图 ax1 = plt.subplot(3, 1, 1) # 视图对象添加子视图 ax1.plot(x, np.sin(10 * x)) # 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置 ax1.set_title('ax1_title') # 设置小图的标题 ax2 = plt.subplot(3, 3, (4, 5)) ax2.set_facecolor('green') ax2.plot(x, np.cos(x),color = 'red') ax3 = plt.subplot(3, 3, (6, 9)) ax3.plot(x,np.sin(x) + np.cos(x)) ax4 = plt.subplot(3, 3, 7) ax4.plot([1, 3], [2, 4]) ax5 = plt.subplot(3, 3, 8) ax5.scatter([1, 2, 3], [0, 2, 4]) ax5.set_xlabel('ax5_x',fontsize = 12) ax5.set_ylabel('ax5_y',fontsize = 12) plt.show()
方法二:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 2 * np.pi, 100) plt.figure(figsize = (12, 9)) # 子视图1 ax1 = plt.subplot2grid(shape = (3, 3),# 布局形状 loc = (0, 0), # 布局绘制位置 colspan = 3) # 跨几列 ax1.plot(x, np.sin(10 * x)) # 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置 ax1.set_title('ax1_title') # 设置小图的标题 # 子视图2 ax2 = plt.subplot2grid((3, 3), (1, 0), colspan = 2) # 跨两列 ax2.set_facecolor('green') ax2.plot(x,np.cos(x),color = 'red') # 子视图3 ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan = 2) # 跨两行 ax3.plot(x,np.sin(x) + np.cos(x)) # 子视图4 ax4 = plt.subplot2grid((3, 3), (2, 0)) ax4.plot([1, 3], [2, 4]) # 子视图5 ax5 = plt.subplot2grid((3, 3), (2, 1)) ax5.scatter([1, 2, 3], [0, 2, 4]) ax5.set_xlabel('ax5_x',fontsize = 12) ax5.set_ylabel('ax5_y',fontsize = 12)
方法三:
import numpy as np import matplotlib.pyplot as plt # 需要导入gridspec模块 import matplotlib.gridspec as gridspec x = np.linspace(0, 2 * np.pi, 200) fig = plt.figure(figsize = (12, 9)) # 将整个视图分成3x3布局 gs = gridspec.GridSpec(3, 3) # 使用切片方式设置子视图 ax1 = fig.add_subplot(gs[0, :]) # 视图对象添加子视图 ax1.plot(x,np.sin(10 * x)) # 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置 ax1.set_title('ax1_title') # 设置小图的标题 ax2 = plt.subplot(gs[1, :2]) # 模块调用 ax2.set_facecolor('green') ax2.plot(x,np.cos(x), color = 'red') # 从第一行到最后,占1、2两行,后面的2表示只占用第二列,也就是最后的一列 ax3 = plt.subplot(gs[1:, 2]) ax3.plot(x,np.sin(x) + np.cos(x)) # 倒数第一行,只占第0列这一列 ax4 = plt.subplot(gs[-1, 0]) ax4.plot([1, 3], [2, 4]) # 倒数第一行,只占倒数第二列,由于总共三列,所以倒数第二列就是序号1的列 ax5 = plt.subplot(gs[-1, -2]) ax5.scatter([1, 2, 3], [0, 2, 4]) ax5.set_xlabel('ax5_x',fontsize = 12) ax5.set_ylabel('ax5_y',fontsize = 12) plt.show()1.4 双轴显示
有时候,有两个轴是不够用的,我们经常会见到如下的图形,下述图形的样式就是双轴显示:
就比如我们在绘制正弦波和指数函数的时候,让他们在一个
y
y
y轴上显示是不合理的:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) plt.plot(x, y, color = 'blue') _ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue') y2 = np.exp(x) plt.plot(x, y2, color = 'red')
所以,这个时候就需要我们的双轴显示:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) plt.plot(x, y, color = 'blue') _ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue') # 获取当前视图 ax = plt.gca() # 双胞胎,相当于两个x轴,并且是合到一起的 # 这两个x轴对应的是两个y轴 ax2 = ax.twinx() # 其中一个视图纵坐标范围是 -1~1.0,另一个视图范围是0~25 # 刻度是自适应的,当然也可以进行调整 # 默认向 ax2这个子视图中绘制 y2 = np.exp(x) plt.plot(x, y2, color = 'red')
坐标是虽然是自适应的,但是我们也可以对其进行调整:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-np.pi, np.pi, 50) y = np.sin(x) plt.plot(x, y, color = 'blue') _ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue') # 获取当前视图 ax = plt.gca() # 双胞胎,相当于两个x轴,并且是合到一起的 # 这两个x轴对应的是两个y轴 ax2 = ax.twinx() # 其中一个视图纵坐标范围是 -1~1.0,另一个视图范围是0~25 # 刻度是自适应的,当然也可以进行调整 # 默认向 ax2这个子视图中绘制 y2 = np.exp(x) plt.plot(x, y2, color = 'red') # 规定刻度 _ = plt.yticks(np.arange(0, 26, 5), color = 'red')2.文本、注释、箭头
常用函数如下:
import numpy as np import matplotlib.pyplot as plt # 字体属性 font = {'fontsize': 20, 'family': 'KaiTi', 'color': 'red', 'weight': 'bold'} # bold表示字体加粗 x = np.linspace(0.0, 5.0, 100) y = np.cos(2 * np.pi * x) * np.exp(-x) plt.figure(figsize = (9, 6)) plt.plot(x, y, 'k') # k代表黑色 # 视图的标题 plt.title('exponential decay', fontdict = font, pad = 20) # figure的大标题 plt.suptitle('指数衰减', y = 1.05, fontdict = font, fontsize = 30) # 向图片中绘制文本 plt.text(x = 2, y = 0.65, # 横纵坐标位置 s = r'$cos(2 pi t) exp(-t)$', # 文本内容 fontsize = 25) # 文本大小 plt.xlabel('time (s)') plt.ylabel('voltage (mV)') plt.show()2.2 箭头
import matplotlib.pyplot as plt import numpy # 随机生成数字,10行2列(x,y) loc = np.random.randint(0, 10,size = (10, 2)) plt.figure(figsize=(10, 10)) # 画图: loc[:, 0]:所有行都获取,索引0--->横坐标 # loc[:, 1]:所有行都获取,索引1--->纵坐标 # g*:g代表的是绿色,*代表的是点的形状是五角星 # ms = 20:点的大小 plt.plot(loc[:, 0], loc[:, 1], 'g*', ms = 20) plt.grid(True) # 路径 way = np.arange(10) # 0、1、2、...、9索引 np.random.shuffle(way) # 洗牌,打乱顺序 # 开始连点:0-->1-->2-->...-->9 # for 循环:少一个:10个点连9条线 for i in range(0, len(way) - 1): start = loc[way[i]] # 起点 end = loc[way[i + 1]] # 终点 # 画箭头: plt.arrow(start[0], start[1], # x = start[0], y = start[1]:起点的(x, y) end[0] - start[0], # 水平方向上移动的距离 end[1] - start[1], # 竖直方向上移动的距离 head_width = 0.2, lw = 2,# 箭头长度,箭尾线宽 length_includes_head = True) # 长度计算包含箭头箭尾 # 绘制编号:0 1 2 3... 8 plt.text(start[0], start[1], s = i, fontsize = 18, color = 'red') # 绘制编号:9 if i == len(way) - 2: plt.text(end[0], end[1], s = i + 1, fontsize = 18, color = 'red')2.3 注释
import numpy as np import matplotlib.pyplot as plt # 数据创建 fig, ax = plt.subplots() x = np.arange(0.0, 5.0, 0.01) y = np.cos(2 * np.pi * x) line, = ax.plot(x, y, lw = 2) # 注释: ax.annotate('local max', # 文本内容 xy = (2, 1), # 箭头指向位置 xytext = (3, 1.5), # 文本位置 # 箭头属性 arrowprops = dict(facecolor = 'black', # 箭头的颜色 shrink = 0.05)) # 箭头两端收缩的百分比(占总长) ax.annotate('local min', xy = (2.5, -1), xytext = (4, -1.8), arrowprops = dict(facecolor = 'black', width = 2, # 箭头宽度 headwidth = 10, # 箭头头部宽度 headlength = 10, # 箭头头部长度 shrink = 0.1)) # 箭头两端收缩的百分比(占总长) ax.annotate('median', xy = (2.25, 0), xytext = (0.5, -1.8), arrowprops = dict(arrowstyle = '-|>'), # 箭头样式 # 按下 Shift+Tab查看提示:-|>:head_length=0.4,head_width=0.2 fontsize = 20) # y轴范围调为(-2, 2) ax.set_ylim(-2, 2)2.4 注释箭头连接形状
import matplotlib.pyplot as plt def annotate_con_style(ax, connectionstyle): x1, y1 = 3,2 x2, y2 = 8,6 ax.plot([x1, x2], [y1, y2], ".") ax.annotate(text = '', xy = (x1, y1), # 相当于B点,arrow head xytext = (x2, y2), # 相当于A点,arrow tail arrowprops = dict(arrowstyle = '->', color = 'red', shrinkA = 5,shrinkB = 5, connectionstyle = connectionstyle)) ax.text(.05, 0.95, connectionstyle.replace(",", "n"), transform = ax.transAxes, # 相对坐标 ha = "left", va = "top") # 指定对齐方式 # 常用箭头连接样式 fig, axs = plt.subplots(3, 5, figsize = (9, 6)) annotate_con_style(axs[0, 0], "angle3,angleA=90,angleB=0") annotate_con_style(axs[1, 0], "angle3,angleA=0,angleB=90") annotate_con_style(axs[2, 0], "angle3,angleA = 0,angleB=150") annotate_con_style(axs[0, 1], "arc3,rad=0.") annotate_con_style(axs[1, 1], "arc3,rad=0.3") annotate_con_style(axs[2, 1], "arc3,rad=-0.3") annotate_con_style(axs[0, 2], "angle,angleA=-90,angleB=180,rad=0") annotate_con_style(axs[1, 2], "angle,angleA=-90,angleB=180,rad=5") annotate_con_style(axs[2, 2], "angle,angleA=-90,angleB=10,rad=5") annotate_con_style(axs[0, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=0") annotate_con_style(axs[1, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=5") annotate_con_style(axs[2, 3], "arc,angleA=-90,angleB=0,armA=0,armB=40,rad=0") annotate_con_style(axs[0, 4], "bar,fraction=0.3") annotate_con_style(axs[1, 4], "bar,fraction=-0.3") annotate_con_style(axs[2, 4], "bar,angle=180,fraction=-0.2") for ax in axs.flat: # 设置轴域刻度 ax.set(xlim = (0, 10), ylim = (0, 10), xticks = [], yticks = [], aspect = 1) fig.tight_layout(pad = 0.2)3.训练场 3.1 绘制如下子图
要求:
设置中文字体并设置字体大小分别计算每个城市年份、季度、月份、小时的PM2.5数据会用到分组求平均值 *** 作进行数据重塑根据需要调整行索引或者列索引创建子视图2行2列向子视图分别绘制年份、季度、月份、小时的各城市PM2.5走势数据根据需要设置坐标轴标签(比如月份、小时)
首先我们需要下载一个 Excel 文件:
链接: https://pan.baidu.com/s/1nSw6wbOEg8GpP19WQ7ggxw?pwd=53ph
提取码: 53ph
下载完成之后,把该文件和我们的代码放到同一个文件夹下,这一 *** 作我们在之前的博客中已经反复说到,这里就不再进行演示
注:代码处于运行中将显示:
下列代码运行几十秒甚至几分钟都是正常的,耐心等待运行结果即可。
我们先来加载我们的数据
%%time import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.family'] = 'KaiTi' plt.rcParams['font.size'] = 18 df = pd.read_excel('./PM2.5.xlsx')
接下来绘制年份的数据:
# 添加子视图 fig, axes = plt.subplots(2, 2, figsize = (16, 12)) # 按照城市年份分组,获取PM2.5,计算平均值并保留2位小数 df2 = df.groupby(by = ['城市', '年份'])[['PM2.5']].mean().round(2) # 数据重塑 df2 = df2.unstack(level = 0) # 城市作为列索引 df2.columns = df2.columns.droplevel(0) # 删除 PM2.5 这个列索引 df2 = df2[['北京', '上海', '广州', '沈阳', '成都']] # 调整列索引的顺序 df2.plot(ax = axes[0, 0]) # 左上角
绘制季度:
df3 = df.groupby(by = ['城市', '季节'])[['PM2.5']].mean().round(2) # 数据重塑 df3 = df3.unstack(level = 0) # 调整行索引顺序 df3 = df3.loc[list('春夏秋冬')] # 删除列索引 PM2.5 df3.columns = df3.columns.droplevel(0) # 调整列索引顺序 df3 = df3[['北京', '上海', '广州', '沈阳', '成都']] # 第一行第二列的子视图(右上角) df3.plot(ax = axes[0, 1])
绘制月份:
df4 = df.groupby(by = ['城市', '月份'])[['PM2.5']].mean().round(2) # 数据重塑 df4 = df4.unstack(level = 0) df4.columns = df4.columns.droplevel(0) df4 = df4[['北京', '上海', '广州', '沈阳', '成都']] ax = df4.plot(ax = axes[1, 0]) # 设置 months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] ax.set_xticks(np.arange(1, 13)) # 设置刻度(x轴) _ = ax.set_xticklabels(months, rotation = 60) # 旋转60°
绘制小时:
df5 = df.groupby(by = ['城市', '小时'])[['PM2.5']].mean().round(2) # 数据重塑 df5 = df5.unstack(level = 0) df5.columns = df5.columns.droplevel(0) df5 = df5[['北京', '上海', '广州', '沈阳', '成都']] ax = df5.plot(ax = axes[1, 1]) _ = ax.set_xticks(np.arange(0, 24))
最后我们来保存我们的图片:
plt.savefig('./homework7.png')
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)