选中内容(绿色)时除了会搜索文章名,还会搜索文章内容
点击结果中的文章名进入文章界面后可以按Ctrl+F在页面内搜索
  • 版权:CC BY-SA 4.0
  • 创建:2020-05-15
  • 更新:2020-10-20
用 python 的 matplotlib 库绘图,完美与 numpy opencv TensorFlow 等库结合展示数据和图片,绘制线图、散点图、等高线图、条形图、柱状图、3D 图形,甚至是图形动画等等


matplotlib_demo0

链接

官网的教程做得非常好, 如果真的想理解用法,建议静下心慢慢读一读官方教程

安装

  1. pip3 install matplotlib

注意,这里所有都是用的 python3

测试

  1. import matplotlib.pyplot as plt

这里用了别名 plt,大家用的时候都这样用,看起来更简单

基本

先直观看一个例子, 用到了plt.title plt.xlabel plt.ylabel plt.plot plt.show

  1. import numpy as np
  2. from matplotlib import pyplot as plt
  3. x = np.arange(1,11)
  4. y = 2 * x + 5
  5. plt.title("Matplotlib demo")
  6. plt.xlabel("x axis caption")
  7. plt.ylabel("y axis caption")
  8. plt.plot(x,y, 'og')
  9. plt.show()

basic_pyplot

下面从是一些基本的概念和用法:

backends, 特别是无 GUI 的服务器要注意

就是选择后端实现,不同的平台可以选择不一样的后端, 比如在jupyterlabpyqt5中的后端就会不同, 默认一个是agg一个是qt5gg

详细地看官方文档说明

  • 在matplotlibrc 文件中的rcParams["backend"] (默认: agg) 变量
  • MPLBACKEND 环境变量
  • matplotlib.use()函数
  • plt.switch_backend()函数
  1. # set for server with no Tkagg GUI support, use agg(non-GUI backend)
  2. plt.switch_backend('agg')

取值有: agg pdf ps svg pgf cairo(非交互式的,也就是不需要窗口显示的,一般在服务器或者jupyter等里面使用这种模式), qt5agg tkagg 等等, 详细的看这里

交互模式

前面的代码, 需要调用plt.show()才最终显示图片, 如果想要实时显示改变,可以调用plt.ion()打开交互模式

  1. plt.ion() # plt.ioff()
  2. plt.plot([1.6, 2.7])
  3. # plt.show()

这里的代码在plot()后就会立刻显示画布,不需要调用show(),使用某些后端可能需要调用plt.draw()来刷新

这样就可以用来动态刷新画布上的内容了

基本名词

matplotlib

  • figure: 面板,或者说画布, 这很重要, 一个面板对应了一个窗口,或者对应jupyter的一个widget;一个figure包含了至少1个或多个axes
  • axis: 轴, x轴y轴z轴,就是数学术语上的坐标轴
  • axes: 由一个xy(xyz)坐标系组成的区域。需要依附在一个figure上面,一个figure至少有一个axes, 你可以想象在画板上画了一幅图或者多幅图,这些图就叫axes, 而这些图可是是图片也可以是含有表格等
    注意 axis 指axes中一幅图的一个坐标轴,在这里axes不是axis在数学坐标轴术语上的复数形式, 注意区分
  • 子图: 通常大家的常规印象是画一张图, 或者在一张图中画多张子图, 但是这里没有子图这个概念, 只有一个figure中有多张图(axes)的概念, 而一张图是axes数量等于1的特殊情况. 如果非要说, 也可以把axes翻译成子图,因为它就是画板上的(子)图
  • markers: 数据点
  • line: 将数据点连线得到的线段
  • legend: 图例
  • markers: 标记点,也就是数据点
  • trick label: 轴刻度标签
  • axis label: 轴名称

基本函数

创建面板: plt.figure

  1. fig = plt.figure() # an empty figure with no Axes

几个经常用的参数:

  • figsize: 面板大小, 默认[6.4, 4.8], 单位是英寸
  • dpi: 每英寸的像素点数量, 默认100
  • facecolor: 背景颜色, 更多样式设置看后面的美化样式
  • edgecolorcolor: 边框颜色
  • tight_layout: 紧凑布局
  • constrained_layoutbool: 受限布局

这里有很重要的参数: 创建图像的大小就取决于dpifigsize, 创建图像需要先考虑

可以创建多个figure

  1. import matplotlib.pyplot as plt
  2. plt.figure(1) # the first figure
  3. plt.figure(2) # a second figure
  4. plt.figure(1) # 将当前figure又重新设置为第一个

创建axes: plt.subplot

subplot

创建axes的几种方法如下:

  • 默认自动创建:使用 plt.plot()会自动创建axes
  1. x = np.linspace(0, 2, 100)
  2. #
  3. plt.plot(x, x, label='linear') # Plot some data on the (implicit) axes.
  4. plt.plot(x, x**2, label='quadratic') # etc.
  5. plt.plot(x, x**3, label='cubic')
  6. plt.xlabel('x label')
  7. plt.ylabel('y label')
  8. plt.title("Simple Plot")
  9. plt.legend()
  10. plt.show()

figure0

  • 或者手动调用subplots创建一个或者多个图, 也叫object-oriented(OO):

调用这个函数会自动创建一个figure, figure的参数也可以放到这个函数里面使用

  1. fig, ax = plt.subplots() # a figure with a single Axes
  2. fig, axs = plt.subplots(3, 2) # a figure with a 3(row)x2(col) grid of Axes # 3行2列
  1. from matplotlib import pyplot as plt
  2. import numpy as np
  3. #
  4. x = np.linspace(0, 2, 100)
  5. # Note that even in the OO-style, we use `.pyplot.figure` to create the figure.
  6. fig, ax = plt.subplots() # Create a figure and an axes.
  7. ax.plot(x, x, label='linear') # Plot some data on the axes.
  8. ax.plot(x, x**2, label='quadratic') # Plot more data on the axes...
  9. ax.plot(x, x**3, label='cubic') # ... and some more.
  10. ax.set_xlabel('x label') # Add an x-label to the axes.
  11. ax.set_ylabel('y label') # Add a y-label to the axes.
  12. ax.set_title("Simple Plot") # Add a title to the axes.
  13. ax.legend() # Add a legend.
  14. plt.show()

注意这里设置标题的函数和plt的一系列函数名不一样,需要注意

  • 还有一种是调用subplot 没有 s, matplotlib有当前 figure和当前axes的概念, 选中当前axes, 使用plt的操作都是对这个axes的操作
    1. def f(t):
    2. return np.exp(-t) * np.cos(2*np.pi*t)
    3. #
    4. t1 = np.arange(0.0, 5.0, 0.1)
    5. t2 = np.arange(0.0, 5.0, 0.02)
    6. #
    7. plt.figure() # 这里不调用也行, 会默认自动调用
    8. # 创建第一个图(ax), 当前 plt 的上下文就是这张图, plt 的很多操作都是对这张图进行操作,比如 plot
    9. plt.subplot(211)
    10. plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
    11. # 创建第二个图,当前 plt 的上下文就是这张图, plt 的很多操作都是对这张图进行操作,比如 plot
    12. plt.subplot(212)
    13. plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
    14. plt.show()
    subplot(nrows, ncols, index, **kwargs)或者 subplot(pos, **kwargs), 这里211的意思就是2行1列第一个图

总结:

使用subplot默认创建这两种方式其实是一种类型, 所以总共就两种方式:

  • 一种是使用plt.subplot定义axes, 并选中当前的图,然后用plt的方法对其进行操作, 我们称之为直接使用pylot的方式, 这和matlib的使用方式一样;
  • 另外一种是使用plt.subplots函数,从返回值得到axes的对象, 然后用这些对象的方法对其进行操作, 我们称之为Object-Oriented(OO)(面向对象)的方式

官方推荐使用OO的方式, 至于原因, 我认为因为面向对象让代码看起来更优雅,更易读

不管用哪种方式, 一定要搞清楚这三个概念,用起来就会很顺手了

  • 一个窗口就是一个面板(figure),这个面板可以被保存成一张图片到文件系统
  • 一个面板里面可以有一个或者多个axes(坐标系或者图片)(代码中一般用ax表示axes中的一个图), 中文我认为就说成图就好了,把figure说成面板,这样就区分开了,或者按照第一印象把axes说成子图也行, 虽然不太准确,但是只要理解了也没啥毛病,就是容易让新手误解
  • 不要把 axesaxis 搞混, 后者是坐标系的某一个轴的意思, 这里axes不是axis的复数的意思, 这里是专有词,代表面板中的图

图表类型

  • plot: 折线图
  • scatter: 散点图
  • bar/barh: 柱状图
  • pie: 饼图
  • table: 表
  • fill: 填充曲线
  • 更多地可以在这里看到样例

填充数据

使用 plot 函数, 比如 plt.plot(x, y, fmt="og", label='linear') 或者 axes.plot(x, y, label='linear'), 具体的参数看文档

也可以在一句话中画多条线, 比如这里三条线

  1. plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')

这里的fmt参数用字符代表线形状颜色以及数据点样式等,也可以使用专用的关键字参数,比如colormarker(数据点样式), linestyle(线段样式)等: 线段样式 数据点样式 和 颜色 字符表 如下:

字符 描述
‘-‘ 实线样式
‘—‘ 短横线样式
‘-.’ 点划线样式
‘:’ 虚线样式
‘.’ 点标记
‘,’ 像素标记
‘o’ 圆标记
‘v’ 倒三角标记
‘^’ 正三角标记
‘<’ 左三角标记
‘>’ 右三角标记
‘1’ 下箭头标记
‘2’ 上箭头标记
‘3’ 左箭头标记
‘4’ 右箭头标记
‘s’ 正方形标记
‘p’ 五边形标记
‘*’ 星形标记
‘h’ 六边形标记 1
‘H’ 六边形标记 2
‘+’ 加号标记
‘x’ X 标记
‘D’ 菱形标记
‘d’ 窄菱形标记
‘|’ 竖直线标记
‘_’ 水平线标记
字符 颜色
‘b’ 蓝色
‘g’ 绿色
‘r’ 红色
‘c’ 青色
‘m’ 品红色
‘y’ 黄色
‘k’ 黑色
‘w’ 白色

也可以使用可以用字符串下标获取的对象比如字典:

  1. data = {
  2. "x" : np.arange(50),
  3. "y": np.arange(50)
  4. }
  5. plt.plot("x", "y", data = data)

注意目前只支持numpy.ndarray对象,其它的要先转换,比如:

  1. a = pandas.DataFrame(np.random.rand(4, 5), columns = list('abcde'))
  2. a_asarray = a.values
  3. # and to convert a numpy.matrix
  4. b = np.matrix([[1, 2], [3, 4]])
  5. b_asarray = np.asarray(b)

显示图片

  1. import matplotlib.pyplot as plt

读取图片:

  • 直接使用PIL.Image.open()
  1. from PIL import Image
  2. img_pil = Image.open(test_img)
  3. print(type(img_pil))
  • matplotlib 依赖于Pillow来读取读片:
    返回 np.ndarray 类型, 像素通道排序为[R, G, B]
  1. import matplotlib.image as mpimg\
  2. test_img = "../assets/Alice.jpg"
  3. img_mp = mpimg.imread(test_img)
  4. print(type(img_mp), img_mp.shape, img_mp[0][0])
  5. # <class 'numpy.ndarray'> (240, 320, 3) [235 140 74]
  • opencv读取:
    返回 np.ndarray 类型, 像素通道排列为[B, G, R]
  1. import cv2
  2. test_img = "../assets/Alice.jpg"
  3. img_cv = cv2.imread(test_img)
  4. print(type(img_cv), img_cv.shape, img_cv[0][0])
  5. # <class 'numpy.ndarray'> (240, 320, 3) [ 74 140 235]

这里要注意 opencvPillow的像素点数据排序不一样, 如果要给plt显示,需要先转换一下通道顺序

  1. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

显示图片

  1. plt.figure()
  2. plt.subplot(2, 1, 1)
  3. imgplot = plt.imshow(img_mp)
  4. plt.subplot(2, 1, 2)
  5. imgplot = plt.imshow(img_cv)
  6. plt.show()

插值显示

  1. # interpolation 插值显示
  2. # resize image to small image
  3. img_pil.thumbnail((32, 24), Image.ANTIALIAS)
  4. plt.subplot(2, 1, 1)
  5. imgplot = plt.imshow(img_pil)
  6. # display
  7. plt.subplot(2, 1, 2)
  8. imgplot = plt.imshow(img_pil, interpolation="bicubic")
  9. plt.show()

保存图片

  1. print(fig.canvas.get_supported_filetypes())
  2. # {'eps': 'Encapsulated Postscript', 'jpg': 'Joint Photographic Experts Group', 'jpeg': 'Joint Photographic Experts Group', 'pdf': 'Portable Document Format', 'pgf': 'PGF code for LaTeX', 'png': 'Portable Network Graphics', 'ps': 'Postscript', 'raw': 'Raw RGBA bitmap', 'rgba': 'Raw RGBA bitmap', 'svg': 'Scalable Vector Graphics', 'svgz': 'Scalable Vector Graphics', 'tif': 'Tagged Image File Format', 'tiff': 'Tagged Image File Format'}

使用savefig函数: 详情看这里

  1. savefig(fname, dpi=None, facecolor='w', edgecolor='w',
  2. orientation='portrait', papertype=None, format=None,
  3. transparent=False, bbox_inches=None, pad_inches=0.1,
  4. frameon=None, metadata=None)

对于subplots()返回的fig

  1. fig.savefig('fig.png', transparent=False, dpi=80, bbox_inches="tight")

或者

  1. plt.savefig('fig.png')

常用方法

可以在这里找到: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.html

前面提到的,一般用两种方式, 一种是使用plt.subplot定义axes, 并选中当前的图,然后用plt的方法对其进行操作;
另外一种是使用plt.subplots函数,从返回值得到axes的对象, 然后用这些对象的方法对其进行操作

列一些常用的方法, 参数可以在文档中看

  • pyplot(plt)的方法:

    • plot: 填充数据
    • axis: 设置轴的属性, 包括是否显示, 取值范围等, 比如 plt.axis([x_min, x_max, y_min, y_max]), 更多看文档
    • xlim/ylim: 轴的取值范围,超出的不显示,上面的函数也能实现
    • suptitle: 设置figure的标题
    • xlabe ylabel: 横坐标 纵坐标
    • title: axes 的标题
    • gride(bool): 现实网格
    • text(x,y,str): 特定坐标写字
    • annotate('local max', xy=(2, 1), xytext=(3, 1.5),arrowprops=dict(facecolor='black', shrink=0.05)): 注解标记
    • xscale/yscale: 轴按照某种函数的密度现实, 就是坐标轴刻度间隔不均等,而是按照函数分布
    • colorbar: 显示颜色图例
    • set_clim(self, vmin=None, vmax=None): 限制值范围
    • close: 关闭figure
    • legend: 显示图例
  • axes(来自subplots)的方法:

    • set: 设置图的属性,包括刻度标签, 轴标签,图标题, 值限制等,详情看api
    • set_xlim(): 设置x轴(x_axis)的坐标范围, y轴同理, 也可以ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
    • text: 写字
    • set_title(): 设置标题
    • title.set: 设置标题相关参数
    • set_xlabel(): 设置x轴标题,y轴同理
    • set_xticks: 设置x轴刻度值
    • set_xticklabels: 设置x轴刻度标签值
    • get_xticklabels: 获取x轴刻度标签值
    • ax.axvline(v, ls='--', color='r'): 在特定的图中加入一条竖直虚线
    • legend: 显示图例

显示中文

自定义字体文件:

  1. # from https://www.runoob.com/numpy/numpy-matplotlib.html
  2. import numpy as np
  3. from matplotlib import pyplot as plt
  4. import matplotlib
  5. # fname 为 你下载的字体库路径,注意 SourceHanSansSC-Bold.otf 字体的路径
  6. zhfont1 = matplotlib.font_manager.FontProperties(fname="SourceHanSansSC-Bold.otf")
  7. x = np.arange(1,11)
  8. y = 2 * x + 5
  9. plt.title("菜鸟教程 - 测试", fontproperties=zhfont1)
  10. # fontproperties 设置中文显示,fontsize 设置字体大小
  11. plt.xlabel("x 轴", fontproperties=zhfont1)
  12. plt.ylabel("y 轴", fontproperties=zhfont1)
  13. plt.plot(x,y)
  14. plt.show()

使用系统的字体:plt.rcParams['font.family']

  1. from matplotlib import pyplot as plt
  2. import matplotlib
  3. import numpy as np
  4. a=sorted([f.name for f in matplotlib.font_manager.fontManager.ttflist])
  5. for i in a:
  6. print(i)
  7. plt.rcParams['font.family']=['YouYuan']
  8. x = np.arange(1,11)
  9. y = 2 * x + 5
  10. plt.title("Matplotlib 中文")
  11. plt.xlabel("x axis caption")
  12. plt.ylabel("y axis caption")
  13. plt.plot(x,y,"og")
  14. plt.show()

效率

在大数据量时可能对渲染效率就有要求了

  • 比如下面画线段的代码, 后面一段渲染用的时间明显少很多
  1. import matplotlib
  2. # Setup, and create the data to plot
  3. y = np.random.rand(100000)
  4. y[50000:] *= 2
  5. y[np.geomspace(10, 50000, 400).astype(int)] = -1
  6. matplotlib.rcParams['path.simplify'] = True
  7. matplotlib.rcParams['path.simplify_threshold'] = 0.0
  8. plt.plot(y)
  9. plt.show()
  10. matplotlib.rcParams['path.simplify_threshold'] = 1.0
  11. plt.plot(y)
  12. plt.show()
  • 线段分块

    1. matplotlib.rcParams['agg.path.chunksize'] = 10000 # 默认是0
  • 锚点也可以少画来加快渲染

    1. plt.plot(x, y, markevery=10)
  • 图例也可以指定位置而不是自动选择最佳位置(loc='best'),减少计算量

    1. axes[0].legend(loc='best')
  • 使用 fast style

自动设置前面说的simplificationchunking

  1. import matplotlib.style as mplstyle
  2. mplstyle.use('fast')
  3. # 或者
  4. mplstyle.use(['dark_background', 'ggplot', 'fast'])

详细的说明参考官方文档

美化, 样式

有时候画出来的图可能显示不完全, 或者太拥挤等等,需要调整样式, 以及颜色等等

主要使用 style sheet 和rcParams, 参见: https://matplotlib.org/tutorials/introductory/customizing.html

使用 rcParams

详细的参数看这里: https://matplotlib.org/tutorials/introductory/customizing.html#the-matplotlibrc-file

  1. import matplotlib
  2. print(matplotlib.matplotlib_fname()) # '/home/foo/.config/matplotlib/matplotlibrc'
  3. mpl.rcParams['lines.linewidth'] = 2
  4. mpl.rcParams['lines.linestyle'] = '--'
  5. plt.plot(data)

整体颜色风格

使用style, 这里面有很多样例参考

使用plt.style.use('ggplot'), 参数可以传内置的, 也可以传样式文件路径(前面的rcParams文件), 可以用一个列表, 后面的会覆盖前面的属性

  1. print(plt.style.available)
  2. # ['Solarize_Light2', '_classic_test_patch', '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']
  3. plt.style.use('ggplot') # 参数可以传内置的, 也可以传样式文件路径, 可以用一个列表, 后面的会覆盖前面的属性
  4. fig, ax = plt.subplots()
  5. plt.show()

如果不想改变全局样式, 可以

  1. with plt.style.context('dark_background'):
  2. plt.plot(np.sin(np.linspace(0, 2 * np.pi)), 'r-o')
  3. plt.show()

自动缩放显示完全所有内容

使用rcParams, 缩放会导致图片变小,但是能看完全了

  1. plt.rcParams.update({'figure.autolayout': True})

constrained_layout

参见文档: https://matplotlib.org/tutorials/intermediate/constrainedlayout_guide.html#sphx-glr-tutorials-intermediate-constrainedlayout-guide-py

有时图像太拥挤, 可以用这个, 也可以用tight_layout, 并不一定每个图像都奏效, 太复杂的图还是需要自己手动调整

  1. plt.figure(constrained_layout=True)
  1. fig1, f1_axes = plt.subplots(ncols=2, nrows=2, constrained_layout=True)
  1. plt.rcParams['figure.constrained_layout.use'] = True

tight_layout

tight_layoutconstrained_layout 类似, 在生成figure时指定, tight_layout 也可以在画完元素后调用, 通常用这个效果不错

  1. Figure.set_tight_layout
  2. pyplot.tight_layout
  1. plt.figure(constrained_layout=False, tight_layout = True, figsize=(4,10))

或者

  1. img_num = 2
  2. plt.subplot(img_num, 1, 1)
  3. imgplot = plt.imshow(img_mp)
  4. plt.subplot(img_num, 1, 2)
  5. imgplot = plt.imshow(img_cv)
  6. plt.tight_layout()
  7. plt.show()

边距

  1. fig.subplots_adjust(right=.1)

设置轴标签样式

  • 设置轴标签角度
  1. fig, ax = plt.subplots()
  2. ax.barh(group_names, group_data)
  3. labels = ax.get_xticklabels()
  4. plt.setp(labels, rotation=45, horizontalalignment='right')

也可以

  1. ax.set_xticklabels(labels.keys(), rotation=45)
  • 设置轴标签格式

axis_formatter

  1. def currency(x, pos):
  2. """The two args are the value and tick position"""
  3. if x >= 1e6:
  4. s = '${:1.1f}M'.format(x*1e-6)
  5. else:
  6. s = '${:1.0f}K'.format(x*1e-3)
  7. return s
  8. ax.xaxis.set_major_formatter(currency)

示例

柱状图

  1. from matplotlib import pyplot as plt
  2. x = [5,8,10]
  3. y = [12,16,6]
  4. x2 = [6,9,11]
  5. y2 = [6,15,7]
  6. plt.bar(x, y, align = 'center')
  7. plt.bar(x2, y2, color = 'g', align = 'center')
  8. plt.title('Bar graph')
  9. plt.ylabel('Y axis')
  10. plt.xlabel('X axis')
  11. plt.show()

范围统计图

统计一组数据在特定范围类的数量, 比如下面统计一组数据的值分别在几个范围(0~100)的数量

histogram

  1. import numpy as np
  2. from matplotlib import pyplot as plt
  3. a = np.array([22,87,5,43,56,73,55,54,11,20,51,5,79,31,27])
  4. # numpy
  5. hist,bins = np.histogram(a,bins = [0,20,40,60,80,100])
  6. print (hist)
  7. print (bins)
  8. # matplotlib
  9. plt.hist(a, bins = [0,20,40,60,80,100])
  10. plt.title("histogram")
  11. plt.show()

colorbar

文档: https://matplotlib.org/tutorials/intermediate/constrainedlayout_guide.html#colorbars

tensorflow 显示训练记录(accuracy和loss)

  1. plt.plot(history.history['acc'], color='#2886EA', label="train")
  2. plt.plot(history.history['val_acc'], color = '#3FCD6D', linestyle = ':', marker = '.', label="valid")
  3. plt.title('model accuracy')
  4. plt.ylabel('accuracy')
  5. plt.xlabel('epoch')
  6. plt.legend()
  7. plt.savefig(out_path)
  8. plt.close()

matplot

  1. plt.figure()
  2. plt.xlabel('Epoch')
  3. plt.ylabel('accuracy')
  4. plt.plot(hist['epoch'], hist['acc'],
  5. label='Train accuracy')
  6. plt.plot(hist['epoch'], hist['val_acc'],
  7. label = 'Val accuracy')
  8. plt.ylim([0, 1])
  9. plt.legend()
  10. plt.savefig(out_path+".jpg")
  11. plt.close()

matplot2

文章有误?有想法想讨论?查看或者发起勘误/讨论 主题
(发起评论需要先登录 github)

/wallpaper/wallhaven-pk9kve.jpg