matplotlib详细教学
Matplotlib初相识
认识matplotlib
Matplotlib是一个Python 2D绘图库,能够以多种硬拷贝格式和跨平台的交互式环境生成出版物质量的图形,用来绘制各种静态,动态,交互式的图表
一个最简单的绘图例子
matplotlib的图像都是画在对应的figure上,可以认为是一个绘图区域。而一个figure又可以包含一个或者多个axes,可以认为是子区域,这个子区域可以指定属于自己的坐标系。下面通过简单的实例进行展示:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
fig, ax = plt.subplots() # 该函数创建一个包含1个axes的figure,并将两者进行返回
ax.plot([1,2,3,4],[1,4,2,3])
那么也可以用更为简单的方式来进行创建:
line = plt.plot([1,2,3,4],[1,4,2,3])
这是因为如果未指定axes,那么会自动创建一个,因此可以简化。
figure的组成
通常,一个完成的matplotlib图像会包括四个层级(容器):
- Figure:顶级层,用来容纳所有绘图元素
- Axes:matplotlib宇宙的核心,容纳了大量元素用来构造一幅幅的子图,一个figure可以由1个或者多个子图构成
- Axis:axes的下层,用来处理所有与坐标轴、网格相关的元素
- Tick:axis的下层,用来处理所有和刻度相关的元素
两种绘图接口
matplotlib提供了两种最常用的绘图接口:
- 创建figure和axes,然后在此之上调用绘图方法
- 依赖pyplot自动创建figure和axes来绘图
就像是上小节所展示的那样两种创建图的方法。
通用绘图模板
Datawhale提供了一个通常的绘图模板,可以根据实际需要对该模板进行修改了补充:
# 先准备好数据
x = np.linspace(0, 2, 100)
y = x**2
# 设置绘图样式(非必须)
mpl.rc('lines', linewidth=4, linestyle='-.')
# 定义布局
fig, ax = plt.subplots()
# 绘制图像
ax.plot(x, y, label='linear')
# 添加标签,文字和图例
ax.set_xlabel('x label')
ax.set_ylabel('y label')
ax.set_title("Simple Plot")
ax.legend() ;
思考题
-
请思考两种绘图模式的优缺点和各自适合的使用场景
- 我觉得先创建figure和axes再进行绘图的方式更适用于你对图的规划比较清晰,或者你想要画多个子图,这样在同一个figure上作画会简洁方便;而pyplot模型更实用于你当前只需要画一个图,那么把所有元素都加到当前这个图上就可以了
-
在第五节绘图模板中我们是以OO模式作为例子展示的,请思考并写一个pyplot绘图模式的简单模板
-
plt.plot(x,y,label='linear') plt.xlabel("x label") plt.ylabel("y label") plt.title("simple plot") plt.legend()
-
艺术画笔见乾坤
先准备待会儿要用到的库
import numpy as np
import pandas as pd
import re
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.patches import Circle, Wedge
from matplotlib.collections import PatchCollection
概述
matplotlib的三层api
matplotlib的原理或者说基础逻辑是,用Artist对象在画布(canvas)上绘制(Render)图形。因此跟人作画类似,需要三个步骤:
- 准备一个画图
- 准备画笔、颜料
- 作画
因此可以认为matplotlib有三层的API:
matplotlib.backend_bases.FigureCanvas
代表了绘图区,所有的图像都是在绘图区完成的matplotlib.backend_bases.Renderer
代表了渲染器,可以近似理解为画笔,控制如何在 FigureCanvas 上画图。matplotlib.artist.Artist
代表了具体的图表组件,即调用了Renderer的接口在Canvas上作图。
因此我们大部分是利用Artist类来进行绘图。
Artist的分类
Artist有两种类型:primitives
和containers
:
- primitive是基本要素,包含一些我们要在绘图区作图用到的标准图形对象,例如曲线、文字、矩形等等。
- container是容器,可以认为是用来放置基本要素的地方,包括图形figure,坐标系axes和坐标系axis
基本元素primitives
primitives主要有以下几种类型,我们按照顺序介绍。
2DLines
其中常见的参数主要有:
- xdata:横坐标的取值,默认就是range(1,len(data)+1)
- ydata:纵坐标取值
- linewidth:线条的宽度
- linestyle:线型
- color:线条的颜色
- marker:点的标注样式
- markersize:标注的大小
如何设置参数属性
对于上面提到的各个参数有三种修改方法:
-
在plot函数里面进行设置
x = range(0,5) y = [2,5,7,9,11] plt.plot(x,y,linewidth = 10)
-
获取线对象,对线对象进行设置
x = range(0,5) y = [2,5,7,8,10] line, = plt.plot(x, y, '-') # 这里等号坐标的line,是一个列表解包的操作,目的是获取plt.plot返回列表中的Line2D对象,返回是一个列表类型 line.set_antialiased(False); # 关闭抗锯齿功能,调用线对象的函数
-
获取线属性,使用setp函数设置
x = range(0,5) y = [2,5,7,8,10] lines = plt.plot(x, y) plt.setp(lines, color='r', linewidth=10);
如何绘制lines
那我们常见的功能是绘制直线line,以及绘制errorbar误差折线图,下面对这两种分别进行介绍。
绘制line
可以采用两种方法来绘制直线:
1、plot方法
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]
fig,ax= plt.subplots()
ax.plot(x,y1)
ax.plot(x,y2)
print(ax.lines);
打印为:
<Axes.ArtistList of 2 lines>
可以看到创建了2个lines对象。
2、Line2D对象绘制
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]
fig,ax= plt.subplots()
lines = [Line2D(x, y1), Line2D(x, y2,color='orange')] # 显式创建Line2D对象,但是现在还没有在哪里展示
for line in lines:
ax.add_line(line) # 使用add_line方法将创建的Line2D添加到子图中,才会展示
ax.set_xlim(0,4)
ax.set_ylim(2, 11);
绘制errorbar误差折线图
是利用pyplot中的errorbar类来实现,其参数为:
- x:横坐标
- y:纵坐标
- yerr:指定在y轴水平的误差
- xerr:指定在x轴水平的误差
- fmt:指定折线图中某个点的颜色、形状、线条风格等
- ecolor:指定errorbar的颜色
- elinewidth:指定errorbar的线条宽度
那么具体的绘制方法就是将plot更改为errorbar即可:
fig = plt.figure()
x = np.arange(10)
y = 2.5 * np.sin(x / 20 * np.pi)
yerr = np.linspace(0.05, 0.2, 10)
plt.errorbar(x,y+3,yerr=yerr,fmt='o-',ecolor='r',elinewidth=2);
patches
这个类是二维图形类,它最常见的可以用来绘制矩形、多边形、楔形。
矩形
Rectangle矩形类比较简单,主要是通过xy来控制锚点,然后控制矩形的高宽即可。
最常见的矩形图是hist直方图和bar条形图
hist-直方图
其函数为plt.hist(),那么参数为:
- x:数据集,直方图将会对这个数据集进行统计
- bins:统计的区间分布,我们可以指定区间进行统计,例如按照([0,10],[11,20])区间进行统计
- range:tuplt,显示的区间
- density:是否显示频数统计结果
- histtype:可选{'bar', 'barstacked', 'step', 'stepfilled'}之一,默认为bar,step使用的是梯状,stepfilled则会对梯状内部进行填充,效果与bar类似
- align:可选{'left', 'mid', 'right'}之一,默认为'mid',控制柱状图的水平分布,left或者right,会有部分空白区域,推荐使用默认
- log:y轴是否采用指数刻度
- stacked:是否为堆积状图
x=np.random.randint(0,100,100) #生成[0-100)之间的100个数据,即 数据集
bins=np.arange(0,101,10) #设置连续的边界值,即直方图的分布区间[0,10),[10,20)...
fig = plt.figure(figsize = (6,12))
plt.subplot(311)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "left")#alpha设置透明度,0为完全透明
plt.xlabel('scores')
plt.ylabel('count')
plt.xlim(0,100); #设置x轴分布范围 plt.show()
plt.subplot(312)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "mid")
plt.subplot(313)
plt.hist(x,bins,color='fuchsia',alpha=0.5, density = True, histtype="step",
align = "right")
这里对比了一下参数align的区别:
bar-柱状图
同样,也是采用plt.bar()函数,其参数为:
- left:x轴的位置序列,一般采用range函数产生一个序列,但是有时候可以是字符串
- height:y轴的数值序列,也就是柱形图的高度,一般就是我们需要展示的数据
- alpha:透明度,值越小越透明
- width:柱形的宽度
- color或者facecolor:柱形填充的颜色
- edgecolor:柱形边缘颜色
- label:标签
y = range(1,17)
plt.bar(np.arange(16), y, alpha=0.5, width=0.5, color='yellow', edgecolor='red', label='The First Bar', lw=2);
# lw是柱形描边的线宽度
多边形
Polygon类是多边形类,其参数主要是绘制的多边形的顶点坐标。
那么这个类中最常用的是fill类,它是基于顶点坐标绘制一个填充的多边形,例如:
x = np.linspace(0, 5 * np.pi, 1000)
y1 = np.sin(x)
y2 = np.sin(2 * x)
plt.fill(x, y1, color = "g", alpha = 0.3);
楔型(饼状图)
一个楔型是以坐标xy为中心,半径r,从角度1扫到角度2。最常用是绘制饼状图plt.pie()
其参数为:
- x:楔型的形状,一维数组,可以看成是扫过角度的大小
- explode:如果不是None,那么就是一个len(x)的数组,用来指定每块的偏移
- labels:指定每个块的标签,列表或者none
- colors:指定每个块的颜色,列表或者none
- startangle:饼状图开始绘制的角度
labels = ['Frogs', 'Hogs', 'Dogs', 'Logs']
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0)
fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)
ax1.axis('equal'); # 设置axes为等高宽比,这样才能够确保画出来为圆形
collections
这个类是用来绘制一组对象的集合,那么最常见的是用来绘制散点图,即scatter方法,根据xy绘制不同大小或者颜色标记的散点图。
其主要的参数如下:
- x和y
- s:散点的尺寸大小
- c:颜色
- marker:标记类型
x = [0,2,4,6,8,10]
y = [10]*len(x)
s = [20*2**n for n in range(len(x))]
plt.scatter(x,y,s=s) ;
image
这是绘制图像的类,最常用的imshow可以根据数组绘制成图像(数值是各个像素值)。
使用imshow画图时首先需要传入一个数组,数组对应的是空间内的像素位置和像素点的值,interpolation参数可以设置不同的差值方法,可以理解为不同像素之间的处理手段:
methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']
grid = np.random.rand(4, 4)
fig, axs = plt.subplots(nrows=3, ncols=6, figsize=(9, 6),
subplot_kw={'xticks': [], 'yticks': []})
for ax, interp_method in zip(axs.flat, methods):
ax.imshow(grid, interpolation=interp_method, cmap='viridis')
ax.set_title(str(interp_method))
plt.tight_layout() # 自动调整子图使其填充整个图像
对象容器-Object container
前面我们介绍的primitives基础元素,是包含在容器里面的,当然容器还会包含它自身的属性。
Figure容器
figure是最顶层的一个容器,它包含了图中的所有元素,而一个图表的背景可以认为就是在figure中添加的一个矩形。
当我们向图表中添加add_subplot或者add_axes时,这些元素会被添加到figure.axes列表中:
fig = plt.figure()
ax1 = fig.add_subplot(211) # 作一幅2*1的图,选择第1个子图
ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3]) # 再添加一个子图位置参数,四个数分别代表了(left,bottom,width,height)
ax3 = fig.add_axes([0.2,0.1,0.3,0.4]) # 添加第三个子图
print(ax1)
print(fig.axes) # fig.axes 中包含了subplot和axes两个实例, 刚刚添加的
可以看到如果添加的子图位置重叠的可能存在的情况。而输出结果为:
AxesSubplot(0.125,0.53;0.775x0.35)
[<AxesSubplot:>, <Axes:>, <Axes:>]
figure.axes的列表中当前有三个元素,代表三个子图。
而我们可以通过figure.delaxes()来删除其中的图表,或者可以通过迭代访问列表中的元素获取子图表,再在其上做修改:
fig = plt.figure()
ax1 = fig.add_subplot(211) # 作一幅2*1的图,选择第1个子图
ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3]) # 再添加一个子图位置参数,四个数分别代表了(left,bottom,width,height)
ax3 = fig.add_axes([0.2,0.1,0.3,0.4])
print(ax1)
print(fig.axes) # fig.axes 中包含了subplot和axes两个实例, 刚刚添加的
for ax in fig.axes:
ax.grid(True)
Axes容器
Axes
是matplotlib的核心。大量的用于绘图的Artist
存放在它内部,并且它有许多辅助方法来创建和添加Artist
给它自己,而且它也有许多赋值方法来访问和修改这些Artist
。
和figure类似,axes包含一个patch属性,这个可以认为就是它的绘图区域:
fig = plt.figure()
ax = fig.add_subplot(111)
rect = ax.patch # 获取实例
rect.set_facecolor("blue")
Axes
有许多方法用于绘图,如.plot()、.text()、.hist()、.imshow()
等方法用于创建大多数常见的primitive
(如Line2D,Rectangle,Text,Image
等等)。
可以在任意区域创建Axes,通过Figure.add_axes([left,bottom,width,height])来创建一个任意区域的Axes,其中left,bottom,width,height都是[0—1]之间的浮点数,他们代表了相对于Figure的坐标。
而我们往axes里面添加图表是通过add_line和add_patch来进行添加。
另外Axes还包含两个最重要的Artist container:
ax.xaxis
:XAxis对象的实例,用于处理x轴tick以及label的绘制ax.yaxis
:YAxis对象的实例,用于处理y轴tick以及label的绘制
Axis容器
该容器用来处理跟坐标轴相关的属性,它包括坐标轴上的刻度线、刻度label、坐标网格、坐标轴标题等,而且可以独立对上下左右四个坐标轴进行处理。
可以通过下面的方法获取坐标轴的各个属性实例:
fig, ax = plt.subplots()
x = range(0,5)
y = [2,5,7,8,10]
plt.plot(x, y, '-')
axis = ax.xaxis # axis为X轴对象
axis.get_ticklocs() # 获取刻度线位置
axis.get_ticklabels() # 获取刻度label列表(一个Text实例的列表)
axis.get_ticklines() # 获取刻度线列表(一个Line2D实例的列表)
axis.get_data_interval()# 获取轴刻度间隔
axis.get_view_interval()# 获取轴视角(位置)的间隔
也可以对获取的属性进行修改,例如:
fig = plt.figure() # 创建一个新图表
rect = fig.patch # 矩形实例并将其设为黄色
rect.set_facecolor('lightgoldenrodyellow')
ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4]) # 创一个axes对象,从(0.1,0.3)的位置开始,宽和高都为0.4,
rect = ax1.patch # ax1的矩形设为灰色
rect.set_facecolor('lightslategray')
for label in ax1.xaxis.get_ticklabels():
# 调用x轴刻度标签实例,是一个text实例
label.set_color('blue') # 颜色
label.set_rotation(45) # 旋转角度
label.set_fontsize(14) # 字体大小
for line in ax1.yaxis.get_ticklines():
# 调用y轴刻度线条实例, 是一个Line2D实例
line.set_markeredgecolor('green') # 颜色
line.set_markersize(25) # marker大小
line.set_markeredgewidth(2)# marker粗细
Tick容器
它是axis下方的一个容器对象,包含了tick、grid、line实例以及对应的label。我们可以访问它的属性来获取这些实例:
Tick.tick1line
:Line2D实例Tick.tick2line
:Line2D实例Tick.gridline
:Line2D实例Tick.label1
:Text实例Tick.label2
:Text实例
y轴分为左右两个,因此tick1对应左侧的轴;tick2对应右侧的轴。
x轴分为上下两个,因此tick1对应下侧的轴;tick2对应上侧的轴。
例如我们做如下修改:
fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20))
ax.yaxis.set_tick_params(which='major', labelcolor='blue',
labelleft=False, labelright=True);
将主轴设在右边且修改其颜色。
思考题
- primitives 和 container的区别和联系是什么,分别用于控制可视化图表中的哪些要
素- 【答】:我认为container是一个容器,而primitives 是基本元素,可以理解为container是包容primitives的,例如figure,axes,axis等作为一个容器,它们可以包含很多primitives 的基础元素在其上面进行展示
- 使用提供的drug数据集,对第一列yyyy和第二列state分组求和,画出下面折线图。PA加粗标黄,其他为灰色。
import pandas as pd
df = pd.read_csv("Drugs.csv")
df.head(5)
new_df = df.groupby(["YYYY","State"]).sum()
new_df
data = new_df.reset_index().pivot(index='YYYY', columns='State', values='DrugReports')
data
data = data.reset_index()
data
因此就可以开始绘图了:
fig,ax = plt.subplots(figsize = (12,12))
ax.grid(True, color='white')
rect = ax.patch
rect.set_facecolor('#efefef')
ax.plot(data["YYYY"], data["KY"],color='#afafaf')
ax.plot(data["YYYY"], data["OH"],color='#afafaf')
ax.plot(data["YYYY"], data["PA"],color='yellow',linewidth='8')
ax.plot(data["YYYY"], data["VA"],color='#afafaf')
ax.plot(data["YYYY"], data["WV"],color='#afafaf')
ax.set_title('Evolution of PA vs other states', color='yellow', loc='left')
ax.set_xlabel('Year')
ax.set_ylabel('DrugReports')
- 分别用一组长方形柱和填充面积的方式模仿画出下图,函数 y = -1 * (x - 2) * (x - 8) +10 在区间[2,9]的积分面积
import numpy as np
x = np.linspace(0,10)
y = -1 * (x - 2) * (x - 8) + 10
fig,ax = plt.subplots(2,1,figsize = (8,12))
x_bar = np.linspace(2,9)
y_bar = -1 * (x_bar - 2) * (x_bar - 8) + 10
y_bar_button = y_bar * 0
ax[0].plot(x,y,color="red")
ax[1].plot(x,y,color="red")
ax[0].bar(x_bar, y_bar,width=0.1, color='lightgray')
ax[1].bar(x_bar, y_bar, width = 0.1, color='lightgray')
ax[0].set_ylim((0,20))
ax[1].set_ylim((0,20))
ax[1].fill_between(x_bar, y_bar, y_bar_button, color="lightgray")
布局格式定方圆
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.subplots()绘制均匀状态下的子图
该函数的返回分别是画布和子图构成的列表,传入的参数为行、列、第几个子图,figsize用来指定画布的大小,sharex和sharey用来表示是否共享横轴和纵轴刻度,tight_layout用来调整子图的相对大小使字符不重叠:
fig, axs = plt.subplots(2,5, figsize = (10,4), sharex = True, sharey = True)
fig.suptitle("样例1",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('第%d行,第%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('横坐标')
if j==0: axs[i][j].set_ylabel('纵坐标')
fig.tight_layout()
前面是利用subplots(注意加了s)显式的创建多个对象,然后一一进行画图;我们还可以通过plt和subplot(注意没加s),每次在指定位置创建子图,创建后当前的绘制都会指向该子图:
plt.figure()
# 子图1
plt.subplot(2,2,1)
plt.plot([1,2], 'r')
# 子图2
plt.subplot(2,2,2)
plt.plot([1,2], 'b')
#子图3
plt.subplot(224) # 当三位数都小于10时,可以省略中间的逗号,这行命令等价于plt.subplot(2,2,4)
plt.plot([1,2], 'g');
除了常规的直角坐标系,还可以用projection方法创建极坐标系下的图表:
N = 300
r = 2 * np.random.rand(N)
theta = 2 * np.pi * np.random.rand(N)
area = 50 * r**2
colors = theta
plt.subplot(projection='polar')
plt.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75);
练一练
请思考如何用极坐标系画出类似的玫瑰图
fig = plt.figure(figsize = (8,12))
ax = plt.subplot(projection = "polar")
x = np.arange(100,1000, 20) # 间隔为20
y = np.linspace(0,np.pi*2, len(x))
ax.set_theta_direction(-1) # 设置极坐标的方向为顺时针,1为逆时针
ax.set_theta_zero_location('N') # 设置开始画的方位,有8个方位
ax.bar(y, x, width = 0.15,color=np.random.random((len(r), 3)))
plt.tight_layout()
主要就是set_theta_direction和set_theta_zero_location两个函数调整图像。
使用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])
fig.suptitle('样例2', 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('第%d行,第%d列'%(i+1,j+1))
if i==1: ax.set_xlabel('横坐标')
if j==0: ax.set_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])
fig.suptitle('样例3', size=20)
# sub1
ax = fig.add_subplot(spec[0, :3]) # 高度取第一个,宽度前三个都要了,就是1,7.5
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub2
ax = fig.add_subplot(spec[0, 3:5]) # 1,1+1.5
ax.scatter(np.random.randn(10), np.random.randn(10))
# sub3
ax = fig.add_subplot(spec[:, 5])
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()
子图上的方法
补充一些子图上的常用方法。
常用来画直线的方法为axhline, axvline, axline
(水平、垂直、任意方向)
fig, ax = plt.subplots(figsize=(4,3))
ax.axhline(0.5,0.1,0.8, color = 'red')
# 第一个参数为水平y等于多少,第二个为xmin,第三个为xmax,都是浮点数代表坐标轴占百分比
ax.axvline(0.5,0.2,0.8, color = "blue")
ax.axline([0.3,0.3],[0.7,0.7], color = "green");
利用grid可以添加灰色网格:
fig, ax = plt.subplots(figsize=(4,3))
ax.grid(True)
使用set_xscale或者set_yscale可以设置坐标轴的刻度:
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
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')
else:
pass
fig.tight_layout()
思考题
- 墨尔本1981年至1990年的每月温度情况
data = pd.read_csv("layout_ex1.csv")
data["Time"] = pd.to_datetime(data["Time"])
data["year_num"] = data["Time"].apply(lambda x: x.year)
fig, ax = plt.subplots(2, 5, figsize = (20,4))
fig.suptitle('墨尔本1981年至1990年月温度曲线',size=20,y=1.1)
for i in range(2):
for j in range(5):
tem = data[data["year_num"] == j+1981+i*5]["Temperature"]
x = np.arange(0,12)
ax[i][j].plot(x,tem,marker = "o",color='b')
ax[i][j].set_title(str(j+1981 + i*5 ) + "年")
if( j == 0):
ax[i][j].set_ylabel("气温")
plt.tight_layout()
- 用
np.random.randn(2, 150)
生成一组二维数据,使用两种非均匀子图的分割方法,做出该数据对应的散点图和边际分布图
data = np.random.randn(2,150)
fig = plt.figure(figsize = (12,12))
spec = fig.add_gridspec(nrows = 2, ncols = 2,width_ratios = [3,1],height_ratios=[1,3])
ax = fig.add_subplot(spec[0,0])
ax.hist(data[0,:],color = "blue",width = 0.4)
ax.axis("off")
ax2 = fig.add_subplot(spec[1,1])
ax2.hist(data[1,:], orientation='horizontal',color = "blue",rwidth = 0.8)
# 第二个参数设置为在y上面
ax2.axis("off")
ax3 = fig.add_subplot(spec[1,0])
ax3.scatter(data[0,:],data[1,:],color = "blue")
ax3.grid(True)
ax3.set_ylabel("my_data_y")
ax3.set_xlabel("my_data_x")
plt.tight_layout()
文字图例尽眉目
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
import datetime
Figure和Axes上的文本
文本API示例
下面这些命令是通过pyplot API和ooAPI分别创建文本的方式:
fig = plt.figure()
ax = fig.add_subplot()
# 设置x和y轴标签
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
# 设置x和y轴显示范围均为0到10
ax.axis([0, 10, 0, 10])
ax.text(3, 8, 'boxed italics text in data coords', style='italic',
bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})
# 在画布上添加文本,一般在子图上添加文本是更常见的操作,这种方法很少用
fig.text(0.4,0.8,'This is text for figure')
ax.plot([2], [1], 'o')
# 添加注解
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),
arrowprops=dict(facecolor='black', shrink=0.05));
text-子图上的文本
其调用方法为axes.text()。那么其参数为:
- x,y:文本出现的位置
- s:文本的内容
- fontdict:可选参数,用来调整文本的属性
重点解释下fontdict和**kwargs参数,这两种方式都可以用于调整呈现的文本样式,最终效果是一样的,不仅text方法,其他文本方法如set_xlabel,set_title等同样适用这两种方式修改样式。通过一个例子演示这两种方法是如何使用的。
fig = plt.figure(figsize = (10,3))
axes = fig.subplots(1,2)
axes[0].text(0.3,0.8, "modift by **kwargs", style="italic",
bbox = {"facecolor":"red", "alpha":0.5, "pad": 10})
font = {"bbox": {"facecolor":"red", "alpha":0.5, "pad": 10},
"style":"italic"}
axes[1].text(0.3,0.8, "modify by fontdict", fontdict = font)
那么这些样式常用的参数如下:
xlabel和ylabel
其调用方法为axes.set_xlabel和axes.set_ylabel
其参数为:
- xlabel:标签内容
- fontdict和之前一样
- **kwargs也和之前一样
- labelpad:标签和坐标轴之间的距离
- loc:标签位置,可选为"left","center","right"
在**kwargs中有另外的参数可以调整标签的位置等信息,下面来观察他们的区别:
fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,2)
axes[0].set_xlabel('xlabel',labelpad=20,loc='left')
# loc参数仅能提供粗略的位置调整,如果想要更精确的设置标签的位置,可以使用position参数+horizontalalignment参数来定位
# position由一个元组过程,第一个元素0.2表示x轴标签在x轴的位置,第二个元素对于xlabel其实是无意义的,随便填一个数都可以
# horizontalalignment='left'表示左对齐,这样设置后x轴标签就能精确定位在x=0.2的位置处
axes[1].set_xlabel('xlabel', position=(0.2, _), horizontalalignment='left');
title和suptitle-子图和画布的标题
title调用方法为axes.set_title(),其参数为:
- label:标签内容
- fontdict,loc,**kwargs和之前一样
- pad:标题偏离图表顶部的位置
- y:title所在子图垂向的位置,默认在子图的顶部
suptitle的调用为figure.suptitle()。
下面查看pad和y的影响:
fig = plt.figure(figsize=(10,3))
fig.suptitle('This is figure title',y=1.2) # 通过参数y设置高度
axes = fig.subplots(1,2)
axes[0].set_title('This is title,pad = 15',pad=15)
axes[1].set_title('This is title,pad = 6',pad=6);
fig = plt.figure(figsize=(10,3))
fig.suptitle('This is figure title2',y=1)
axes = fig.subplots(1,2)
axes[0].set_title('This is title,y = 1',y = 1)
axes[1].set_title('This is title,y = 1.2',y = 1.2);
可以看到两者其实就是控制标题与图的距离而已。
annotate-子图的注解
调用方式为axes.annotate(),其参数为:
- text:注解的内容
- xy:注解箭头指向的位置
- xytext:注解文字的坐标
- xycoords:用来定义xy参数的坐标系
- textcoords:用来定义xytext参数的坐标系
- arrowprops:用来定义指向箭头的样式
其参数特别多样化,这里只是举个例子:
fig = plt.figure()
ax = fig.add_subplot()
ax.annotate("annotate1",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0.2")
);
字体的属性设置
字体设置一般有全局字体设置和自定义局部字体设置两种方法。
为了方便在图中加入合适的字体,可以尝试了解中文字体的英文名称,此链接中就有常用的中文字体的英文名
#该block讲述如何在matplotlib里面,修改字体默认属性,完成全局字体的更改。
plt.rcParams['font.sans-serif'] = ['SimSun'] # 指定默认字体为新宋体。
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像时 负号'-' 显示为方块和报错的问题。
#局部字体的修改方法1
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(x, label='小示例图标签')
# 直接用字体的名字
plt.xlabel('x 轴名称参数', fontproperties='Microsoft YaHei', fontsize=16) # 设置x轴名称,采用微软雅黑字体
plt.ylabel('y 轴名称参数', fontproperties='Microsoft YaHei', fontsize=14) # 设置Y轴名称
plt.title('坐标系的标题', fontproperties='Microsoft YaHei', fontsize=20) # 设置坐标系标题的字体
plt.legend(loc='lower right', prop={"family": 'Microsoft YaHei'}, fontsize=10) ; # 小示例图的字体设置
tick上的文本
设置tick(刻度)和ticklabel(刻度标签)也是可视化中经常需要操作的步骤,matplotlib既提供了自动生成刻度和刻度标签的模式(默认状态),同时也提供了许多灵活设置的方式。
简单模式
直接使用axis.set_ticks设置标签位置,使用axis.set_ticklabels设置标签格式:
x1 = np.linspace(0.0, 5.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
axs[1].xaxis.set_ticks(np.arange(0., 10.1, 2.));
可以自动设置相对来说会好一点(上图)
fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
ticks = np.arange(0., 8.1, 2.)
tickla = [f'{tick:1.2f}' for tick in ticks]
axs[1].xaxis.set_ticks(ticks)
axs[1].xaxis.set_ticklabels(tickla);
我们通常设置tick都是要与数值的范围匹配, 然后再设置ticklabel为我们想要的类型,如下:
fig, axs = plt.subplots(2, 1, figsize=(6, 4), tight_layout=True)
x1 = np.linspace(0.0, 6.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
axs[0].plot(x1, y1)
axs[0].set_xticks([0,1,2,3,4,5,6])
axs[1].plot(x1, y1)
axs[1].set_xticks([0,1,2,3,4,5,6])#要将x轴的刻度放在数据范围中的哪些位置
axs[1].set_xticklabels(['zero','one', 'two', 'three', 'four', 'five','six'],#设置刻度对应的标签
rotation=30, fontsize='small')#rotation选项设定x刻度标签倾斜30度。
axs[1].xaxis.set_ticks_position('top')
#set_ticks_position()方法是用来设置刻度所在的位置,常用的参数有bottom、top、both、none
print(axs[1].xaxis.get_ticklines());
上方的例子就是位置在bottom,下方就是在top,both就是上下都有,none就是都没有。
Tick Lacators and Formatters
除了上述的简单模式以外,还可以通过Axis.set_major_locator和Axis.set_minor_locator方法用来设置标签的位置,Axis.set_major_formatter和Axis.set_minor_formatter方法用来设置标签的格式。这种方式的好处是不用显式地列举出刻度值列表。
set_major_formatter和set_minor_formatter这两个formatter格式命令可以接收字符串格式(matplotlib.ticker.StrMethodFormatter)或函数参数(matplotlib.ticker.FuncFormatter)来设置刻度值的格式 。
这部分的内容比较推荐用到的时候再去查。
Tick Formatters
接受字符串:
fig, axs = plt.subplots(2, 2, figsize=(12, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):
ax.plot(x1*10., y1)
formatter = matplotlib.ticker.FormatStrFormatter('%1.1f')
axs[0, 1].xaxis.set_major_formatter(formatter)
formatter = matplotlib.ticker.FormatStrFormatter('-%1.1f')
axs[1, 0].xaxis.set_major_formatter(formatter)
formatter = matplotlib.ticker.FormatStrFormatter('%1.5f')
axs[1, 1].xaxis.set_major_formatter(formatter);
接受函数:
def formatoddticks(x, pos):
if x % 2:
return f'{x:1.2f}'
else:
return ''
fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.plot(x1, y1)
ax.xaxis.set_major_formatter(formatoddticks);
Tick Locators
这个实现更复杂的操作:
fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):
ax.plot(x1*10., y1)
locator = matplotlib.ticker.AutoLocator()
axs[0, 0].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.MaxNLocator(nbins=3)
axs[0, 1].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.MultipleLocator(5)
axs[1, 0].xaxis.set_major_locator(locator)
locator = matplotlib.ticker.FixedLocator([0,7,14,21,28])
axs[1, 1].xaxis.set_major_locator(locator);
# 特殊的日期型locator和formatter
locator = mdates.DayLocator(bymonthday=[1,15,25])
formatter = mdates.DateFormatter('%b %d')
fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
base = datetime.datetime(2017, 1, 1, 0, 0, 1)
time = [base + datetime.timedelta(days=x) for x in range(len(x1))]
ax.plot(time, y1)
ax.tick_params(axis='x', rotation=70);
legend图例
在学习legend之前需要先学习几个术语:
- legend entry(图例条目):每个图例都有一个或者多个条目组成,一个条目包含一个key和对应的label,例如图中三条曲线需要标注,那么就是3个条目
- legend key(图例键):每个legend label左边的标记,指明是哪条曲线
- legend label(图例标签):描述文本
- legend handle(图例句柄):用于在图例中生成适当图例条目的原始对象
以下图为例,右侧的方框中的共有两个legend entry;两个legend key,分别是一个蓝色和一个黄色的legend key;两个legend label,一个名为‘Line up’和一个名为‘Line Down’的legend label
图例的绘制同样有OO模式和pyplot模式两种方式,写法都是一样的,使用legend()即可调用。
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles = [line_up, line_down], labels = ['Line Up', 'Line Down']);
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend()
而设置图例的位置,可以通过设置loc参数的值来设置,其有10个位置可以选择,每个都有字符串的形式和对应的数字形式:
Location String | Location Code |
---|---|
best | 0 |
upper right | 1 |
upper left | 2 |
lower left | 3 |
lower right | 4 |
right | 5 |
center left | 6 |
center right | 7 |
lower center | 8 |
upper center | 9 |
center | 10 |
fig,axes = plt.subplots(2,5,figsize=(15,5))
for i in range(2):
for j in range(5):
axes[i][j].plot([0.5],[0.5])
axes[i][j].legend(labels='a',loc=i*5+j) # 观察loc参数传入不同值时图例的位置
fig.tight_layout()
还可以设置图例的边框和背景:
fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,3)
for i, ax in enumerate(axes):
ax.plot([1,2,3],label=f'ax {i}')
axes[0].legend(frameon=False) #去掉图例边框
axes[1].legend(edgecolor='blue') #设置图例边框颜色
axes[2].legend(facecolor='gray'); #设置图例背景颜色,若无边框,参数无效
也可以为图例加上标题:
fig,ax =plt.subplots()
ax.plot([1,2,3],label='label')
ax.legend(title='legend title');
思考题
尝试使用两种方式模仿画出下面的图表(重点是柱状图上的标签),本文学习的text方法和matplotlib自带的柱状图标签方法bar_label
第一种:
label = ["Jim","Slim","Harry","Dick","Tom"]
y = [4,7,6,8,10]
error = np.random.rand(len(y)).round(2) #误差
fig,ax = plt.subplots()
ax.set_title("How fast do you want to go today?")
ax.set_xlim(0,15)
for i in range(0, len(y)):
ax.text(y[i] + error[i]+1, label[i], '±' + str(error[i]), fontsize=10,horizontalalignment='center',color='blue')
ax.set_xlabel('performance')
ax.barh(label, y, color = 'blue',xerr = error)
# barh有一个参数为xerr就是来画误差线的
label = ["Jim","Slim","Harry","Dick","Tom"]
y = [4,7,6,8,10]
error = np.random.rand(len(y)).round(2) #误差
fig,ax = plt.subplots()
ax.set_title("How fast do you want to go today?")
ax.set_xlim(0,15)
ax.set_xlabel('performance')
b = ax.barh(label, y, color = 'blue',xerr = error)
plt.bar_label(b, ["±"+str(i) for i in error])
样式色彩秀芳华
第五回详细介绍matplotlib中样式和颜色的使用
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
matplotlib的绘图样式(style)
设置样式最简单就是在绘制每一个元素时在参数中设定对应的样式,不过也可以用方法来批量修改全局的样式。
matplotlib预先定义样式
只需要在python脚步最开始时输入想使用的style的名称就可以调用,那么我们可以查看有哪些方式方便使用:
print(plt.style.available)
['Solarize_Light2', '_classic_test_patch', '_mpl-gallery', '_mpl-gallery-nogrid', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark', 'seaborn-dark-palette', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'tableau-colorblind10']
那么使用方法例如:
plt.style.use('ggplot')
plt.plot([1,2,3,4],[2,3,4,5]);
用户自定义stylesheet
在任意路径下创建一个后缀名为mplstyle的样式清单,编辑文件添加以下样式内容:
axes.titlesize : 24
axes.labelsize : 20
lines.linewidth : 3
lines.markersize : 10
xtick.labelsize : 16
ytick.labelsize : 16
引用自定义stylesheet后观察图表变化:
plt.style.use('style1.mplstyle')
plt.plot([1,2,3,4],[2,3,4,5]);
值得特别注意的是,matplotlib支持混合样式的引用,只需在引用时输入一个样式列表,若是几个样式中涉及到同一个参数,右边的样式表会覆盖左边的值:
plt.style.use(['dark_background', 'style1.mplstyle'])
plt.plot([1,2,3,4],[2,3,4,5]);
设置rcparams
还可以通过修改默认rc设置的方式改变样式,所有rc设置都保存在一个叫做 matplotlib.rcParams的变量中。修改过后再绘图,可以看到绘图样式发生了变化。
plt.style.use('default') # 恢复到默认样式
mpl.rcParams['lines.linewidth'] = 2
mpl.rcParams['lines.linestyle'] = '--'
plt.plot([1,2,3,4],[2,3,4,5]);
另外matplotlib也还提供了一种更便捷的修改样式方式,可以一次性修改多个样式。
mpl.rc('lines', linewidth=4, linestyle='-.')
matplotlib的色彩设置color
在matplotlib中,设置颜色有以下几种方式
RGB或者RGBA
plt.plot([1,2,3],[4,5,6],color=(0.1, 0.2, 0.5))
plt.plot([4,5,6],[1,2,3],color=(0.1, 0.2, 0.5, 0.5));
颜色用[0,1]之间的浮点数表示,四个分量按顺序分别为(red, green, blue, alpha),其中alpha透明度可省略。
HEX RGB或者RGBA
# 用十六进制颜色码表示,同样最后两位表示透明度,可省略
plt.plot([1,2,3],[4,5,6],color='#0f0f0f')
plt.plot([4,5,6],[1,2,3],color='#0f0f0f80');
灰度色阶
# 当只有一个位于[0,1]的值时,表示灰度色阶
plt.plot([1,2,3],[4,5,6],color='0.5');
单字符基本颜色
八个基本颜色可以用单个字符来表示,分别是'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w',对应的是blue, green, red, cyan, magenta, yellow, black, and white的英文缩写,设置color='m'即可。
颜色名称
matplotlib提供了颜色对照表,可供查询颜色对应的名称
用colormap设置一组颜色
具体可以阅读这篇文章。
x = np.random.randn(50)
y = np.random.randn(50)
plt.scatter(x,y,c=x,cmap='RdYlBu');