导论:
在科研和研究的过程中,无论是哪个学科或者将来走上工作岗位,可视化是非常重要的一个环节。
这里的重要性,在我看来有三点:人是视觉动物,老板看你工作做的怎么样,paper reviewer看你研究做的怎么样,有相当一部分来自于图表的合理展示以及对图表的‘故事性’叙述。
通过对数据的可视化,进一步找到规律,发现问题并解决问题。因为数据都是冷冰冰的,只有把让他们“跃然纸上“,才能进一步进行挖掘价值。
针对特定研究领域(如计算机视觉,图形学),需在算法的某些步骤进行可视化以检验算法的(大致)准确性。
以上就是个人对其重要性的理解,其他就不哆嗦了。
本篇文章主要介绍matplotlib的进阶教程。基本涵盖了大部分的使用场景。matplotlib是基于python语言的一个可视化的package,在2D可视化领域还是极其强大的,应该不比R或者matlab的2D绘图功能差。
Note1: 适合入门级选手,大神请绕道~
Note2:如果研究兴趣是图形学,或者说处理对象是point cloud, mesh等3D对象,欢迎使用亮亮老师的mapple软件,非常强大。(链接如下)
顺便吐槽一下,matplotlib或者任何其他基于python的package可视化点云或者面片的效果均不理想,可以尝试下上面的链接,事半功倍。
概述:
下面五幅图是截取一些以前做research相关的中间图
其实在二维可视化场景大致分为五大类型(常用的一般都是这五种),上面的五个示意图除了条形图未覆盖其他均有。正文部分根据每个类型以一个完整Simple case +More complicated case 说明。Line-style 线状图
Scatter-style 散点图
Histograms-style 直方图
Bar-style 条形图
Image 图像
正文:
1. Line-style 线状图
(1) Simple case:
非常简单,平常画画函数是绰绰有余的
先上图:2D Line plot(simple)2D Line plot(simple)
再上代码:(这个就不注释了)
import numpy as np
import matplotlib.pyplot as plt
# 第一幅图
X = np.linspace(-np.pi, np.pi, 20, endpoint=True)
Cos, Sin = np.cos(X), np.sin(X)
plt.plot(X,Cos)
plt.plot(X,Sin )
plt.show()
# 第二幅图
n = 256
X = np.linspace(-np.pi,np.pi,n,endpoint=True)
Y = np.sin(2*X)
plt.axes([0.025,0.025,0.95,0.95])
plt.plot (X, Y+1, color='blue', alpha=1.00)
plt.fill_between(X, 1, Y+1, color='green', alpha=.25)
plt.plot (X, Y-1, color='blue', alpha=1.00)
plt.fill_between(X, -1, Y-1, (Y-1) > -1, color='blue', alpha=.25)
plt.fill_between(X, -1, Y-1, (Y-1) < -1, color='red', alpha=.25)
plt.xlim(-np.pi,np.pi), plt.xticks([])
plt.ylim(-2.5,2.5), plt.yticks([])
plt.show()
(2)More complicated case
标准的画法,基本的参数设置都有提及。
用的最多的一种形式,可以将多个曲线绘制在同一张fig中,同时可以高度定制线条、坐标轴、网格线、图例、注释等等。
先上图:2D Line plot(comprehend)
再上代码:(适当的地方我会给出注释)
要点:
设置线条:color, linewidth, linestyle, marker, markersize以及后面legend会用到的label
设置坐标轴:主要是label, ticks, lim
设置图例:主要就是简单的loc(做右下至左下分别设置为0,1,2,3),以及可以定制的bbox_to_anchor,以及是否frameon。高级用法可以有个handle可以使用(不过一般用不上)
设置网格:只需要调用plt.grid()即可
设置spine: 可以取消可见以及移动到任意位置
设置标注:两种方式。一是指向data的标注(plt.annotate),另一种是纯文本标注(plt.text)
import numpy as np
import matplotlib.pyplot as plt
import os
import sys
# 找出当前路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 生成2-D数据
X = np.linspace(-np.pi, np.pi, 20, endpoint=True)
Cos, Sin = np.cos(X), np.sin(X)
# 这里显示调用了plt.figure返回一个fig对象, 为了能够后面保存这个fig对象
# 并且设置了fig的大小和每英寸的分辨率
# 注意: resolution = figsize*dpi(因此这里的savefig的分辨率为1200X900)
fig = plt.figure(figsize=(4,3),dpi=300)
ax = plt.gca() # gca: get current axis
# 调用plt.plot函数将两条曲线画在同一个坐标系内,并设置相关的参数
# 设置线条参数:设置颜色,线宽,线形,标记,标记大小,图例标签等等
plt.plot(X, Cos, color='blue', linewidth=1, linestyle='-', marker='>', ms=5, label='cosine')
plt.plot(X, Sin, color='red', linewidth=1, linestyle='--', marker='<', ms=5, label='sine')
# 设置图例(legend)
# plt.legend(loc='auto', frameon=False) # frameon is flag to draw a frame around the legend
# Advanced legend
plt.legend(bbox_to_anchor=(0.02, .95), loc=3,
borderaxespad=0., frameon=False)
# 设置坐标轴的取值范围(lim)
plt.xlim(X.min()*1.1, X.max()*1.1)
plt.ylim(-1.2, 1.2)
# 设置坐标轴的记号(ticks)
plt.xticks(np.linspace(-np.pi, np.pi, 5, endpoint=True), [r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$'])
plt.yticks([-1, 0, +1], [r'$-1$', r'$0$', r'$+1$'], )
# 设置坐标轴的标签(label)
plt.xlabel('x_axis')
plt.ylabel('y_axis')
# # 打开grid选项
# plt.grid()
# 移动坐标轴到原点
# 先将上方和右方的spine去掉(color设为none即可)
# 再将x(y)坐标轴设为下方(左方)的spine,并且移动至原点
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
# 标注(annotate)
# 在x=t处画虚线,并且把(t, y)这个用plt.scatter的方式标记出来
# 在(t, y)处用plt.annotate方法以箭头的方式做标注。
t = 2*np.pi/3
plt.plot([t,t], [0, np.cos(t)], color='blue', linewidth=1, linestyle='--')
plt.scatter([t], [np.cos(t)], 50, color='blue')
plt.annotate(r'$\cos(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$',
xy=(t, np.cos(t)), xycoords='data',
xytext=(-90, -30), textcoords='offset points', fontsize=11,
arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=.2'))
plt.plot([t,t], [0, np.sin(t)], color='red', linewidth=1, linestyle='--')
plt.scatter([t], [np.sin(t)], 50, color='red')
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=11,
arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=.2'))
# 标注(text)
plt.text(-2, 0.8, 'Annotation with pure text', verticalalignment='bottom', horizontalalignment='right',
color='green', fontsize=8, bbox={'facecolor':'red', 'alpha':0.4, 'pad':2})
# # 将figure保存在path
# fig_name = os.path.join(BASE_DIR, 'figure.jpg')
# fig.savefig(fig_name)
plt.show()
(3) First Special case:规则化多图
多图通常用于横向或者纵向对比。可以是同类型的也可以是不同类型的。
本例主要用于示例,所以只画了四个背景色填充的fig。
先上图:regular multiple plotregular multiple plot(without ticks)
再上代码:
要点:
学会画多图,通常有两种方式:
a. plt.subplot(). 这里调用函数即可,偏于面向过程的思想
b. fig.add_subplot(). 这里的fig是plt.figure()出来的实例,偏向面向对象的思想
Note:其实还有一种方式,使用方法为: fig, ax_list = plt.subplot()。目前碰到唯一的缺陷是不能绘制不规则的多图。(不规则多图请见(4)Second special case)
完整代码如下:
import numpy as np
import matplotlib.pyplot as plt
import os
import sys
# 第一个图
for i,color in enumerate("rgby"):
plt.subplot(221+i, facecolor=color) # facecolor一般用于控制背景色
plt.show()
# 第二个图
fig = plt.figure()
for i,color in enumerate("rgby"):
fig.add_subplot(221+i, facecolor=color)
plt.xticks([]) # 用于去掉ticks
plt.yticks([]) # 用于去掉ticks
plt.show()
(4) Second Special case:不规则化多图
偶尔也会用到。
先上图:irregular multiple plotirregular multiple plot
再上代码:
要点:
在调用fig.add_subplot时,要清楚是如何划分的
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
import sys
# 第一个图
fig = plt.figure()
ax1 = fig.add_subplot(121, facecolor='r')
ax1.set_xticks([])
ax1.set_yticks([])
ax2 = fig.add_subplot(222, facecolor='g')
ax2.set_xticks([])
ax2.set_yticks([])
ax3 = fig.add_subplot(224, facecolor='k')
ax3.set_xticks([])
ax3.set_yticks([])
plt.show()
# 第二个图
G = gridspec.GridSpec(3, 3)
axes_1 = plt.subplot(G[0, :], facecolor='r')
plt.xticks([]), plt.yticks([])
plt.text(0.5,0.5, 'Axes 1',ha='center',va='center',size=24,alpha=.5)
axes_2 = plt.subplot(G[1,:-1], facecolor='g')
plt.xticks([]), plt.yticks([])
plt.text(0.5,0.5, 'Axes 2',ha='center',va='center',size=24,alpha=.5)
axes_3 = plt.subplot(G[1:, -1], facecolor='b')
plt.xticks([]), plt.yticks([])
plt.text(0.5,0.5, 'Axes 3',ha='center',va='center',size=24,alpha=.5)
axes_4 = plt.subplot(G[-1,0], facecolor='y')
plt.xticks([]), plt.yticks([])
plt.text(0.5,0.5, 'Axes 4',ha='center',va='center',size=24,alpha=.5)
axes_5 = plt.subplot(G[-1,-2], facecolor='k')
plt.xticks([]), plt.yticks([])
plt.text(0.5,0.5, 'Axes 5',ha='center',va='center',size=24,alpha=.5)
plt.show()
=======================(手动分割)=====================
2. Scatter-style 线状图
(1) Simple case:
第一幅图时使用了默认的cmap(应该是Sequential colormaps中的'hot')。第二个为qualitative colormaps中的'Set1'。
具体请参照:
先上图:cmap: defaultcmap: 'Set1'
再上代码:
import matplotlib.pyplot as plt
import numpy as np
# 生成mean=0.0, sigma=1的二维标准正态分布,数据点为1000
# 并且采用一个函数生成一个color的一个序列(只是为了好看而已,无须研究)
n = 1000
normal_2D_data = np.random.normal(0, 1, (n, 2))
T = np.arctan2(normal_2D_data[:, 1],normal_2D_data[:, 0]) # for color value
# 调用plt.scatter进行画图,第一句采用默认的cmap即第一个图,第二句采用称为‘Set1’的 cmap即第二个图
# plt.scatter(normal_2D_data[:, 0], normal_2D_data[:, 1], s=75, c=T, alpha=.6)
plt.scatter(normal_2D_data[:, 0], normal_2D_data[:, 1], s=75, c=T, alpha=.6, cmap='Set1')
plt.xlim(-1.5, 1.5)
plt.xticks(()) # ignore xticks
plt.ylim(-1.5, 1.5)
plt.yticks(()) # ignore yticks
plt.show()
(2)More complicated case
标准的画法,基本的参数设置都有提及
假设我们有五个二维点集p1...p5。我们希望将五个点集用不同的颜色显示以区分(相当于区分不同的class)
先上图:
再上代码:
要点:
区分:本例和(1) Simple case中的第二张图,可视化出来看似有label是因为生成了一个color的一个序列并且给出了一个qualitative colormap,但是和本例有本质的区别。
核心步骤:是根据label归一化出一个值,并且根据这个值通过选择的colormap生成一个color。有两种方式:
# 方式一
import matplotlib.cm as cm
import matplotlib.colors as colors
cmap = cm.Set10
norm = Normalize(vmin=0, vmax=4) # 假设有5类
cmap(norm(0)) # 第1类的color
#方式二
norm = Normalize(vmin=0, vmax=4) # 假设有5类
color_map = cm.ScalarMappable(norm=norm , cmap=cm.Set1)
完整代码如下:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.cm as cm
import matplotlib.colors as colors
import os
import sys
# 生成数据和标签
n_points=200
n_classes = 5
plot_with_labels = True
pts = (10-1)*np.random.random(size=(n_points ,2))-1 # 1000 poinrs, range is (1, 10]
labels = np.random.randint(0, n_classes, size=pts.shape[0]) # 5 classes, [0, 1, 2, 3, 4]
labels_dict = {0: 'pts_1', 1: 'pts_2', 2: 'pts_3', 3: 'pots_4', 4: 'pts_5'}
fig = plt.figure(dpi=120)
axe = plt.axes([0.1, 0.1, 0.7, 0.7])
# axe = fig.add_subplot(111)
if not plot_with_labels:
axe.scatter(pts[:, 0], pts[:, 1], s=100, alpha=0.6)
else:
# # 第一种方法(use: cm.color_map(color_norm(index)))
# unique_labels = np.unique(labels)
# print(unique_labels)
# n_classes = len(unique_labels)
# n_classes = n_classes - 1
# color_norm = colors.Normalize(vmin=0, vmax=4)
# for class_idx, label in enumerate(unique_labels):
# mask = np.where(labels == label)[0]
# print("mask len", len(mask))
# axe.scatter(pts[:, 0][mask], pts[:, 1][mask],
# s=100,
# color=cm.Set1(color_norm(class_idx)),
# label=labels_dict[class_idx],
# alpha=0.6)
# 第二种方法(use: combine cm.ScalarMappable and colors.Normalize)
unique_labels = np.unique(labels)
print(unique_labels)
n_classes = len(unique_labels)
n_classes = n_classes - 1
color_norm = colors.Normalize(vmin=0, vmax=4)
color_map = cm.ScalarMappable(norm=color_norm, cmap=cm.Set1)
for class_idx, label in enumerate(unique_labels):
mask = np.where(labels == label)[0]
print("mask len", len(mask))
axe.scatter(pts[:, 0][mask], pts[:, 1][mask],
s=100,
color=color_map.to_rgba(class_idx),
label=labels_dict[class_idx],
alpha=0.6)
plt.legend(bbox_to_anchor=(1.02, .8), loc=3,
borderaxespad=0., frameon=True)
axe.grid()
axe.set_title('Five Class Points Set')
# fig_name = os.path.join(BASE_DIR, 'figure_1.jpg')
# fig.savefig(fig_name)
plt.show()
=======================(手动分割)=====================
3. Histogram-style 直方图
(1) Simple case:
先上图:
再上代码:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
\mu, sigma = 100, 15
x = mu + sigma*np.random.randn(100000)
n, bins, patches = plt.hist(x, bins=30, histtype='bar', rwidth=0.8, normed=1)
print(n, bins, patches)
# add a 'best fit' line
y = mlab.normpdf( bins, mu, sigma)
l = plt.plot(bins, y, 'r--', linewidth=1)
plt.xlabel('Smarts')
plt.ylabel('Probability')
plt.title(r'$\mathrm{Histogram\ of\ IQ:}\ \mu=100,\ \sigma=15$')
plt.axis([40, 160, 0, 0.03])
plt.grid(True)
plt.show()
(2)More complicated case
标准的画法,基本的参数设置都有提及
先上图:
再上代码:
要点:
bin的个数,柱状图的类型,透明度等
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
gaussian_numbers_1 = np.random.normal(size=100000)
gaussian_numbers_2 = np.random.normal(3, 2, size=100000)
n, bins, patches = plt.hist(gaussian_numbers_1, bins=30, histtype='bar', rwidth=0.8, normed=1, color='g', alpha=0.5, label='Gaussian_1')
y = mlab.normpdf( bins, 0, 1)
l = plt.plot(bins, y, 'k--', linewidth=1)
n, bins, patches = plt.hist(gaussian_numbers_2, bins=30, histtype='bar', rwidth=0.8, normed=1, color='r', alpha=0.5, label='Gaussian_2')
y = mlab.normpdf( bins, 3, 2)
l = plt.plot(bins, y, 'k--', linewidth=1)
plt.title("Gaussian Comparasion Histogram")
plt.xlabel("Value")
plt.ylabel("Probability")
plt.legend(loc='best')
plt.show()
=======================(手动分割)=====================
4. Bar-style 条形图
(1) Simple case:
先上图:
再上代码:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(20)
y = x**2
plt.bar(x, y)
plt.xticks(np.arange(20))
plt.show()
(2)More complicated case
标准的画法,基本的参数设置都有提及
先上图:
再上代码:
要点:
n = 12
X = np.arange(n)
Y1 = (1-X/float(n)) * np.random.uniform(0.5,1.0,n)
Y2 = (1-X/float(n)) * np.random.uniform(0.5,1.0,n)
# plt.axes([0.025,0.025,0.95,0.95])
fig = plt.figure(2)
plt.bar(X, +Y1, facecolor='#9999ff', edgecolor='white', label='+Y1')
plt.bar(X, -Y2, facecolor='#ff9999', edgecolor='white', label='-Y2')
for x,y in zip(X,Y1):
plt.text(x, y+0.05, '%.2f' % y, ha='center', va= 'bottom')
for x,y in zip(X,Y2):
plt.text(x, -y-0.05, '%.2f' % y, ha='center', va= 'top')
plt.xlim(-.5,n)
plt.ylim(-1.25,+1.25)
plt.xticks([])
plt.legend()
plt.show()
=======================(手动分割)=====================
5. Image 图像
(1) Simple case:
先上图:
再上代码:
import matplotlib.pyplot as plt
import numpy as np
def f(x,y):
return (1-x/2+x**5+y**3)*np.exp(-x**2-y**2)
n = 10
x = np.linspace(-3,3,3.5*n)
y = np.linspace(-3,3,3.0*n)
X,Y = np.meshgrid(x,y)
Z = f(X,Y)
plt.axes([0.025,0.025,0.95,0.95])
plt.imshow(Z,interpolation='nearest', cmap='bone', origin='lower')
plt.colorbar(shrink=.92)
plt.xticks([]), plt.yticks([])
# savefig('../figures/imshow_ex.png', dpi=48)
plt.show()
(2)More complicated case
先上图:
再上代码:
要点:
这里是一个用于可视化模板匹配的结果。有模板,原图,热度图,此外还有colorbar。
fig = plt.figure() # the same as plt.subplot() or plt.subplot()==> return fig and a tuple of axis
ax1 = fig.add_subplot(221)
ax1.imshow(T)
ax1.set_title('Tempalte')
ax1.axis('off')
ax2 = fig.add_subplot(222)
ax2.imshow(I)
ax2.set_title('Result with bbox')
ax2.axis('off')
ax3 = fig.add_subplot(224)
cax = ax3.imshow(heatmap, cmap=cm.jet)
ax3.set_title('Heatmap')
ax3.axis('off')
ax4 = fig.add_subplot(223)
ax4.axis('off')
# Add colorbar, make sure to specify tick locations to match desired ticklabels
cbar = fig.colorbar(cax, ax=ax4, orientation='vertical', ticks=[0, np.max(heatmap)], shrink=0.6)
cbar.ax.set_yticklabels(['0', str(np.max(heatmap))]) # vertically oriented colorbar
# fig.colorbar(cax, ax=ax[2])
plt.suptitle('Method: '+similarity_method)
plt.show()
=======================(手动分割)=====================
好了,该完结了。有空再增加点三维可视化的东西。最后给大家推荐一个除了matplotlib之外的一个非常好用的packages:plotly(链接如下)
这篇博文算是这段时间经常用matplotlib的一个总结和再学习,同时也希望能够帮助入门同志们学习和使用(对,是入门级,如果是大神请直接再看一遍找找问题哈哈哈),如果有什么问题,欢迎在评论区交流。