Copyright ©2022 Zhang Tongshuai

Python可视化库——matplotlib¶



张统帅 清华大学¶

2020.06.20¶

Python可视化简介¶

  • Python是一种面向对象的解释型计算机程序设计语言
  • “胶水语言”,能够把用其他语言制作的各种模块联结在一起
  • Python语言也有一系列的数据可视化包(packages),包括
    • Pandas
    • Matplotlib
    • Seaborn
    • Bokeh
    • plotly
    • plotnine
    • ...

关于matplotlib¶

  • 目的:提供MATLAB之外的选择
  • matlab是Python可视化的基本库
  • 主要用于2D绘图,支持部分3D
  • 最强大、最流行、可扩展

Matplotlib设计思想¶

  • 在一张Figure中包含并且管理图的所有元素
  • 架构设计:将 Figure与渲染的过程相分离

Matplotlib架构层¶

  • Scripting (脚本)层
    • pyplot,类似matlab的绘图api
    • pylab (deprecated)
  • Artist (表现)层
    • 图中的所有元素,标题、轴标签、刻度……
    • 知道如何在画布上绘制“自己”
  • Backend (后端)层
    • 提供通用绘图接口(draw_point ……)
    • 与“物理”世界进行交互(键盘、鼠标)
  • Scripting 访问 Artist, Artist 访问 Backend

matplotlib接口¶

pyplot 面向对象的API
优点 命名空间 完全控制
缺点 控制不足 使用复杂
适用场景
  • 交互式数据探索
  • 小规模可视化
  • 嵌入式绘图
  • 大规模可视化

use-the-matplotlib

画一张图需要几步?¶

matplotlib_vs_human

FigureCanvas 帮你确定画图的地方

Renderer 帮你把想画的东西展示在屏幕上

Artist 帮你用 Renderer 在 Canvas 上画图

95% 的用户 (我们这些凡人) 只需用 Artist 就能自由的在电脑上画图了。

matplotlib作图剖析¶

Artist包含两类元素:

  • 基础(primitives)类
    • 线(line)
    • 点(marker)
    • 文字(text)
    • 图例(legend)
    • ....
  • 容器(containers)类
    • 图(figure)
    • 坐标系(axes)
    • 坐标轴(axis)
    • 刻度(tick)
In [7]:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
plt.show()
print( 'fig.axes:', fig.axes)
fig.axes: [<matplotlib.axes._subplots.AxesSubplot object at 0x00000256491F4CD0>]
In [8]:
xax = ax.xaxis
yax = ax.yaxis
print( 'ax.xaxis:', xax )
print( 'ax.yaxis:', yax, '\n' )
print( 'ax.xaxis.majorTicks:', xax.majorTicks, '\n' )
print( 'ax.xaxis.minorTicks:', xax.minorTicks )
print( 'ax.yaxis.minorTicks:', yax.minorTicks )
ax.xaxis: XAxis(54.000000,36.000000)
ax.yaxis: YAxis(54.000000,36.000000) 

ax.xaxis.majorTicks: [<matplotlib.axis.XTick object at 0x000002564A931B80>, <matplotlib.axis.XTick object at 0x000002564CEBE6A0>, <matplotlib.axis.XTick object at 0x000002564BFA31C0>, <matplotlib.axis.XTick object at 0x000002564CEBEA90>, <matplotlib.axis.XTick object at 0x000002564CEBECD0>, <matplotlib.axis.XTick object at 0x000002564BFD72B0>] 

ax.xaxis.minorTicks: [<matplotlib.axis.XTick object at 0x000002564BFDA610>]
ax.yaxis.minorTicks: [<matplotlib.axis.YTick object at 0x0000025649220160>]

选择四大容器¶

图(Figure) → 坐标系(Axes) → 坐标轴(Axis) → 刻度(Ticks)

创造完以上四个容器元素后,我们可在上面添加各种基础元素,比如:

  • 在坐标轴和刻度上添加标签

  • 在坐标系中添加线、点、网格、图例和文字

  • 在图中添加图例

添加基础元素¶

要画出一幅有内容的图,需要在容器里添加基础元素比如线 (line), 点(marker), 文字 (text), 图例 (legend), 网格 (grid), 标题 (title), 图片 (image) 等,具体来说

  • 画一条线,用 plt.plot() 或 ax.plot()
  • 画个记号,用 plt.scatter() 或 ax.scatter()

  • 添加文字,用 plt.text() 或 ax.text()

  • 添加图例,用 plt.legend() 或 ax.legend()

  • 添加图片,用 plt.imshow() 或 ax.imshow()

线图(Line plot)¶

使用matplotlib.pyplot中的plot()

In [2]:
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 10, 0.1)
y = np.sin(x)
# 根据数据绘图
plt.plot(x,y)
plt.show()

散点图(Scatter)¶

使用scatter()

In [ ]:
x = [1,2,3,4]
y = [1,4,9,16]
plt.scatter(x,y)
plt.show()
In [76]:
plt.scatter(x,y)
plt.show()

柱状图(Bar)¶

使用bar()

In [60]:
N = 20
x = np.arange(0,N)
y =  x ** 2

plt.bar(x, y)
plt.show()

极坐标图¶

在调用subplot()创建子图时通过设置projection='polar'

In [62]:
theta = np.linspace(0, 2 * np.pi, 20, endpoint = False)
radii = sorted(y,reverse=True)
width = np.pi / 10

ax = plt.subplot(111, projection = 'polar')
bars = ax.bar(theta, y, width = width, bottom = 0.0)

饼图(Pie)¶

使用pie()

In [89]:
x = np.random.randint(1, 10, 4)
print(x)
plt.pie(x, startangle=90, counterclock=False)
plt.show()
[5 9 7 6]

堆叠图¶

使用stackplot()

In [4]:
x = [1, 2, 3, 4, 5]
y1 = [1, 1, 2, 3, 5]
y2 = [0, 4, 2, 6, 8]
y3 = [1, 3, 5, 7, 9]

labels = ["Fibonacci ", "Evens", "Odds"]

fig, ax = plt.subplots()
ax.stackplot(x, y1, y2, y3, labels=labels)
ax.legend(loc='upper left')
plt.show()

热力图¶

调用imshow()

In [160]:
X = np.random.randint(1,10,(10,10))
plt.imshow(X)
plt.colorbar()
plt.show()

直方图(Histogram)¶

使用hist()

In [71]:
# 产生正态分布
mu, sigma = 0, 1
s = np.random.normal(loc=0, scale=1, size=1000)
# 绘制直方图
count, bins, ignored = plt.hist(s, 30, density=True)
# 绘制概率密度图
plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) * np.exp( - (bins - mu)**2 / (2 * sigma**2) ), linewidth=2, color='r')
plt.show()

箱型图(Box)¶

使用boxplot()

In [26]:
s = np.random.normal(loc=0, scale=1, size=100)

plt.boxplot(s)
plt.show()

小提琴图¶

使用violinplot()

In [27]:
ax = plt.violinplot(s,
               showmeans=True,
               showmedians=True)
print(ax.keys())
ax['cmeans'].set_color('orange')
plt.show()
dict_keys(['bodies', 'cmeans', 'cmaxes', 'cmins', 'cbars', 'cmedians'])

等高线图¶

使用contour()

In [7]:
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)

# 使用np.meshgrid,将 X 和 Y 编织成栅格
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

fig, ax = plt.subplots()
# 绘制Z的等值图
CS = ax.contour(X, Y, Z)
ax.clabel(CS, inline=1, fontsize=10)
plt.show()

3D图¶

使用matplotlib.pyplot和mpl_toolkits.mplot3d模块

In [115]:
# 导入 Axes 3D
from mpl_toolkits.mplot3d import Axes3D

# 将图像加载在Figure对象,使用Axes3D添加坐标轴
fig = plt.figure()
ax = Axes3D(fig)

# 使用np.meshgrid,将 X 和 Y 编织成栅格
# X, Y value
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X, Y)    # x-y 平面的网格
R = np.sqrt(X ** 2 + Y ** 2)
# height value
Z = np.sin(R)

# 使用Axes 3D的plot_surface()绘制
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('rainbow')) 

# 添加XY平面的投影
ax.contourf(X, Y, Z, zdir='z', offset=-2, cmap=plt.get_cmap('rainbow'))
ax.set_zlim(-2,1)
plt.show()

设置颜色¶

  • 简单颜色
  • 灰度
  • 十六进制代码
  • RGB
In [43]:
# 2. 使用numpy创建数据
x = np.arange(0, 10, 0.1)
y = np.sin(x)
# 3. 根据数据绘图
plt.plot(x,y, color='b') 
plt.plot(x,y+1,color='0.5') #灰度
plt.plot(x,y+2,color='#0FEC8B') #十六进制颜色表示
plt.plot(x,y+3,color=(0.6, 0.1, 0.5)) # RGB
plt.show()

设置线型/标记¶

设置linestyle和marker

In [51]:
plt.plot(y, linestyle='dashed',marker='*')
plt.show()

FMT参数设置¶

[fmt] 是一个字符串来定义图的基本属性如:颜色(color),点型(marker),线型(linestyle),具体形式:

fmt = '[color][marker][line]'

若属性用的是全名则不能用fmt参数来组合赋值,应该用关键字参数对单个属性赋值

In [63]:
plt.plot(x, y, 'go-')  # 绿色圆点实线
plt.show()

设置标题¶

In [9]:
import matplotlib.pyplot as plt
#括号当中输入标题的名称
plt.title("my title")
plt.show()

试一下中文?¶

In [10]:
#设置rc参数显示中文标题
#设置字体为SimHei显示中文
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.title("我的标题")
plt.show()

坐标轴及标签¶

In [11]:
plt.xlim(0,6) #x轴坐标轴
plt.ylim((0, 3))#y轴坐标轴
plt.xlabel('X坐标')#x轴标签
plt.ylabel('Y坐标')#y轴标签
plt.show()

试一下负数?¶

In [12]:
plt.rcParams['axes.unicode_minus']=False
plt.ylim((-3, 3))#y轴坐标轴
plt.show()

设置标签和图例¶

In [13]:
plt.plot(2, 3, '*',label="star")#第一个label
plt.plot(1, 3* 2, 'o',label="dot")#第二个label
plt.legend(loc='best')#图列位置,可选best,center等
plt.show()

添加注释¶

有时候我们需要对特定的点进行标注,我们可以使用 plt.annotate 函数来实现:

  • s: 注释信息内容
  • xy:箭头点所在的坐标位置
  • xytext:注释内容的坐标位置
  • arrowprops:设置指向箭头的参数
In [15]:
import numpy as np
x=np.linspace(0,10,200)#从0到10之间等距产生200个值
y=np.sin(x)

plt.plot(x,y,linestyle=':',color='b')
plt.annotate(s='标记点',xy=(3,np.sin(3)),
             xytext=(4,-0.5),weight='bold',color='b',
             arrowprops=dict(arrowstyle='-|>',color='k'))
plt.show()

使用子图¶

使用subplot()将多张子图展示在一起:

  • 在调用 plot()函数之前需要先调用 subplot() 函数
  • 第一个参数代表子图的总行数
  • 第二个参数代表子图的总列数
  • 第三个参数代表当前激活的子图
In [16]:
ax1 = plt.subplot(2, 2, 1)
plt.plot(x,np.sin(x), 'k')

ax2 = plt.subplot(2, 2, 2, sharey=ax1) # 与 ax1 共享y轴
plt.plot(x, np.cos(x), 'g')

ax3 = plt.subplot(2, 2, 3)
plt.plot(x,x, 'r')

ax4 = plt.subplot(2, 2, 4, sharey=ax3) # 与 ax3 共享y轴
plt.plot(x, 2*x, 'y')
plt.show()

backend设置¶

In [46]:
i_bk = matplotlib.rcsetup.interactive_bk # 获取 interactive backend
n_i_bk = matplotlib.rcsetup.non_interactive_bk # 获取 non-interactive backend
all_bk = matplotlib.rcsetup.all_backends # 获取 所有 backend
print(i_bk)
print(n_i_bk)
# 查看当前的backend
print(matplotlib.get_backend())
['GTK3Agg', 'GTK3Cairo', 'MacOSX', 'nbAgg', 'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', 'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo']
['agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg', 'template']
Qt5Agg

切换backend¶

In [57]:
import matplotlib 
import matplotlib.pyplot as plt

matplotlib.use('Qt5Agg')
%matplotlib qt5
print(matplotlib.get_backend())

# plt.switch_backend('tkagg') 在Jupyter下试试?
%matplotlib inline
print (matplotlib.rcParams['backend']) 
Qt5Agg
module://ipykernel.pylab.backend_inline

保存图片¶

使用'savefig()'

In [23]:
fig.savefig('image.png', dpi=300)
image = plt.imread('image.png')
plt.figure(figsize = (20,16))
plt.imshow(image)
plt.show()

总结¶

  • matplotlib画图可以总结为3个步骤:
    • 获取数据
    • 画出基本图形
    • 设置细节
  • matplotlib所提供的图形非常丰富,可以自由组合
  • matplotlib提供精细的图像表达,通过细节设置丰富效果
  • 不足:
    • 古老
    • 不够简洁
    • 不够美观