matplotlib 画图相关

参考:

Matplotlib Totorial

Python优雅地可视化数据

对 matplotlib 中 figure, axes 和 subplots 的理解

区别

A figure in matplotlib means the whole window in the user interface. Within this figure there can be subplots. While subplot positions the plots in a regular grid, axes allows free placement within the figure. Both can be useful depending on your intention. We've already worked with figures and subplots without explicitly calling them. When we call plot, matplotlib calls gca() to get the current axes and gca in turn calls gcf() to get the current figure. If there is none it calls figure() to make one, strictly speaking, to make a subplot(111).

figure 是指整个用户界面窗口,figure 内可以有 subplots。subplots 在 figure 内的位置是比较固定或者说规整的,而 axes 可以在 figure 内随意指定位置。两种对象都很有用,取决于你具体的意图,当我们调用 plt 的时候,matplotlib 调用 gca() 函数以获取当前的 axes 然后 gca() 再调用 gcf() 来获取当前 figure。如果当前不存在 figure 那么 gcf() 会调用 figure() 创建一个,更具体的,创建一个 subplot(111)

figure

A figure is the windows in the GUI that has "Figure #" as title. Figures are numbered starting from 1 as opposed to the normal Python way starting from 0. This is clearly MATLAB-style. There are several parameters that determine what the figure looks like.

Subplots

With subplot you can arrange plots in a regular grid. You need to specify the number of rows and columns and the number of the plot. Note that the gridspec command is a more powerful alternative.

Axes

Axes are very similar to subplots but allow placement of plots at any location in the figure. So if we want to put a smaller plot inside a bigger one we do so with axes.

在 figure 中 add_axes 进行绘图默认是不显示 axis 的

两种 利用 matplotlib 画动态图的方法

  • 1 利用 animation 模块
  • 2 打开交互模式 plt.ion()

利用 animation 模块进行动态图绘制

matplotlib.animation.FuncAnimation

class matplotlib.animation.FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, **kwargs)

其中 blit 控制是否优化 图形绘制,默认为 False,即重新绘制不变图像。设置为 True 时,将只重新绘制变化部分。

BUG:当 interval 设置过小时,比如 interval = 1,开启 blit = True 将导致坐标轴部分显示为黑屏。

看一个利用 animation 模拟下雨过程的脚本

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# No toolbar
matplotlib.rcParams['toolbar'] = 'None'

# New figure with white background
fig = plt.figure(figsize=(6,6), facecolor='white')

# New axis over the whole figureand a 1:1 aspect ratio
# ax = fig.add_axes([0,0,1,1], frameon=False, aspect=1)
ax = fig.add_axes([0.005,0.005,0.990,0.990], frameon=True, aspect=1)

# Number of ring
n = 50
size_min = 50
size_max = 50*50

# Ring position
P = np.random.uniform(0,1,(n,2))

# Ring colors
C = np.ones((n,4)) * (0,0,0,1)

# Alpha color channel goes from 0 (transparent) to 1 (opaque)
C[:,3] = np.linspace(0,1,n)

# Ring sizes
S = np.linspace(size_min, size_max, n)

# Scatter plot
scat = ax.scatter(P[:,0], P[:,1], s=S, lw = 0.5,
                  edgecolors = C, facecolors='None')

# Ensure limits are [0,1] and remove ticks
ax.set_xlim(0,1), ax.set_xticks([])
ax.set_ylim(0,1), ax.set_yticks([])


def update(frame):
    global P, C, S

    # Every ring is made more transparent
    C[:,3] = np.maximum(0, C[:,3] - 1.0/n)

    # Each ring is made larger
    S += (size_max - size_min) / n

    # Reset ring specific ring (relative to frame number)
    i = frame % 50
    P[i] = np.random.uniform(0,1,2)
    S[i] = size_min
    C[i,3] = 1

    # Update scatter object
    scat.set_edgecolors(C)
    scat.set_sizes(S)
    scat.set_offsets(P)
    return scat,

animation = FuncAnimation(fig, update, interval=10)
# animation.save('../figures/rain.gif', writer='imagemagick', fps=30, dpi=72)
plt.show()

animation 的实现就是通过 FuncAnimation 反复调用回调函数 update

实际上 animation = FuncAnimation(fig, update, interval=10) 一句中,还有 frame 关键字参数,用以提供 frame 的具体生成形式,例如 frame = (i in range(100)) 这样的,因为 update 函数接受 frame 参数嘛。

打开交互模式

plt.ion()

此时 plt.show() 不再阻塞程序的运行,所以 figure 就可以一直更新了

import numpy as np
import matplotlib.pyplot as plt

plt.axis([0, 100, 0, 1])
plt.ion()
for i in range(100):
    y = np.random.random()
    plt.autoscale()
    plt.scatter(i, y)
    plt.pause(0.01)

再探 subplots

之前提到过,subplots 在 figure 内对位置的控制能力较弱,而 axes 对位置的控制能力是很强的,下面简单展示下分别利用 axes 和 subplots 画多个子图

import numpy as np
import matplotlib.pyplot as plt

X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
C,S = np.cos(X), np.sin(X)

plt.figure(1, figsize = (15, 6))

for i in range(6):
    plt.subplot(2,3,i+1)
    plt.plot(X, C, label = 'cos')
    plt.plot(X, S, label = 'sin')
    plt.xlabel('angle')
    plt.ylabel('sin or cos')
    plt.title('triangle functions')
    plt.legend()

plt.show()

以上是 一直用 plt 在进行画图,其实也就是不断的调用 gca 和 gcf

下面再利用 axes 对象重画一遍,这次只画一个子图,因为位置不好调。。。

import numpy as np
import matplotlib.pyplot as plt

X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
C,S = np.cos(X), np.sin(X)

fig = plt.figure(1, figsize = (15, 6))
ax = fig.add_axes([0.005,0.005,0.990,0.990], frameon=True, aspect=1)

ploter1 = ax.plot(X, C, label = 'cos')
ploter2 = ax.plot(X, S, label = 'sin')
legend = ax.legend(loc='upper left', shadow=True, fontsize='x-large')
xlabel = ax.set_xlabel('angle')
ylabel = ax.set_ylabel('sin or cos')
title = ax.set_title('triangle functions')

plt.show()

以上两个例子大致说明了 利用 plt 直接画图 和利用 axes 或 subplot 对象进行绘图的区别。实际上第一个实现就是用的 subplot 进行绘图的,只不过 通过 plt 对 gca 的调用隐去了对 subplt 的调用

换一种写法

fig = plt.figure()
ax = fig.add_subplot(2,3,1)
# 或者另一种惯用写法
fig, ax = subplots() # 在不指定参数的情况下默认是 111,如果指定行列,返回的 ax 是一个 list

一些别的东西

设置坐标轴的 最大最小值
plt.xlim()

清空坐标轴的刻度显示
plt.set_xticks([])

指定坐标轴刻度显示的位置
plt.xticks(loc,label)

一个自动设置 plt 比例的函数
plt.autoscale()

简单的多项式拟合
fp = np.polyfit(x,y,3)
f = np.poly1d(fp)

画直线
plt.vlines(x,y_min,y_max)