0 序
这一章主要讲解了子图种类和子图上的方法。对于数据分析来说,针对某一类数据往往需要给出不同维度的图表分析,以形成可视化看板,所以子图的排版以及操作方法的熟练掌握就显的尤为重要了。
章节内容不多,但是同样重要,上代码~
1 导入三方模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 解决中文乱码的问题
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号异常显示问题
plt.rcParams['axes.unicode_minus'] = False
这里的plt.rcParams可以解决大多数中文乱码的问题了。因为时间问题,我还没有细细研究mac上的字体解决方案(尝试了几个都还是乱码,所以暂时先用英文啦~)
2 子图
2.1 使用plt.subplots绘制均匀状态下的子图
返回元素分别是画布和子图构成的列表,第一个数字为行,第二个为列
figsize参数可以指定整个画布的大小
sharex和sharey分别表示是否共享横轴和纵轴刻度
tight_layout函数可以调整子图的相对大小使字符不会重叠
fig, axs = plt.subplots(2, 5, figsize=(10, 4), sharex=True, sharey=True)
fig.suptitle('sample1',size=20)
'''整个画布的标题'''
for i in range(2):
for j in range(5):
axs[i][j].scatter(np.random.randn(10), np.random.randn(10))
axs[i][j].set_title('row%d, column%d'%(i+1, j+1))
'''设置每个子图的子标题'''
axs[i][j].set_xlim(-5, 5)
axs[i][j].set_ylim(-5, 5)
if i==1: axs[i][j].set_xlabel('xlabel')
'''xlabel只在最底部显示'''
if j==0: axs[i][j].set_ylabel('ylabel')
'''ylabel只在最左侧显示'''
fig.tight_layout()
'''调整子图的相对大小使字符不会重叠'''
2.2 使用GridSpec绘制非均匀子图
所谓非均匀包含两层含义:
第一是指图的比例大小不同但没有跨行或跨列
第二是指图为跨列或跨行状态
利用 add_gridspec 可以指定相对宽度比例 width_ratios 和相对高度比例参数 height_ratios
fig = plt.figure(figsize=(10, 4))
spec = fig.add_gridspec(nrows=2, ncols=5, width_ratios=[1, 2, 3, 4, 5], height_ratios=[1, 3])
'''width_ratios和height_ratios都是比例'''
'''下面的代码和上一段代码意思相近'''
fig.suptitle('sample2', size=20)
for i in range(2):
for j in range(5):
ax = fig.add_subplot(spec[i, j])
ax.scatter(np.random.randn(10), np.random.randn(10))
ax.set_title('row%d, column%d'%(i+1, j+1))
if i==1: ax.set_xlabel('xlabel')
if j==0: ax.set_ylabel('ylabel')
fig.tight_layout()
在上面的例子中出现了 spec[i, j] 的用法,事实上通过切片就可以实现子图的合并而达到跨图的功能
fig = plt.figure(figsize=(10, 4))
spec = fig.add_gridspec(nrows=2, ncols=6,
width_ratios=[2, 2.5, 3, 1, 1.5, 2], height_ratios=[1, 2])
# sub1
ax = fig.add_subplot(spec[0, :3])
'''sub1占用的大小为第一行,宽度为第零块到第二块'''
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub2
ax = fig.add_subplot(spec[0, 3: 5])
'''sub占用的大小为第一行,宽度为第三块到第四块'''
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub3
ax = fig.add_subplot(spec[:, 5])
'''整列(1行+2行),第五块'''
ax.scatter(np.random.randn(10), np.random.randn(10))
'''下同,类似切片,画画图就能理解了'''
# sub4
ax = fig.add_subplot(spec[1, 0])
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub5
ax = fig.add_subplot(spec[1, 1:5])
ax.scatter(np.random.randn(10), np.random.randn(10))
fig.tight_layout()
3 子图上的方法
在 ax 对象上定义了和 plt 类似的图形绘制函数,常用的有: plot, hist, scatter, bar, barh, pie
绘制直线
fig, ax = plt.subplots(figsize=(4, 3))
ax.plot([1, 2], [2, 1])
绘制直方图
fig, ax = plt.subplots(figsize=(4, 3))
ax.hist(np.random.randn(1000))
常用直线的画法为: axhline, axvline, axline (水平、垂直、任意方向)
添加水平/垂直辅助线
fig, ax = plt.subplots(figsize=(4, 3))
# 这里的0.2和0.8不知道设置什么
ax.axhline(0.5, 0.2, 0.8)
ax.axvline(0.5, 0.2, 0.8)
ax.axline([0.3, 0.3], [0.7, 0.7])
使用grid加灰色网格
fig, ax = plt.subplots(figsize=(4, 3))
ax.grid(True)
使用 set_xscale, set_title, set_xlabel 分别可以设置坐标轴的规度(指对数坐标等)、标题、轴名
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
fig.suptitle('sample3', size=20)
for j in range(2):
axs[j].plot(list('abcd'), [10 ** i for i in range(4)])
if j==0:
axs[j].set_yscale('log')
'''y轴刻度为log坐标'''
axs[j].set_title('sub_title1')
axs[j].set_ylabel('log_label')
else:
axs[j].set_title('sub_title1')
axs[j].set_ylabel('normal_label')
'''y轴刻度为普通坐标'''
fig.tight_layout()
与一般的 plt 方法类似, legend, annotate, arrow, text 对象也可以进行相应的绘制
fig, ax = plt.subplots()
ax.arrow(0, 0, 1, 1, head_width=0.03, head_length=0.05, facecolor='red', edgecolor='blue')
'''
绘制箭头
0,0 - 箭头起点坐标
1,1 - 箭头终点坐标
head_width - 箭头头部的宽
head_length - 箭头头部的高
facecolor - 箭头填充色
edgecolor - 箭头的轮廓色
'''
ax.text(x=0, y=0, s='this is a graph', fontsize=16, rotation=70,
rotation_mode='anchor', color='green')
'''
x,y - 文字的坐标
s - 文字内容
fontsize - 字体大小
rotation - 旋转的角度
rotation_mode - 对齐方式
'''
ax.annotate('this is mid point', xy=(0.5, 0.5),
xytext=(0.8, 0.2), arrowprops=dict(facecolor='yellow', edgecolor='black'),
fontsize=16)
'''
xy - 箭头坐标
xytext - 文字起始坐标
arrowprops - 箭头的参数字典:填充颜色和边缘色
'''
这里有点丑,可以把画布调整大点或把箭头的注释字体弄小点
fig, ax = plt.subplots()
ax.plot([1, 2], [2, 1], label='line1')
ax.plot([1, 1], [1, 2], label='line1')
ax.legend(loc=1)
'''loc=1 - 图例在右上方''
为了方便,我网上找了一个图例参数-代码的对照表,po给大家:
4 作业
第一题
数据为墨尔本1981年至1990年的每月温度情况,请利用数据,画出如下的图:
先看一下数据
data = pd.read_csv('./data/layout_ex1.csv')
data.head()
我看每个子图的x轴是月份刻度,但是数据里面是yyyy-mm,所以我选择做一下提取。
data['Time_month'] = data['Time'].apply(lambda x: int(str(x)[-2:]))
data.head(15)
然后准备每个子图的x轴和y轴的数据
x1 = list(data['Time_month'][0: 12])
x2 = list(data['Time_month'][12: 24])
x3 = list(data['Time_month'][24: 36])
x4 = list(data['Time_month'][36: 48])
x5 = list(data['Time_month'][48: 60])
x6 = list(data['Time_month'][60: 72])
x7 = list(data['Time_month'][72: 84])
x8 = list(data['Time_month'][84: 96])
x9 = list(data['Time_month'][96: 108])
x10 = list(data['Time_month'][108: 120])
x_list = [x1, x2, x3, x4, x5, x6, x7, x8, x9, x10]
y1 = list(data['Temperature'][0: 12])
y2 = list(data['Temperature'][12: 24])
y3 = list(data['Temperature'][24: 36])
y4 = list(data['Temperature'][36: 48])
y5 = list(data['Temperature'][48: 60])
y6 = list(data['Temperature'][60: 72])
y7 = list(data['Temperature'][72: 84])
y8 = list(data['Temperature'][84: 96])
y9 = list(data['Temperature'][96: 108])
y10 = list(data['Temperature'][108: 120])
y_list = [y1, y2, y3, y4, y5, y6, y7, y8, y9, y10]
开始画图!!!
fig, axs = plt.subplots(2, 5, figsize=(20, 5), sharex=True, sharey=True)
fig.suptitle('Temperature curve of Melbourne from 1981 to 1990', size=25)
subtitle = ['1981', '1982', '1983', '1984', '1985', '1986', '1987',
'1988', '1989', '1990']
'''搞了一个子标题的列表,方便循环的时候直接用'''
k = 0
'''这个k用于数子标题以及数据的选择'''
for i in range(2):
for j in range(5):
axs[i][j].plot(x_list[k], y_list[k], marker='p')
'''marker='p' - 标记点为五角星~~~'''
axs[i][j].set_title(subtitle[k])
axs[i][j].set_yticks([0, 5, 10, 15,20])
if i == 1: axs[i][j].set_xticks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
'''只在第二行的子图显示x轴刻度'''
if j == 0: axs[i][j].set_ylabel('Temperature')
'''在第一列的子图显示y轴刻度'''
k += 1
fig.tight_layout()
第二题
画出数据的散点图和边际分布
用 np.random.randn(2, 150) 生成一组二维数据,使用两种非均匀子图的分割方法,做出该数据对应的散点图和边际分布图,如下图:
fig = plt.figure(figsize=(10, 10))
'''正方形'''
spec = fig.add_gridspec(nrows=2, ncols=2, width_ratios=[5, 1], height_ratios=[1, 6])
'''划分块块,方便分区'''
# sub1
ax = fig.add_subplot(spec[1, 0])
'''这里还是比较抽象,画个图就能理解'''
ax.scatter(np.random.randn(2, 150)[0], np.random.randn(2, 150)[1])
'''二维数据,我第一行用来当x,第一行用来当y'''
plt.xlabel('my_data_x')
plt.ylabel('my_data_y')
# sub2
ax = fig.add_subplot(spec[0, 0])
ax.hist(np.random.randn(2, 150)[0], bins=10)
ax.set_xticks([])
ax.set_yticks([])
# sub3
ax = fig.add_subplot(spec[1, 1])
ax.hist(np.random.randn(2, 150)[1], bins=10, orientation='horizontal')
'''orientation='horizontal'表示条形为水平方向'''
ax.set_xticks([])
ax.set_yticks([])
plt.tight_layout()
plt.subplots_adjust(wspace=0, hspace=0)
'''冬天了,让图靠得紧一点'''
画出来丑丑的,边界的色块也不知道该怎么调,不过肯定有方法,后面学会了再完善呀~
5 结
这一期内容并不多,但是axes在matplotlib中的地位十分重要,希望通过这一篇文章可以提高大家的认识和基础功,下篇我们再会~