工欲善其事必先利其器之Matplotlib

Python开发工具集之可视化包Matplotlib

  • matplotlib是常用的Python的2D绘图包,可以对Python中的数据进行快速可视化,并以多种格式输出
  • 在matplotlib面向对象的绘图库中,pyplot是一个很好的API
  • pyplot与pylab的功能基本相同,pyplab包含许多NumPy和pyplot模块中常用的函数,十分适合在IPython交互式环境中使用
  • Pylab combines pyplot with numpy into a single namespace. This is convenient for interactive work, but for programming it is recommended that the namespaces be kept separate.

配置依赖环境

导入依赖模块

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

导入中文依赖模块

In [62]:
from matplotlib.font_manager import *

# 解决中文显示问题
# 使用场景:在text()、label()、title()中添加:fontproperties=myfont1
myfont1 = FontProperties(
    fname='/usr/share/fonts/truetype/arphic/ukai.ttc'
)
# 第二种解决图例中文显示问题,配置:plt.legend(prop=myfont2)
myfont2 = matplotlib.font_manager.FontProperties(
      fname='/home/jw/etl/venv/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/simhei.ttf'
    , size = 14
)
# 解决不显示负号的问题
matplotlib.rcParams['axes.unicode_minus'] = False
# 解决 cannot find sans-serif family 问题
matplotlib.rcParams.update({    
    'font.family':'sans-serif',
    'font.sans-serif':['Liberation Sans'],
    })

# 有时配置后需要清除一下缓存才能正确显示
# rm -rf ~/.matplotlib/*.cache

plot 基础制图

  • matplotlib plot提供了很多 API 函数. 本节从 plot 基础制图开始
In [63]:
# 先上一副最基本的图表
# 如果仅向plot()命令提供单个列表或数组
# 则matplotlib假定它是一个y值序列
# 并自动生成x值
plt.plot([1, 2, 3, 4])
# 显示图表
plt.show()

添加 x, y 值

In [64]:
plt.plot(
      [1, 2, 3, 4]    # x 值(list)
    , [1, 4, 16, 64]  # x 值(list)
)
plt.show()

配置图形颜色和形状

颜色字符 说明 颜色字符 说明 颜色字符 说明
‘b’ 蓝色 ‘m’ 洋红色 magenta ‘0.8’ 灰度值字符串
‘g’ 绿色 ‘y’ 黄色 ‘#008000’ RGB某颜色
‘r’ 红色 ‘k’ 黑色 ‘w’ 白色
‘c’ 青绿色 cyan
标记字符 说明 标记字符 说明 标记字符 说明
‘-‘ 实线 ’s’ 实心方形标记 ‘^’ 上三角标记
‘–’ 破折线 ‘p’ 实心五角标记 ‘>’ 右三角标记
‘-.’ 点划线 '*' 星形标记 ‘<’ 左三角标记
‘:’ 虚线 '竖线' 垂直线标记 ‘h’ 竖六边形标记
‘.’ 点标记 ‘1’ 下花三角标记 ‘H’ 横六边形标记
‘,’ 像素标记(极小点) ‘2’ 上花三角标记 ‘+’ 十字标记
‘o’ 实心圈标记 ‘3’ 左花三角标记 ‘x’ x标记
‘v’ 倒三角标记 ‘4’ 右花三角标记 ‘d’ 瘦菱形标记
‘D’ 菱形标记
In [121]:
plt.plot(
      [1, 2, 3, 4]      # x 值(list)
    , [1, 4, 16, 64]    # x 值(list)
    , 'r:'              # r 表示红色,: 表示虚线
)
plt.show()

添加关键字 plt.plot(x, y, format_string, **kwargs)

In [122]:
plt.plot(
      [1, 2, 3, 4]           # x 值(list)
    , [1, 4, 16, 64]         # x 值(list)
    , 'r:'                   # r 表示红色,: 表示虚线. 是关键字中color和linestyle的简化写法. 相同配置中生效的写法是后面的代码
    , color = 'm'            # 线条颜色.与前述 r 功能相同
    , linestyle = 'dashdot'  # 线条类型,与前述 : 功能相同,选择项包括: [‘solid’ | ‘dashed’, ‘dashdot’, ‘dotted’ | (offset, on-off-dash-seq) | '-' | '--' | '-.' | ':' | 'None' | ' ' | '']
    , linewidth = 5.0        # 线条宽度
    , marker ='o'            # 标记点形状
    , markersize = '10'      # 标记点大小
    , markerfacecolor ='k'   # 标记点颜色
)
plt.show()

配置两个线条

In [123]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(
      x, y1, 'r-'
    , x, y2, 'g--'
)
plt.show()

配置两个线条的第二种方法

In [68]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(x, y1, 'r-')
plt.plot(x, y2, 'g--')
plt.show()

控制线条属性-使用set方法

In [69]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(x, y1, 'r-')
line, = plt.plot(x, y2, 'g--', lw = 5)
line.set_antialiased(False)  # antialised代表字体抗锯齿,False为关闭
plt.show()

控制线条属性-使用setp()命令

In [70]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(x, y1, 'r-')
line, = plt.plot(x, y2, 'g--')
plt.setp(line, color='b', linewidth=1)
plt.show()

添加标题 title

In [71]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(x, y1, 'r-')
plt.plot(x, y2, 'g--')
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
plt.show()

添加 x,y 轴的标签 plt.xlabel(), plt.ylabel()

In [72]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
plt.plot(x, y1, 'r-')
plt.plot(x, y2, 'g--')
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
# 如有中文,配置字体为之前设定的 myfont
# fontsize=14 配置字体大小
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
plt.show()

配置图例 plt.legend()

loc 数值代表的位置
'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
In [73]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
l1, = plt.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = plt.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
# 如有中文,配置字体为之前设定的 myfont1
# fontsize=14 配置字体大小
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
# 如前导入中文依赖模块,此处引用 prop=myfont2
# frameon=True 为配置图例是否使用外框
# 图例的 loc 配置项见下表列出
# 如果此处使用porp, 则字体大小只能由myfont2中配置的size=14起作用
# 如果此处没有使用porp, 则字体大小由fontsize=xxx生效, 但这样无法显示中文
plt.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2)
# plt.legend(handles=[l1, l2], loc = 0, frameon=False, fontsize=14) 

plt.show()

添加文本 text()

  • text()命令可用于在任意位置添加文本
  • xlabel(), ylabel() 和 title() 用于在指定的位置添加文本
In [74]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
l1, = plt.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = plt.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
plt.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2)
# 前两个参数是text的x和y的位置
plt.text(-2.7, 2.5, ' www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=14, color='b') # 植入硬广告
plt.show()

添加网格 grid()

In [75]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
l1, = plt.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = plt.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
plt.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2)
# 前两个参数是text的x和y的位置
plt.text(-2.7, 2.5, ' www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=14, color='b') # 植入硬广告
# 添加网格
plt.grid(color='m', linestyle='--', linewidth=1, alpha=0.3)  # 配置网格的颜色, 线条类型和宽度,以及透明度等等
plt.show()

标注特殊点之 annotate()

In [76]:
def funy(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,5, endpoint=True) # endpoint为True包含终止点
print('x = ', x)
print('~'*70)
y=funy(x)
print('y = ', y)
print('~'*70)
plt.plot(x, y, 'r-', label="www.jasper.wang", marker='o')
# zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成元组
# zip() 函数返回由元组组成的列表
for i, xy in enumerate(zip(x, y)):
    print('xy[', i, '] = ', xy)
    plt.annotate(
          "(%s,%s)" % xy               # 使用 (%s,%s) % (s1,s2) 格式化文本的方式提供标注点文本
        , xy=xy                        # 标记的位置即循环变量 xy 的当前值
        , xytext=(-10, 10)             # 标注文本的偏移量
        , textcoords='offset points'   # 确定以标注点的偏移量位置显示文本
    )
plt.show()
x =  [-5.  -2.5  0.   2.5  5. ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
y =  [ 3.85  0.1   0.1   3.85 11.35]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xy[ 0 ] =  (-5.0, 3.85)
xy[ 1 ] =  (-2.5, 0.1)
xy[ 2 ] =  (0.0, 0.1)
xy[ 3 ] =  (2.5, 3.85)
xy[ 4 ] =  (5.0, 11.35)

标注特殊点之 plt.scatter()

  • 两个点之间的连接路径的创建由connectionstyle键控制,可以使用以下样式:
名称 属性
angle angleA=90,angleB=0,rad=0.0
angle3 angleA=90,angleB=0
arc angleA=0,angleB=0,armA=None,armB=None,rad=0.0
arc3 rad=0.0
bar armA=0.0,armB=0.0,fraction=0.3,angle=None
In [77]:
x = np.linspace(0,np.pi,20)
y = np.sin(x)

# 前后添加"$"符号 表示数学公式
plt.plot(x, y, label='$cos(x)$', color='red', linewidth=1.0, linestyle='--') 

# 添加标注点
x0 = np.pi/2
y0 = np.sin(x0) 

# 显示一个点
plt.scatter(x0, y0, color='black')

# 在一条点到x轴画出垂直线
plt.plot([x0, x0], [0, y0],'b--') 

# 标注方法1
plt.annotate(
      'y = %s' % y0
    , xy=(x0, y0)
    , xycoords='data'
    , xytext=(+80,-30)
    , textcoords='offset points'
    , arrowprops=dict(
          arrowstyle='->'
        , connectionstyle='arc3, rad=-0.8'  # 两个点之间的连接路径的创建由connectionstyle键控制, 样式选择arc3:rad=-0.8
    )
)

# 标注方法2
plt.text(x0+0.3, y0, 'this is a sin(x) line')

plt.show()

将 x,y 轴坐标转换成字符坐标 plt.xticks(), plt.yticks()

In [78]:
# 使用 np.linspace 创建等差数列,三个参数分别代表起止和分隔数
x = np.linspace(-4, 4, 50)
y = np.cos(x)

# 作图 
plt.plot(x, y)

# 使用 ticks 将相应坐标数转换成字符坐标
plt.xticks(
      [-np.pi, -np.pi/2, 0, np.pi/2, np.pi]               # x 配置坐标刻度线
    , ['$-\pi$', '$-\pi/2$',  '$0$', '$\pi/2$','$\pi$']   # x 配置坐标刻度线对应的显示文本
    , fontsize=14
) 
plt.yticks(
      [-1, 0, 1]                                          # y 配置坐标刻度线
    , ['- one','zero','+ one']                            # y 配置坐标刻度线对应的显示文本
    , fontsize=14
)

plt.show()

配置x轴和y轴范围 plt.xlim(), plt.ylim()

In [79]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
l1, = plt.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = plt.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
plt.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2)
# 前两个参数是text的x和y的位置
plt.text(-2.7, 2.5, ' www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=14, color='b') # 植入硬广告
# 添加网格
plt.grid(True)
# 配置x轴和y轴范围
# 范围变大了,曲线图相对变小了
# 但 title, text, label, legend等等都没有变小
plt.xlim(-10.0, 10.0)
plt.ylim(-5, 15)

plt.show()

配置x轴和y轴范围的第二种方法 x.min(), x.max()

In [80]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
l1, = plt.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = plt.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
plt.title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=18) # 植入硬广告
plt.ylabel('y 轴标签', fontproperties=myfont1, fontsize=14) 
plt.xlabel('x 轴标签', fontproperties=myfont1, fontsize=14)
plt.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2)
plt.text(-2.7, 2.5, ' www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1, fontsize=14, color='b') # 植入硬广告
plt.grid(True)
# 根据 min, max动态配置最大最小值
plt.xlim(x.min()*1.1, x.max()*1.1)
plt.ylim(y2.min()*3, y2.max()*1.1)
print('y2的最小值是:', y2.min())
plt.show()
y2的最小值是: -0.3166666666666669

在 plot 中绘制横线, 竖线, 块

  • axhline: 在轴上添加一条水平线
  • axhspan: 在轴上添加水平跨度(矩形)
  • axvline: 在轴上添加垂直线
  • axvspan: 在轴上添加垂直跨度(矩形)
In [81]:
t = np.arange(-1, 2, .01)
s = np.sin(2*np.pi*t)
plt.plot(t, s, 'k:')
# draw a thick red hline at y=0 that spans the xrange
l = plt.axhline(linewidth=8, color='r')
# draw a default hline at y=1 that spans the xrange
l = plt.axhline(y=0.8, color='b')
# draw a default hline at y=.5 that spans the middle half of the axes
# xmin: Should be between 0 and 1, 0 being the far left of the plot, 1 the far right of the plot.
# xmax: Should be between 0 and 1, 0 being the far left of the plot, 1 the far right of the plot.
l = plt.axhline(y=.6, xmin=0.2, xmax=0.6)
# draw a default vline at x=1 that spans the yrange
l = plt.axvline(x=0.8, linewidth=8)
# draw a thick blue vline at x=0 that spans the upper quadrant of the yrange
l = plt.axvline(x=0.7, ymin=0.75, ymax=0.25, linewidth=1, color='b')
p = plt.axhspan(0.2, 0.38, facecolor='0.5', alpha=0.5)
p = plt.axvspan(0.05, 0.25, facecolor='g', alpha=0.5)
plt.axis([0, 1, 0, 1])
plt.show()

使用示例 可用于二元对比的柱形图 plt.bar

In [82]:
n = 10
X = np.arange(n)
male = (1-X/float(n)) * np.random.uniform(0.5, 1.0, n)
female = (1-X/float(n)) * np.random.uniform(0.5, 1.0, n)
plt.bar(X, +male, facecolor='green', edgecolor='white')
plt.bar(X, -female, facecolor='blue', edgecolor='white')
for x,y in zip(X,male):
    plt.text(x, y, '%.2f' % y, ha='center', va= 'bottom', fontsize=12)
for x,y in zip(X,female):
    plt.text(x, -y-0.02, '%.2f' % y, ha='center', va= 'top', fontsize=12)
plt.xlim(-1.1, n*1.1)
plt.xticks([])
plt.ylim(-1.1, +1.1)
plt.yticks([])
plt.show()

Matplotlib组件

  • 以上基础制图操作,是直接在plot中实现快速绘图, 是一种面向函数的方法
  • matplotlib可以使用 figure,subplot 和 axes 配置布局
    • figure 是 matplotlib 中面向用户的界面,在 figure 内部配置 subplots
    • subplot 指定 plots 在 figure 中的位置
    • axes 是被指定位置后的的 plots

术语表

英文 中文
Annotation 标注
Axes 轴域
Axis 轴/坐标轴
Coordinate 坐标
Figure 图形
Handle 句柄
Legend 图例
Line 线条
Patch 补丁
Path 路径
Subplot 子图
Text 文本
Tick 刻度
Tick Label 刻度标签

Figure, Subplot, Axes的关系

  • 一个Figure是一个空白的画板
  • Figure可以切割为一个或多个Sublots
  • 切割后, 每一个Subplot也是一个空白的画板
  • Axes是布局在画板上的轴域(包含纵轴xaxix, 横轴yaxis等信息)
  • Axes可以直接布局在Figure之上, 也可以布局在每一个Subplot之上
  • 一个Figure(或subplot)上可以包含一个或多个Axes
  • Subplots的位置, 只能按照既定规则排列显示在Figure之上, 不可重叠和交叉
  • Axes的位置, 可以在任意位置显示, 还可以跨Subplots显示

plot 和 axes 的选择

  • matplotlib的函数式编程是通过封装对象实现的
  • matplotlib本质上是构建对象来构建图像, 但函数式编程将构建对象的过程封装在函数中方便调用
  • matplotlib的函数式编程存在的不足
    • 操作"对象"时,函数调用降低了效率
    • 函数掩盖Figure, subplot, axes的隶属关系, 容易导致知其然而不知其所以然
    • plot函数力有不逮,有时处理图像仍然需要对对象进行操作
    • 针对不同对象的操作实现相同的绘图效果容易导致混淆
  • 对于程序猿(媛)来说, Axes可能是更好的选择

Figure, Subplot, Axes关系演示

  • 面对对象是指在轴域上对axes进行操作, 但是画板的切割,使用subplot还是更为规范
  • 查看对象基本类型
    • fig1: class 'matplotlib.figure.Figure'
    • ax1: class 'matplotlib.axes._axes.Axes', 直接配置在Figure之上的axes
    • ax3: class 'matplotlib.axes._subplots.AxesSubplot', 配置在subplot之上的axes

声明一个空白Figure

In [83]:
fig = plt.figure()
print(type(fig))
# 可以发现对象类型为: <class 'matplotlib.figure.Figure'>
<class 'matplotlib.figure.Figure'>
<Figure size 432x288 with 0 Axes>

声明2行1列subplots fig.add_subplot()

In [84]:
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
print(type(ax1))
# 可以发现对象类型为: <class 'matplotlib.axes._subplots.AxesSubplot'>
<class 'matplotlib.axes._subplots.AxesSubplot'>

声明2行,第一行2列,第二行1列的subplots(第二种声明方法) plt.subplot()

In [85]:
ax1 = plt.subplot(221) # 第一行的左图
ax2 = plt.subplot(222) # 第一行的右图
ax3 = plt.subplot(212) # 第二整行
print(type(ax1))
# 可以发现对象类型为: <class 'matplotlib.axes._subplots.AxesSubplot'>
<class 'matplotlib.axes._subplots.AxesSubplot'>

声明三个轴域(直接在Figure之上),第二个和第三个重叠(且都覆盖在第一个之上) fig.add_axes()

In [86]:
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # 四个参数分别代表起点的x,y值和终点的x,y值
ax2 = fig.add_axes([0.4, 0.4, 0.4, 0.4])
ax3 = fig.add_axes([0.2, 0.2, 0.4, 0.4])
print(type(ax1))
# 可以发现对象类型为: <class 'matplotlib.axes._axes.Axes'>
<class 'matplotlib.axes._axes.Axes'>

同时声明 Axes 和 Subplot

In [87]:
fig1=plt.figure(figsize=(8, 5))
# 在fig1中配置一个轴域
# 隐式声明了一个subplot(111)
ax1 = plt.Axes(fig1,[0.2, 0.2, 0.4, 0.4])
# 需要执行add操作将其添加到fig中
fig1.add_axes(ax1)
# 清除axes的方法
# ax1.set_axis_off()
# 如果使用plt.subplot方法配置,会清除上一段代码中配置的axes
# ax1=plt.subplot(221)
# ax3=plt.subplot(223)
# 将Figure切割为2行2列,配置其中的第一个和第四个
ax1=fig1.add_subplot(221)
ax2=fig1.add_subplot(224)

Subplot画板切片 GridSpec

In [88]:
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3)   # 切分为3行3列
ax1 = plt.subplot(gs[0, :2])   # 行列均以0为开始下标 第1行到第2列
ax2 = plt.subplot(gs[1, 0])    # 第2行, 第1列
ax3 = plt.subplot(gs[1, 1])    # 第2行, 第2列
ax4 = plt.subplot(gs[0:2, 2])  # 第1到第2行, 第3列
ax5 = plt.subplot(gs[2:, :])   # 第3行, 第1到第3列
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) # 调整布局
plt.show()

声明一个带有ax的非空白Figure

In [89]:
fig, ax = plt.subplots()
print(type(ax))
# 可以发现ax对象类型为: <class 'matplotlib.axes._subplots.AxesSubplot'>
<class 'matplotlib.axes._subplots.AxesSubplot'>

声明Axes后为每一个subplot(ax数组中的列表中的成员)配置轴域信息

In [90]:
t=np.arange(0.0,2.0,0.1)
s=np.sin(t*np.pi)
figure, ax=plt.subplots(2,2)
ax[0][0].plot(t,s,'r*')       # 配置第1个plot
ax[0][1].plot(t*2,-s,'b--')   # 配置第2个plot
ax[1][0].grid(color='r', linestyle='--', linewidth=1, alpha=0.3) # 配置第3个plot网格
ax[1][1].plot(s,t,'k*')       # 配置第4个plot
print(type(ax))
ax
# 可以发现ax对象类型为: <class 'numpy.ndarray'>, 表示ax是一个包含Subplot之上的Axes的列表的数组
<class 'numpy.ndarray'>
Out[90]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f2c5dcaa588>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f2c58015518>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f2c5da5c128>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f2c57eaf780>]],
      dtype=object)

Figures 语法

figure(num = None, figsize = None, dpi = None, facecolor = None, edgecolor = None, frameon = True)

参数 默认值 描述
num 1 图像编号或名称,数字为编号 ,字符串为名称
figsize figure.figsize 指定figure的宽和高,单位为英寸
dpi figure.dpi 指定绘图对象的分辨率,即每英寸多少个像素,缺省值为80
facecolor figure.facecolor 背景颜色
edgecolor figure.edgecolor 边框颜色
frameon True 是否显示边框

Subplot 语法

subplot(nrows, ncols, sharex, sharey, subplot_kw, **fig_kw)

参数 描述
nrows subplot的行数
ncols subplot的列数
sharex 共享的x轴
sharey 共享的y轴
subplot_kw subplot关键字
**fig_kw figure关键字,如plt.subplots(2, 2, figsize=(8, 6))

Axes 语法

  • Axes(fig, rect, facecolor=None, frameon=True, sharex=None, sharey=None, label='', xscale=None, yscale=None, **kwargs)
  • rect = [left, bottom, width, height] in Figure coordinates
  • 针对axes操作可以得到与subplot相同的显示效果, 但所使用的函数形式不同
  • plt.xlabel() 设置的是当前坐标系的X坐标,默认是suplot(111)
  • ax.set_xlabel() 则是利用坐标系"对象"在轴域axes上设置X坐标
  • 四大坐标系
数据坐标系 说明(x轴范围,y轴范围)
子图坐标系 描述子图中位置的坐标系,左下角为(0,0),右上角为(1,1)
图表坐标系 左下角为(0,0),右上角为(1,1)
窗口坐标系 左下角坐标为(0,0),右上角坐标为(width,height). 以像素为单位的坐标系,不包含标题栏、工具条及状态栏部分
  • 两大对象
类别 对象
axes对象(1) transData为数据坐标变换对象
axes对象(2) transAxes为子图坐标变换对象
Figure对象 transFigure为图表变换对象
  • 注释
    • 在python模板中提供的绘制文字的函数为text()和figtext().
      • text():调用当前Axes对象的text()方法进行绘图,默认在数据坐标系中添加文字
      • figtext():调用当前figure对象的text()方法进行绘图,默认在图表坐标系中添加文字
    • 可通过transform参数改变文字所在的坐标系

声明fig和ax figure, ax=plt.subplots()

In [91]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
figure, ax=plt.subplots()
l1, = ax.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = ax.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
ax.set_xlabel("www.jasper.wang")   # 设定x轴的标签
ax.set_ylabel("www.jasper.wang")   # 设定y轴的标签
ax.set_xlim(x.min()*1.1, x.max()*1.1)         # 设定x轴范围
ax.set_ylim(y2.min()*3, y2.max()*1.1)         # 设定y轴范围
ax.set_title('www.jasper.wang')
ax.grid(color='m', linestyle='--', linewidth=1, alpha=0.3)
ax.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2, shadow=True)
plt.show()

隐藏边框 spines[].set_color()

In [92]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
figure, ax=plt.subplots()
l1, = ax.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = ax.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
ax.set_xlabel("www.jasper.wang")   # 设定x轴的标签
ax.set_ylabel("www.jasper.wang")   # 设定y轴的标签
ax.set_xlim(x.min()*1.1, x.max()*1.1)         # 设定x轴范围
ax.set_ylim(y2.min()*3, y2.max()*1.1)         # 设定y轴范围
ax.set_title('www.jasper.wang')
ax.grid(color='m', linestyle='--', linewidth=1, alpha=0.3)
ax.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2, shadow=True)
# ax.spines['left'].set_color('none')    # 隐藏左边的y轴
ax.spines['right'].set_color('none')   # 隐藏右边的y轴
ax.spines['top'].set_color('none')     # 隐藏顶部的x轴
# ax.spines['bottom'].set_color('none')  # 隐藏底部的x轴
plt.show()

移动坐标轴与刻度 set_ticks_position()

In [93]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
figure, ax=plt.subplots()
l1, = ax.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = ax.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
ax.set_xlabel("www.jasper.wang")              # 设定x轴的标签
ax.set_ylabel("www.jasper.wang")              # 设定y轴的标签
ax.set_xlim(x.min()*1.1, x.max()*1.1)         # 设定x轴范围
ax.set_ylim(y2.min()*3, y2.max()*1.1)         # 设定y轴范围
ax.set_title('www.jasper.wang')
ax.grid(color='m', linestyle='--', linewidth=1, alpha=0.3)
ax.legend(handles=[l1, l2], loc = 0, frameon=False, prop=myfont2, shadow=True)
ax.spines['left'].set_color('none')           # 隐藏左边的y轴
ax.spines['top'].set_color('none')            # 隐藏顶部的x轴
# ax.xaxis.set_ticks_position('bottom')
ax.xaxis.set_ticks_position('bottom')         # 配置刻度是标注在坐标的上面还是下面
ax.spines['bottom'].set_position(('data', 6))
ax.yaxis.set_ticks_position('right')          # 配置刻度是标注在坐标的左侧还是右侧
ax.spines['right'].set_position(('data', x.max()*1.1)) # 配置右侧坐标,在x数据最大值的1.1的位置

plt.show()

配置x,y轴范围 plt.axis([xmin,xmax,ymin,ymax])

In [94]:
def funy1(x):
    return x*0.75+5
def funy2(x):
    return x**2*0.3+x*0.75+0.1
x = np.linspace(-5,5,10)
y1=funy1(x)
y2=funy2(x)
figure, ax=plt.subplots()
l1, = ax.plot(x, y1, 'r-', label="www.jasper.wang")
l2, = ax.plot(x, y2, 'g--', label="土豪哥我们做朋友吧")
ax.set_xlabel("www.jasper.wang")               # 设定x轴的标签
ax.set_ylabel("www.jasper.wang")               # 设定y轴的标签
ax.set_xlim(x.min()*1.1, x.max()*1.1)          # 设定x轴范围
ax.set_ylim(y2.min()*3, y2.max()*1.1)          # 设定y轴范围
ax.set_title('www.jasper.wang')
ax.grid(color='m', linestyle='--', linewidth=1, alpha=0.3)
ax.legend(handles=[l1, l2], loc = 2, frameon=False, prop=myfont2, shadow=True)
ax.spines['left'].set_color('none')            # 隐藏左边的y轴
ax.spines['top'].set_color('none')             # 隐藏顶部的x轴
# ax.xaxis.set_ticks_position('bottom')
ax.xaxis.set_ticks_position('bottom')          # 配置刻度是标注在坐标的上面还是下面
ax.spines['bottom'].set_position(('data', 12.5))
ax.yaxis.set_ticks_position('right')           # 配置刻度是标注在坐标的左侧还是右侧
ax.spines['right'].set_position(('data', x.max()*2.2)) # 配置右侧坐标,在x数据最大值的1.1的位置
# 指定轴域的可视区域
# axis()命令接收[xmin,xmax,ymin,ymax]列表
plt.axis([-10, 10, -2, 18])
plt.show()

嵌入式Axes,使用句柄 fig.add_axes([left, bottom, width, height])

In [95]:
#新建figure
fig = plt.figure()
# 定义数据
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 4, 2, 5, 8, 6]

# 新建区域ax1
# figure的百分比,从figure 10%的位置开始绘制, 宽高是figure的80%
left, bottom, width, height = 0.1, 0.1, 0.85, 0.85
# 获得绘制的句柄
ax1 = fig.add_axes([left, bottom, width, height])
ax1.plot(x, y, 'r')
ax1.set_title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1)  # 植入硬广告

# 新增区域ax2, 嵌套在ax1内
left, bottom, width, height = 0.25, 0.5225, 0.26, 0.26
# 获得绘制的句柄
ax2 = fig.add_axes([left, bottom, width, height])
ax2.plot(x,y, 'b')
ax2.set_title('www.jasper.wang\n土豪哥我们做朋友吧', fontproperties=myfont1)  # 植入硬广告
# plt.title('jasper.wang')    # 针对当前axes,即ax2,配置title
plt.xticks([])                # 针对当前axes,即ax2,配置隐藏 x 坐标
plt.yticks([])                # 针对当前axes,即ax2,配置隐藏 y 坐标

plt.show() 

子图共享坐标轴

In [96]:
t = np.arange(0.01, 5.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = np.sin(4*np.pi*t)

ax1 = plt.subplot(311)
plt.plot(t, s1)
plt.setp(ax1.get_xticklabels(), fontsize=14)
plt.setp(ax1.get_yticklabels(), fontsize=14)

# ax2共享ax1的x轴
ax2 = plt.subplot(312, sharex=ax1)
plt.plot(t, s2)

# 隐藏ax2的x坐标刻度
plt.setp(ax2.get_xticklabels(), visible=False)

# ax3共享ax1的x轴和y轴
ax3 = plt.subplot(313, sharex=ax1, sharey=ax1)
plt.plot(t, s3)

plt.xlim(0.01, 5.0)
plt.show()

调整子图位置以避免覆盖 plt.tight_layout

In [97]:
t = np.arange(0.01, 5.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = np.sin(4*np.pi*t)

ax1 = plt.subplot(311)
plt.plot(t, s1)
plt.setp(ax1.get_xticklabels(), fontsize=14)
plt.setp(ax1.get_yticklabels(), fontsize=14)

ax2 = plt.subplot(312, sharex=ax1)
plt.plot(t, s2)

plt.setp(ax2.get_xticklabels(), visible=False)

ax3 = plt.subplot(313, sharex=ax1, sharey=ax1)
plt.plot(t, s3)

plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) # 配置位置信息避免覆盖重叠

plt.xlim(0.01, 5.0)
plt.show()

在plot的axes中标注特殊点详细语法 annotate()

In [98]:
# 在标注中,有两个要考虑的点:
# 由参数 xy 表示的标注位置   (箭头提示)
# 由 xytext 表示的文本位置  (文本)
# 这两个参数都是(x, y)元组
fig = plt.figure()
ax = fig.add_subplot(111)

x = np.arange(0.0, 5.0, 0.01)
y = np.cos(2*np.pi*x)
# linewidth 简写 lw
ax.plot(x, y, lw = 2, color='blue')
'''
    xy=(横坐标,纵坐标)  箭头尖端
    xytext=(横坐标,纵坐标) 文字的坐标,指的是最左边的坐标
    arrowprops= {
        facecolor= '颜色',
        shrink = '数字' <1  收缩箭头
    }
plt.annotate(
      r'$\sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$'
    , xy=(t, np.sin(t))
    , xycoords='data'
    , xytext=(+10, +30)
    , textcoords='offset points'
    , fontsize=16
    , arrowprops=dict(
              arrowstyle="->"
            , connectionstyle="arc3,rad=.2"
            )
    )
'''
ax.annotate(
      '最大值', fontproperties=myfont1  # 第一个参数s为要标记上去的文本
    , xy=(2, 1) # 第二个参数xy为标记的位置,必须是可迭代对象(如tuple,list)
    , xycoords='data'  # 第三个对象xycoords表示xy的参考系
    , xytext=(20, 40) # 第四个对象xytext为标记文本相对标记位置的位置,必须是可迭代对象iterable
    , textcoords='offset points' # 第五个对象textcoords表示xytext的参考系,是以偏移点计算位置,如上x移动40点,y移动80点
    , fontsize=16 # 第六个对象fontsize为字体大小
    , arrowprops=dict(  # 第七个对象arrowprops为箭头样式
          facecolor='black'
        , shrink=0.005 #  fraction of total length to ‘shrink’ from both ends 
        , width=2 # 箭头的线条宽度
        , headlength=5  # 箭头长度
        , headwidth=8   # 箭头宽度
    )
) 
# 配置 y 轴刻度的范围
ax.set_ylim(-1.5, 2)
Out[98]:
(-1.5, 2)

更多文本处理技巧

  • text:在Axes的任意位置添加文本。 命令式:matplotlib.pyplot.text,面向对象:matplotlib.axes.Axes.text。

  • xlabel:向 x 轴添加轴标签。 命令式:matplotlib.pyplot.xlabel,面向对象:matplotlib.axes.Axes.set_xlabel。

  • ylabel:向 y 轴添加轴标签。 命令式:matplotlib.pyplot.ylabel,面向对象:matplotlib.axes.Axes.set_ylabel。

  • title:向Axes添加标题。 命令式:matplotlib.pyplot.title,面向对象:matplotlib.axes.Axes.set_title。

  • figtext:向Figure的任意位置添加文本。 命令式:matplotlib.pyplot.figtext,面向对象:matplotlib.figure.Figure.text。

  • suptitle:向Figure添加标题。 命令式:matplotlib.pyplot.suptitle,面向对象:matplotlib.figure.Figure.suptitle。

  • annotate:向Axes添加标注,带有可选的箭头。 命令式:matplotlib.pyplot.annotate,面向对象:matplotlib.axes.Axes.annotate。

  • 所有这些函数创建并返回一个matplotlib.text.Text()实例,它可以配置各种字体和其他属性

In [99]:
fig = plt.figure()
fig.suptitle('Figure标题', fontproperties=myfont1, fontsize=14, fontweight='bold')
ax = fig.add_subplot(111)
fig.subplots_adjust(top=0.85)
ax.set_title('Axes标题', fontproperties=myfont1)
ax.set_xlabel('x 轴标签', fontproperties=myfont1)
ax.set_ylabel('y 轴标签', fontproperties=myfont1)
ax.text(3, 8, '数据坐标中的字体', fontproperties=myfont1, bbox={'facecolor':'y', 'alpha':0.5, 'pad':10})
ax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)
ax.plot([2], [1], 'o')
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4), arrowprops=dict(facecolor='black', shrink=0.05))
ax.axis([0, 10, 0, 10])
plt.show()
In [100]:
import matplotlib.patches as patches
# 在axes坐标中创建一个矩形
left, width, bottom, height = .15, .75, .15, .65
right = left + width
top = bottom + height
fig = plt.figure()
ax = fig.add_subplot(111)
# axes coordinates are 0, 0 is bottom left and 1,1 is upper right
# patch: 作为背景的Rectangle对象
# patches: patch对象列表
# 语法: class matplotlib.patches.Rectangle(xy, width, height, angle=0.0, **kwargs)[source]
p = patches.Rectangle(
      (left, bottom)
    , width
    , height
    , fill=False
    , transform=ax.transAxes
    , clip_on=False
    )
ax.add_patch(p)
ax.text(left, bottom, 'left top', horizontalalignment='left', verticalalignment='top', transform=ax.transAxes, fontsize=14, color='b')
ax.text(left, bottom, 'left bottom', horizontalalignment='left', verticalalignment='bottom', transform=ax.transAxes, fontsize=14, color='b')
ax.text(right, top, 'right bottom', horizontalalignment='right', verticalalignment='bottom', transform=ax.transAxes, fontsize=14, color='b')
ax.text(right, top, 'right top', horizontalalignment='right', verticalalignment='top', transform=ax.transAxes, fontsize=14, color='b')
ax.text(right, bottom, 'center top', horizontalalignment='center', verticalalignment='top', transform=ax.transAxes, fontsize=14, color='b')
ax.text(left, 0.5*(bottom+top), 'right center', horizontalalignment='right', verticalalignment='center', rotation='vertical', transform=ax.transAxes, fontsize=14, color='b')
ax.text(left, 0.5*(bottom+top), 'left center', horizontalalignment='left', verticalalignment='center', rotation='vertical', transform=ax.transAxes, fontsize=14, color='b')
ax.text(0.5*(left+right), 0.5*(bottom+top), '请看着我!\n...不是,\n是看着摄像机!\n   --安倍晋三', horizontalalignment='center', verticalalignment='center', fontsize=20, color='red', transform=ax.transAxes, fontproperties=myfont1)
ax.text(right, 0.5*(bottom+top), 'centered', horizontalalignment='center', verticalalignment='center', rotation='vertical', transform=ax.transAxes, fontsize=14, color='b')
ax.text(left, top, 'rotated\nwith newlines', horizontalalignment='center', verticalalignment='center', rotation=45, transform=ax.transAxes, fontsize=14, color='b')
plt.show()

比较历史走势的堆积图 ax.stackplot()

In [101]:
# subplots(1, 2) 则ax的成员为ax[0]
# subplots(2, 2) 则ax的成员为ax[0][0]
# 维数增加,索引号相应增加
figure, ax=plt.subplots(1, 2, figsize=(16, 4))
x=np.arange(10)
# 定义一个函数, 随机产生最小5,最大50. 成员数为10个的一维数组
def fnx():
    return np.random.randint(5, 50, 10) 
# 将一维数组扩展为3维数组赋值给y
y = np.row_stack((fnx(), fnx(), fnx()))
# 3个y[]分别为一维数组
y1, y2, y3 = fnx(), fnx(), fnx()
ax[0].stackplot(x, [y1, y2, y3])      # 其中[]可以省略 
ax[0].set_xlabel("www.jasper.wang")   # 设定x轴的标签
ax[0].set_ylabel("www.jasper.wang")   # 设定y轴的标签
ax[0].set_xlim(1,x.max()*1.1)         # 设定x轴范围
ax[0].set_ylim(1,y.max()*1.1)         # 设定y轴范围
ax[0].set_xticks(range(0,10,1))       # 设定x轴的刻度
ax[0].set_yticks(range(0,150,10))     # 设定y轴的刻度
ax[0].set_title('www.jasper.wang')
ax[0].grid(True)
ax[1].stackplot(x, y) 
ax[1].axis('off')                     # 隐藏第2个ax的坐标轴
plt.tight_layout()
plt.show()

使用插值函数实现平滑曲线 interpolate.interp1d(x, y, kind='cubic')

In [102]:
from scipy import interpolate
x = np.linspace(1, 5, num=5)
y = - np.sin(np.pi ** x)
print('x = ', x)
print("~"*90)
print('y = ', y)
li = interpolate.interp1d(x, y, kind="cubic")
# 插值算法
# 插值:通过已知的离散数据来求解未知数据的方法,要求曲线通过所有的已知数据
# 拟合:要求曲线函数与已知数据集的误差最小,不要求曲线通过所有的已知数据
# 其中, 参数 kind 是插值类型, kind 的选项有四个:
# nearest:最邻近插值法
# zero:阶梯插值
# slinear、linear:线性插值
# quadratic、cubic:2、3阶B样条曲线插值

# interpld 计算 x 的取值范围之内任意点的 函数值,并返回新的 数组
# 参数x和y是一系列已知的数据点
x_new = np.linspace(1, 5, num=15)                 # 新的linspace起点和终值应等于(或小于)原X的起点终值范围
                                                  # 且num的数值(间隔点)越大,曲线越平滑
                                                  # 返回插值后的x值
y_new = li(x_new)                                 # 返回插值后的y值
print("~"*90)
print('x_new = ', x_new)
print("~"*90)
print('y_new = ', y_new)
plt.figure(figsize=(8, 4), dpi=80)
plt.plot(x, y, "y", lw=5 )                        # 原数据
plt.plot(x_new, y_new, "r", lw=1)                 # 插值后的新数据
plt.legend(['原始数据', '插值数据'], prop=myfont2)
plt.show()
x =  [1. 2. 3. 4. 5.]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
y =  [-1.22464680e-16  4.30301217e-01  3.98288179e-01  1.97174949e-02
  9.59493084e-01]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
x_new =  [1.         1.28571429 1.57142857 1.85714286 2.14285714 2.42857143
 2.71428571 3.         3.28571429 3.57142857 3.85714286 4.14285714
 4.42857143 4.71428571 5.        ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
y_new =  [-1.22464680e-16  1.54285463e-01  2.86663653e-01  3.90801503e-01
  4.60365947e-01  4.89023918e-01  4.70442351e-01  3.98288179e-01
  2.75261264e-01  1.40193186e-01  4.09484504e-02  2.53915653e-02
  1.41387038e-01  4.36799375e-01  9.59493084e-01]

经典数据分析基本功示例

In [103]:
from matplotlib import transforms
def func1(x):
    return x*0.75+3
def func2(x):
    return x**2*0.3+x*0.75+1.5
#交点横坐标
def find_intersects(x,y1,y2):
    d=y1-y2
    idx=np.where(d[:-1]*d[1:]<=0)[0]
    x1,x2=x[idx],x[idx+1]
    d1,d2=d[idx],d[idx+1]
    return -d1*(x2-x1)/(d2-d1)+x1
#绘图
x=np.linspace(-5,5,100)
f1=func1(x)
f2=func2(x)
fig,ax=plt.subplots(figsize=(6,4))
ax.plot(x,func1(x),x,func2(x))

#找到交点横坐标,将交点用圆圈表示
x1,x2=find_intersects(x,f1,f2)
ax.plot(x1,func1(x1),'o')
ax.plot(x2,func2(x2),'o')

#直线>曲线部分的面积填充
ax.fill_between(x,f1,f2,where=f1>f2,color='g',alpha=0.5)

#将一个以数据横坐标为宽,子图高度为高的矩形,用颜色填充
#transforms的blended_transform_factory函数可创建一个混合坐标(数据坐标,子图坐标)
from matplotlib import transforms
trans=transforms.blended_transform_factory(ax.transData,ax.transAxes)
ax.fill_between(
      [x1,x2]            # x轴的范围
    , 0                  # y轴起点
    , 1                  # y轴终点
    , transform=trans    # 指定混合坐标
    , alpha=0.1          # 透明度
)

#子图注释
a=ax.text(0.05,0.95,'直线与二次曲线的交点',transform=ax.transAxes,
          va='top',fontsize=18, fontproperties=myfont1,
          bbox={'color':'r','alpha':0.4,})
#箭头注释
#data表示使用的是数据坐标系中的坐标变换对象
#axes fraction表示使用的是子图坐标系的坐标变换对象
#offset points表示文字与箭头的相对位置保持不变
#arrowprops为描述箭头样式的字典
arrow={'arrowstyle':'fancy,tail_width=0.6','color':'gray'}
ax.annotate('交点',xy=(x1,func1(x1)),xycoords='data',xytext=(0.4,0.5),
            textcoords='axes fraction',arrowprops=arrow, fontproperties=myfont1)
ax.annotate('交点',xy=(x2,func2(x2)),xycoords='data',xytext=(0.4,0.5),
            textcoords='axes fraction',arrowprops=arrow, fontproperties=myfont1)
xm=(x1+x2)/2
ym=(func1(xm)-func2(xm))/2+func2(xm)
ax.annotate('直线大于曲线区域',xy=(xm,ym),xycoords='data',xytext=(30,-30),
            textcoords='offset points',arrowprops=arrow,fontproperties=myfont1,
            bbox={'color':'g','alpha':0.4,}
            )
plt.show()

其他可用于商业数据分析的Axes辅助方法

  • 有非常多的Axes辅助方法用于创建基本艺术家并将它们添加到他们各自的容器中
  • 下表总结了他们的一部分,他们创造的Artist的种类,以及他们在哪里存储它们
辅助方法 艺术家 容器
ax.annotate - 文本标注 Annotate ax.texts
ax.bar - 条形图 Rectangle ax.patches
ax.errorbar - 误差条形图 Line2D 和 Rectangle ax.lines 和 ax.patches
ax.fill - 共享区域 Polygon ax.patches
ax.hist - 直方图 Rectangle ax.patches
ax.imshow - 图像数据 AxesImage ax.images
ax.legend - 轴域图例 Legend ax.legends
ax.plot - xy 绘图 Line2D ax.lines
ax.scatter - 散点图 PolygonCollection ax.collections
ax.text - 文本 Text ax.texts

刻度

  • 坐标轴的刻度如此重要, 以至于需要单列一章

隐藏刻度与边框

In [104]:
x = [2, 4, 6, 8, 10]
y = [i**3 for i in x]
fig, ax = plt.subplots(1, 4, figsize=(12, 4))

ax[0].plot(x, y, 'b-') 

ax[1].plot(x, y, 'b-') 
ax[1].set_xticks([])                      # 隐藏x轴刻度
ax[1].set_yticks([])                      # 隐藏x轴刻度

ax[2].plot(x, y, 'b-') 
ax[2].spines['left'].set_color('none')    # 隐藏左边的y轴
ax[2].spines['right'].set_color('none')   # 隐藏右边的y轴
ax[2].spines['top'].set_color('none')     # 隐藏顶部的x轴
ax[2].spines['bottom'].set_color('none')  # 隐藏底部的x轴

ax[3].plot(x, y, 'b-') 
ax[3].spines['left'].set_color('none')    # 隐藏左边的y轴
ax[3].spines['right'].set_color('none')   # 隐藏右边的y轴
ax[3].spines['top'].set_color('none')     # 隐藏顶部的x轴
ax[3].spines['bottom'].set_color('none')  # 隐藏底部的x轴
ax[3].set_xticks([])                      # 隐藏x轴刻度
ax[3].set_yticks([])                      # 隐藏x轴刻度

plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
plt.show()

隐藏刻度与边框的第二种方法

In [105]:
x = [2, 4, 6, 8, 10]
y = [i**3 for i in x]
fig, ax = plt.subplots()
ax.plot(x, y, 'b-') 
plt.axis('off')
plt.show()

隐藏刻度的第三种方法

In [106]:
x = [2, 4, 6, 8, 10]
y = [i**3 for i in x]
fig, ax = plt.subplots()
ax.plot(x, y, 'b-') 
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()

导入刻度包 ticker

Locator description
NullLocator No ticks.
MultipleLocator Ticks and range are a multiple of base; either integer or float.
FixedLocator Tick locations are fixed.
LinearLocator Space ticks evenly from min to max.
IndexLocator Locator for index plots (e.g., where x = range(len(y))).
AutoLocator MaxNLocator with simple defaults. This is the default tick locator for most plotting.
MaxNLocator Finds up to a max number of intervals with ticks at nice locations.
LogLocator Space ticks logarithmically from min to max.
In [107]:
import matplotlib.ticker as ticker

# Setup a plot such that only the bottom spine is shown
def setup(ax):                                           # 传入参数 ax
    ax.spines['right'].set_color('none')                 # 隐藏右刻度线
    ax.spines['left'].set_color('none')                  # 隐藏左刻度线
    ax.yaxis.set_major_locator(ticker.NullLocator())     # 设置y轴主刻度标签为空
    ax.spines['top'].set_color('none')                   # 隐藏上刻度线
    ax.xaxis.set_ticks_position('bottom')                # 配置x底部轴
    ax.tick_params(which='major', width=1.00, color='b') # 配置主轴宽度
    ax.tick_params(which='major', length=5)              # 配置主轴长度
    ax.tick_params(which='minor', width=0.75, color='m') # 配置次轴宽度
    ax.tick_params(which='minor', length=2.5)            # 配置次轴长度
    ax.set_xlim(0, 5)
    ax.set_ylim(0, 1)
    ax.patch.set_alpha(0.0)

plt.figure(figsize=(12, 6))
n = 8
# Push the top of the top axes outside the figure because we only show the bottom spine.
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=1.05)
<Figure size 864x432 with 0 Axes>

Null Locator

In [108]:
ax = plt.subplot(n, 1, 1)       # 生成n行1列的subplot,此处为第一个
setup(ax)                       # 调用setup函数
ax.xaxis.set_major_locator(ticker.NullLocator())  # 配置主刻度为空
ax.xaxis.set_minor_locator(ticker.NullLocator())  # 配置次刻度为空
ax.text(                        # 在Axes的任意位置添加文本
      0.0                       # x轴的0.0处
    , 0.2                       # y轴的0.1处
    , "NullLocator()"           # 显示文本
    , fontsize=14               # 字体大小
    , transform=ax.transAxes    # 通过transform 参数改变文字所在的坐标系, 此处变换为子图坐标
)
plt.show()

Multiple Locator

In [109]:
ax = plt.subplot(n, 1, 2)
setup(ax)
ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.1))
ax.text(0.0, 0.2, "MultipleLocator(0.5)", fontsize=14, transform=ax.transAxes)
plt.show()

Fixed Locator

In [110]:
ax = plt.subplot(n, 1, 3)
setup(ax)
majors = [0, 1, 5]                                         # 配置显示的主刻度
ax.xaxis.set_major_locator(ticker.FixedLocator(majors))    # 选择显示的主刻度
minors = np.linspace(0, 1, 6)[3:-1]                        # 配置:刻度为0到1,5个分割数(num=6), 并选择第4位到最后一位
ax.xaxis.set_minor_locator(ticker.FixedLocator(minors))
ax.text(0.0, 0.2, "FixedLocator([0, 1, 5])", fontsize=14, transform=ax.transAxes)
plt.show()

Linear Locator

In [111]:
ax = plt.subplot(n, 1, 4)
setup(ax)
ax.xaxis.set_major_locator(ticker.LinearLocator(3))
ax.xaxis.set_minor_locator(ticker.LinearLocator(9))
ax.text(0.0, 0.2, "LinearLocator(numticks=3)", fontsize=14, transform=ax.transAxes)
plt.show()

Index Locator

In [112]:
ax = plt.subplot(n, 1, 5)
setup(ax)
ax.plot(range(0, 5), [0]*5, color='red')                                # [0]*5, 配置y为5个0
ax.xaxis.set_major_locator(ticker.IndexLocator(base=0.5, offset=.25))   # 累加的基数为0.5, 偏移量为0.25
ax.text(0.0, 0.2, "IndexLocator(base=0.5, offset=0.25)", fontsize=14, transform=ax.transAxes)
plt.show()

Auto Locator

In [113]:
ax = plt.subplot(n, 1, 6)
setup(ax)
ax.xaxis.set_major_locator(ticker.AutoLocator())
ax.xaxis.set_minor_locator(ticker.AutoMinorLocator())
ax.text(0.0, 0.2, "AutoLocator()", fontsize=14, transform=ax.transAxes)
plt.show()

MaxN Locator

In [114]:
ax = plt.subplot(n, 1, 7)
setup(ax)
ax.xaxis.set_major_locator(ticker.MaxNLocator(4))
ax.xaxis.set_minor_locator(ticker.MaxNLocator(12))
ax.text(0.0, 0.2, "MaxNLocator(n=4)", fontsize=14, transform=ax.transAxes)
plt.show()

Log Locator

In [115]:
ax = plt.subplot(n, 1, 8)
setup(ax)
ax.set_xlim(10**3, 10**10)
ax.set_xscale('log')
ax.xaxis.set_major_locator(ticker.LogLocator(base=10.0, numticks=15))
ax.text(0.0, 0.1, "LogLocator(base=10, numticks=15)", fontsize=15, transform=ax.transAxes)
plt.show()

刻度ticker对str与int的缺省处理

  • 当x为int时,刻度为自然数,并按照自然数刻度显示对应的y值
  • 当x为str时(譬如str类型的日期),按照str类型日期出现的次序(不重复地)排列在x轴,并将重复的相同x对应的y值显示在第一次出现x的位置
  • 注意, str类型的日期,并不会按照日期由前到后排列
In [116]:
fig, ax = plt.subplots(4, 1, figsize=(12, 12))

x = ['a', 'bb', 'cccc', 'ddddd', 'eeeeee']
y = [len(i) for i in x]
ax[0].plot(x, y, 'r*') 
ax[0].xaxis.set_major_locator(ticker.MultipleLocator(1))

x = ['a', 'bb', 'cccc', 'ddddd', 'eeeeee']
y = [len(i) for i in x]
ax[1].plot(x, y, 'r*') 
ax[1].xaxis.set_major_locator(ticker.MultipleLocator(1))
ax[1].xaxis.set_minor_locator(ticker.MultipleLocator(0.5))

x = [1, 3, 5, 7, 7, 7, 2, 10, 7, 4, 11, 3, 3]
y = [i**np.random.rand() for i in x]
ax[2].plot(x, y, 'r*') 
ax[2].xaxis.set_major_locator(ticker.MultipleLocator(1))
plt.grid(True)

x = ['2018-08-12', '2018-08-12', '2018-07-05', '2018-07-12', '2018-07-10', '2018-08-28', '2018-08-10', '2018-08-10', '2018-07-12', '2018-08-10', '2018-08-15', '2018-08-28', '2018-08-30']
print(type(x), ' : ', x)
for i in x[0:3]:
    print(type(i), ' : ', i)
y = [int(len(i))**np.random.rand() for i in x]
ax[3].plot(x, y, 'r*') 
ax[3].xaxis.set_major_locator(ticker.MultipleLocator(1))
plt.grid(True)
<class 'list'>  :  ['2018-08-12', '2018-08-12', '2018-07-05', '2018-07-12', '2018-07-10', '2018-08-28', '2018-08-10', '2018-08-10', '2018-07-12', '2018-08-10', '2018-08-15', '2018-08-28', '2018-08-30']
<class 'str'>  :  2018-08-12
<class 'str'>  :  2018-08-12
<class 'str'>  :  2018-07-05

刻度的偏移 Offset()

In [117]:
fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(
      np.arange(2000, 2010)            # 刻度为2000开始,实际显示为1998,向前偏移2位,可查看左上角和右下角的下标
    , range(10)
    , 'r*'
) 
ax.get_xaxis().get_major_formatter().set_useOffset(2) 
ax.get_yaxis().get_major_formatter().set_useOffset(-1) 
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(1))
ax.grid(True)
plt.show()

时间刻度 Matplotlib.dates

  • There are a number of helper functions to convert between datetime objects and Matplotlib dates:
helper function description
date2num Convert datetime objects to Matplotlib dates
num2date Convert Matplotlib dates to datetime objects
num2timedelta Convert number of days to a timedelta object
epoch2num Convert an epoch or sequence of epochs to the new date format, that is days since 0001
num2epoch Convert days since 0001 to epoch
mx2num Convert mx datetime instance (or sequence of mx instances) to the new date format
drange Return a sequence of equally spaced Matplotlib dates

datetime 对象与 Matplotlib dates的转换

In [118]:
import datetime as dt
import pytz
date1 = dt.datetime(2018, 8, 28)
date2 = dt.datetime(2018, 9, 2)
today=dt.datetime.now()

print(type(date1), 'date1 is ', date1)
print(type(today), 'today is ', today)
print('-'*70)

ordinal = matplotlib.dates.date2num(today)
print(type(ordinal), 'today\'s ordinal is ', ordinal)
print('-'*70)

today_of_tz = matplotlib.dates.num2date(ordinal, tz=pytz.timezone('Asia/Shanghai')) 
print(type(today_of_tz), 'today_of_tz is', today_of_tz, '备注:经过date2num和num2date转换后的时间会相差8小时因为格林威治时间与本地时间相差8小时')
print('-'*70)

timedelta = matplotlib.dates.num2timedelta(5)
print(type(timedelta), 'The timedelta is', timedelta)
print('-'*70)

epoch2num = matplotlib.dates.epoch2num(2018)
print(type(epoch2num), 'The epoch2num is', epoch2num)
print('-'*70)

num2epoch = matplotlib.dates.num2epoch(epoch2num)
print(type(num2epoch), 'The num2epoch is', num2epoch)
print('-'*70)

delta = dt.timedelta(days=1)
print(type(delta), 'The delta is', delta)
print('-'*70)

dranday = matplotlib.dates.drange(date1, date2, delta)
ndd = matplotlib.dates.num2epoch(dranday)
print(type(ndd), 'The num2epoch is', ndd)
print('-'*70)
for i in dranday:
    print(type(dt.date.fromordinal(int(i))), ' : ', dt.date.fromordinal(int(i)))
print('-'*70)
x = dt.datetime.strftime(date1, "%y-%m-%d %H:%M")
print(type(x), 'The strftime is', x)
<class 'datetime.datetime'> date1 is  2018-08-28 00:00:00
<class 'datetime.datetime'> today is  2018-08-26 23:21:13.490126
----------------------------------------------------------------------
<class 'float'> today's ordinal is  736932.9730728024
----------------------------------------------------------------------
<class 'datetime.datetime'> today_of_tz is 2018-08-27 07:21:13.490120+08:00 备注:经过date2num和num2date转换后的时间会相差8小时因为格林威治时间与本地时间相差8小时
----------------------------------------------------------------------
<class 'datetime.timedelta'> The timedelta is 5 days, 0:00:00
----------------------------------------------------------------------
<class 'numpy.float64'> The epoch2num is 719163.0233564815
----------------------------------------------------------------------
<class 'numpy.float64'> The num2epoch is 2017.9999977350235
----------------------------------------------------------------------
<class 'datetime.timedelta'> The delta is 1 day, 0:00:00
----------------------------------------------------------------------
<class 'numpy.ndarray'> The num2epoch is [1.5354144e+09 1.5355008e+09 1.5355872e+09 1.5356736e+09 1.5357600e+09]
----------------------------------------------------------------------
<class 'datetime.date'>  :  2018-08-28
<class 'datetime.date'>  :  2018-08-29
<class 'datetime.date'>  :  2018-08-30
<class 'datetime.date'>  :  2018-08-31
<class 'datetime.date'>  :  2018-09-01
----------------------------------------------------------------------
<class 'str'> The strftime is 18-08-28 00:00

date tickers

dateticker description
MicrosecondLocator locate microseconds
SecondLocator locate seconds
MinuteLocator locate minutes
HourLocator locate hours
DayLocator locate specified days of the month
WeekdayLocator Locate days of the week, e.g., MO, TU
MonthLocator locate months, e.g., 7 for july
YearLocator locate years that are multiples of base
RRuleLocator locate using a matplotlib.dates.rrulewrapper. The rrulewrapper is a simple wrapper around a dateutil.rrule (dateutil) which allow almost arbitrary date tick specifications. See rrule example.
AutoDateLocator On autoscale, this class picks the best DateLocator (e.g., RRuleLocator) to set the view limits and the tick locations. If called with interval_multiples=True it will make ticks line up with sensible multiples of the tick intervals. E.g. if the interval is 4 hours, it will pick hours 0, 4, 8, etc as ticks. This behaviour is not guaranteed by default.

ax.xaxis.set_major_locator(YearLocator(5))

In [119]:
import datetime as dt
import matplotlib.pyplot as plt
from matplotlib.dates import DayLocator, HourLocator, DateFormatter, drange, MonthLocator, YearLocator
import numpy as np
from matplotlib import ticker 

date1 = dt.datetime(2012, 1, 1)
date2 = dt.datetime(2018, 8, 25)
delta = dt.timedelta(days=1)
dates = drange(date1, date2, delta)    # Return a sequence of equally spaced Matplotlib dates
y = []
for i in dates:
    # 改变一点曲线走势,当i大于某日时y值+5,否则减5
    a = lambda i:np.random.randn()+3 if i > 735800 else np.random.randn()-3
    y.append(a(i))
fig, ax = plt.subplots(figsize=(18,8))
ax.plot_date(dates, y, '--', linewidth=0.5)
ax.set_xlim(dates[0], dates[-1])
ax.xaxis.set_major_locator(YearLocator(2))  # 每隔2年做标识
ax.xaxis.set_minor_locator(YearLocator(1))  # 每隔1年做标识
ax.yaxis.set_major_locator(ticker.MultipleLocator(2))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(1))
ax.tick_params(
      which= 'major'
    , axis='both'
    , direction='out'
    , length=3
    , width=1
    , colors='b'
    , grid_color='b'
    , grid_alpha=0.5
    , pad = 25
    , labelsize = 14
    , labeltop = True
    , top = True
    , labelright = True
    , right = True
)
ax.xaxis.set_major_formatter(DateFormatter('%Y'))
ax.xaxis.set_minor_formatter(DateFormatter('%y'))   # 使用小写的y代表2位数的年份
ax.tick_params(
      which= 'minor'         # : {'major', 'minor', 'both'}
    , axis='both'            # : {'x', 'y', 'both'}
    , direction='out'        # : {'in', 'out', 'inout'}
    , length=3
    , width=1
    , colors='r'
    , grid_color='g'
    , grid_alpha=0.2
    , pad = 5
    , labeltop = True
    , top = True
    , labelright = True
    , right = True
)
ax.grid(which = 'both' , color='b', linestyle='--', linewidth=1)
plt.show()

选择每周一和周日为x轴的日期刻度

In [120]:
from matplotlib.dates import YearLocator, MonthLocator, WeekdayLocator, DayLocator, HourLocator, DateFormatter, drange
from matplotlib.dates import MO, TU, WE, TH, FR, SA, SU
import pytz

date1 = dt.datetime(2018, 9, 12)
date2 = dt.datetime(2018, 10, 7)
delta = dt.timedelta(days=1)
dates = drange(date1, date2, delta) 
y = []
for i in dates:
    # 改变一点曲线走势,当i大于某日时y值+5,否则减5
    a = lambda i:np.random.randn()+5 if i > 733000 else np.random.randn()-5
    y.append(a(i))

fig, ax = plt.subplots(figsize=(18, 6))
ax.plot(
      dates
    , y
    , 'r-'
) 
loc = WeekdayLocator(byweekday=(MO, SU), tz=pytz.timezone('Asia/Shanghai'))  # 选择每周一和周日为x轴的日期刻度
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(DateFormatter('%y-%m-%d'))
fig.autofmt_xdate()  # 缺省日期格式会倾斜
ax.grid(True)
plt.show()

Date formatters 如上示例中已有体现

date formatter description
AutoDateFormatter attempts to figure out the best format to use. This is most useful when used with the AutoDateLocator.
DateFormatter use strftime() format strings
IndexDateFormatter date plots with implicit x indexing.

后记

  • 作为一款科学计算可视化包, Matplotlib的功能非常强大
  • 本文档只是从商业数据分析可视化角度进行归纳整理, 侧重点十分明显
  • 随着后续数据分析需求的增加, 本文档将会随时更新记录相关功能

爱与彼岸财经分析团队编制

爱与彼岸财经分析