有关Matplotlib的一些技巧

出处:点击打开链接

http://www.yeolar.com/note/2011/04/28/matplotlib-tips/

中文设置

首先是中文字体的设置问题。开始的时候不太会用,后来才发现Matplotlib对字体的支持其实是很完善的。有些输出如.eps等格式不支持中文,和格式有些关系。Matplotlib绘图输出为.png和.pdf格式时都能很好地支持中文。

主要有三种设置中文的方法:

  1. 直接读取指定的字体文件。这种方法的依赖性最小,缺点是要指定字体文件的路径。举个例子,在源码所在目录下有STZHONGS.TTF字体文件,那么可以像下面这样写:

    # -*- coding: utf-8 -*-
    
    from numpy import *
    import matplotlib.pyplot as plt
    from matplotlib import font_manager
    
    # 如果要保存为pdf格式,需要增加如下配置
    #from matplotlib import rcParams
    #rcParams["pdf.fonttype"] = 42
    
    chf = font_manager.FontProperties(fname='STZHONGS.TTF')
    
    plt.plot(arange(0, 10, 1), arange(0, 10, 1))
    plt.title(u'中文测试图样', fontproperties=chf)
    plt.legend((u'图例',), 'lower right', prop=chf)
    plt.savefig('test.png', format='png')    # 或者pdf
    
  2. 在脚本里设置使用系统字体。这种方法要依赖于系统的字体文件。比如:

    # -*- coding: utf-8 -*-
    
    from numpy import *
    import matplotlib.pyplot as plt
    from matplotlib import rcParams
    
    rcParams['font.family'] = 'STZhongSong'
    
    # 如果要保存为pdf格式,需要增加如下配置
    #rcParams["pdf.fonttype"] = 42
    
    plt.plot(arange(0, 10, 1), arange(0, 10, 1))
    plt.title(u'中文测试图样')
    plt.legend((u'图例',), 'lower right')
    plt.savefig('test.png', format='png')    # 或者pdf
    
  3. 第三种方法是修改Matplotlib的默认配置,相当于把脚本里的设置移到了配置文件中。设置mpl-data\matplotlibrc文件,下面是默认的配置:

    #font.family         : sans-serif
    #font.serif          : Bitstream Vera Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif
    #font.sans-serif     : Bitstream Vera Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif
    

可以去掉font.sans-serif行前的注释,并在字体列表开头添加STZhongSong;或者将font.family改为font.serif,然后同样在font.serif的字体列表开头添加STZhongSong。

现在就可以不在脚本里额外写任何字体相关的设置。

PNG图的DPI

Matplotlib可以设置生成的PNG图的DPI值,以适应不同的需求。PDF因为是矢量表示的,所以不需要这一设置。

通常显示器的DPI值为72像素/英寸,为了使生成的图像在显示器显示时清晰,应设置DPI值为72。有两种办法:在脚本里配置,通过配置文件配置。

比如在脚本里:

rcParams['savefig.dpi'] = 72

Matplotlib的rcParams的键和配置文件里的配置项是对应的。

而在需要打印的情况,就需要将DPI设置大些。比如打印机的DPI为600,可以设置DPI为600,或者300,如果值再小可能打印出来的图就会比较模糊。

绘图技巧

Matplotlib的绘图能力是很强大的,可以绘制各种数据图像,如曲线图、样点图、柱状图、积分图和三维图等等。

曲线图比较简单,通过 plot 函数进行绘制。如:

plt.plot(rank09, orders09tt, 'wo', markeredgecolor='green', markersize=7)
plt.plot(srank09, zipf_func(srank09, zplsqt09[0]), 'm--', linewidth=3)
plt.plot(srank09, mzipf_func(srank09, mplsqt09[0]), 'r-', linewidth=3)
plt.legend((u'节目点播热门度', u'Zipf拟合曲线', u'MZipf拟合曲线'), 'lower left', prop=chf)
plt.title(u'2009年总体的热门度分布', fontproperties=chf)
plt.xlabel(u'节目排序', fontproperties=chf)
plt.ylabel(u'节目点播次数', fontproperties=chf)
/media/note/2011/04/28/matplotlib-tip/fig3-3.png

曲线图样例

柱状图可以通过 bar 函数绘制。如:

p1 = plt.bar(xv, yv1, 0.45, color='g')
p2 = plt.bar(xv, yv-yv1, 0.45, color='y', bottom=yv1)
plt.xlim(0, 20)
plt.ylim(0, 40)
plt.title(u'完整文件缓存', fontproperties=chf)
plt.legend((p1[0], p2[0]), (u'已缓存', u'未缓存'), 'lower right', bbox_to_anchor=(1, 0.1), prop=chf)
plt.xlabel(u'节目按热门度排序', fontproperties=chf)
plt.ylabel(u'节目长度 (分钟)', fontproperties=chf)
/media/note/2011/04/28/matplotlib-tip/fig4-3.png

柱状图样例

注意这个例子中legend的实现方法,它指定了图和图例的对应关系,并使用 bbox_to_anchor 来调整位置。

积分图的绘制要麻烦一些,它需要用到axes,先用 plot 绘制曲线,然后用 add_patch 来绘制曲线下方的面积。如:

vt = [(0.1,1)] + zip(xv,yv) + [(1000,1)]
vt1 = [(0.1,1)] + zip(xv,yv1) + [(1000,1)]

ax = subplot(131)
plot(xv, yv, 'k-')
plot(xv, yv1, 'b-')
poly = Polygon(vt, facecolor='0.8', edgecolor='k', linewidth=2)
ax.add_patch(poly)
poly = Polygon(vt1, facecolor='b', edgecolor='b', alpha=0.5)
ax.add_patch(poly)
ax.set_title(u'对冷门文件不友好', fontproperties=chf)
/media/note/2011/04/28/matplotlib-tip/fig4-4.png

积分图样例

最后再给出一个横向柱状图,并且自绘制坐标的例子。

def make_xaxis(ax, yloc, offset=0.05, **props):
    xmin, xmax = ax.get_xlim()
    locs = [loc for loc in ax.xaxis.get_majorticklocs()
            if loc>=xmin and loc<=xmax]
    tickline, = ax.plot(locs, [yloc]*len(locs),linestyle='',
            marker=lines.TICKDOWN, **props)
    axline, = ax.plot([xmin, xmax], [yloc, yloc], **props)
    tickline.set_clip_on(False)
    axline.set_clip_on(False)
    for loc in locs:
        ax.text(loc, yloc-offset, '%d'%loc,
                horizontalalignment='center',
                verticalalignment='top')

def make_yaxis(ax, xloc=0, offset=0.05, **props):
    ymin, ymax = ax.get_ylim()
    locs = [loc for loc in ax.yaxis.get_majorticklocs()
            if loc>=ymin and loc<=ymax]
    tickline, = ax.plot([xloc]*len(locs), locs, linestyle='',
            marker=lines.TICKLEFT, **props)
    axline, = ax.plot([xloc, xloc], [ymin, ymax], **props)
    tickline.set_clip_on(False)
    axline.set_clip_on(False)
    for loc in locs:
        ax.text(xloc-offset, loc, '%d'%abs(loc),
                verticalalignment='center',
                horizontalalignment='right')

props = dict(color='black', linewidth=1, markeredgewidth=1)
fig = figure(facecolor='white')
fig.subplots_adjust(left=0.08, bottom=0.35, right=0.97, top=0.75, wspace=0.25, hspace=0.25)
ax = fig.add_subplot(121)
ax.axison = False
ax.barh(xv, yv1, 0.4, color='g')
yvm = yv1;        ax.barh(xv, yv2,  0.4, yvm, color='y')
yvm = yvm + yv2;  ax.barh(xv, yv3,  0.4, yvm, color='y')
# ...
yvm = yvm + yv38; ax.barh(xv, yv39, 0.4, yvm, color='y')
ax.set_xlim(0, 40)
ax.set_ylim(-12.4, 0)
ax.text(30, 1.5, u'节目长度 (分钟)', fontproperties=chf)
ax.text(-5, -14, u'节目热门度', fontproperties=chf)
ax.text(20, -14, u'播放时长增加 →', fontproperties=chf)
ax.text(45,  -6, u'热门度下降 →', rotation=270, fontproperties=chf)
make_xaxis(ax, 0, offset=-1, **props)
make_yaxis(ax, 0, offset=2, **props)
/media/note/2011/04/28/matplotlib-tip/fig4-5.png

绘制坐标样例