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,那么会自动创建一个,因此可以简化。
通常,一个完成的matplotlib图像会包括四个层级(容器):
matplotlib提供了两种最常用的绘图接口:
就像是上小节所展示的那样两种创建图的方法。
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() ;
思考题
请思考两种绘图模式的优缺点和各自适合的使用场景
在第五节绘图模板中我们是以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的原理或者说基础逻辑是,用Artist对象在画布(canvas)上绘制(Render)图形。因此跟人作画类似,需要三个步骤:
因此可以认为matplotlib有三层的API:
matplotlib.backend_bases.FigureCanvas
代表了绘图区,所有的图像都是在绘图区完成的matplotlib.backend_bases.Renderer
代表了渲染器,可以近似理解为画笔,控制如何在 FigureCanvas 上画图。matplotlib.artist.Artist
代表了具体的图表组件,即调用了Renderer的接口在Canvas上作图。因此我们大部分是利用Artist类来进行绘图。
Artist有两种类型:primitives
和containers
:
primitives主要有以下几种类型,我们按照顺序介绍。
其中常见的参数主要有:
对于上面提到的各个参数有三种修改方法:
在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);
那我们常见的功能是绘制直线line,以及绘制errorbar误差折线图,下面对这两种分别进行介绍。
可以采用两种方法来绘制直线:
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);
是利用pyplot中的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);
这个类是二维图形类,它最常见的可以用来绘制矩形、多边形、楔形。
Rectangle矩形类比较简单,主要是通过xy来控制锚点,然后控制矩形的高宽即可。
最常见的矩形图是hist直方图和bar条形图
其函数为plt.hist(),那么参数为:
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的区别:
同样,也是采用plt.bar()函数,其参数为:
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()
其参数为:
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为等高宽比,这样才能够确保画出来为圆形
这个类是用来绘制一组对象的集合,那么最常见的是用来绘制散点图,即scatter方法,根据xy绘制不同大小或者颜色标记的散点图。
其主要的参数如下:
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) ;
这是绘制图像的类,最常用的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() # 自动调整子图使其填充整个图像
前面我们介绍的primitives基础元素,是包含在容器里面的,当然容器还会包含它自身的属性。
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
是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的绘制该容器用来处理跟坐标轴相关的属性,它包括坐标轴上的刻度线、刻度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粗细
它是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);
将主轴设在右边且修改其颜色。
思考题
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')
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 #用来正常显示负号
该函数的返回分别是画布和子图构成的列表,传入的参数为行、列、第几个子图,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两个函数调整图像。
所谓非均匀包含两层含义,第一是指图的比例大小不同但没有跨行或跨列,第二是指图为跨列或跨行状态
利用 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()
思考题
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
下面这些命令是通过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));
其调用方法为axes.text()。那么其参数为:
重点解释下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)
那么这些样式常用的参数如下:
其调用方法为axes.set_xlabel和axes.set_ylabel
其参数为:
在**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调用方法为axes.set_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);
可以看到两者其实就是控制标题与图的距离而已。
调用方式为axes.annotate(),其参数为:
其参数特别多样化,这里只是举个例子:
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(刻度)和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就是都没有。
除了上述的简单模式以外,还可以通过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)来设置刻度值的格式 。
这部分的内容比较推荐用到的时候再去查。
接受字符串:
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);
这个实现更复杂的操作:
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 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
设置样式最简单就是在绘制每一个元素时在参数中设定对应的样式,不过也可以用方法来批量修改全局的样式。
只需要在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]);
在任意路径下创建一个后缀名为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]);
还可以通过修改默认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中,设置颜色有以下几种方式
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透明度可省略。
# 用十六进制颜色码表示,同样最后两位表示透明度,可省略
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提供了颜色对照表,可供查询颜色对应的名称
具体可以阅读这篇文章。
x = np.random.randn(50)
y = np.random.randn(50)
plt.scatter(x,y,c=x,cmap='RdYlBu');