matplotlib技巧集

6240阅读 0评论2012-04-30 HyryStudio
分类:Python/Ruby

不连续点

当一条曲线中存在跳变点时,如果直接用plot()绘图会用一条直线将跳变前后的点连接起来。若不想绘制跳变处的连接线,可在数组的跳变处插入Nan值,下面的程序演示了这一方法。

import numpy as np
import pylab as pl

x = np.linspace(-1, 1, 1000)
y = np.select([x<-0.5, x<0, x<0.5, True], [x**2, x**3, 1-x, 1-x**2])

ax = pl.subplot(121)
pl.plot(x, y, lw=2)

pl.subplot(122, sharey=ax)
pos = np.where(np.abs(np.diff(y))>0.05)[0]+1
x2 = np.insert(x, pos, np.nan)
y2 = np.insert(y, pos, np.nan)

pl.plot(x2, y2, lw=2)
pl.show()

找到曲线中所有前后两个点的高度差大于0.05的下标,调用np.insert()在不连续点处插入np.nan。

参数曲线上绘制方向箭头

在参数曲线中为了表示随着参数的增大,曲线的变化情况,需要在曲线上绘制箭头表示方向。

import numpy as np
import pylab as pl

t = np.linspace(0, 2*np.pi, 1000)

x = np.sin(3*t)
y = np.cos(5*t)

def split_positions(x, y, n):
    dx = np.diff(x)
    dy = np.diff(y)
    l = np.sqrt(dx*dx+dy*dy)
    cl = np.cumsum(l)        
    pos = np.linspace(0, cl[-1], n)
    idx = np.searchsorted(cl, pos)
    dx = x[idx+1] - x[idx]        
    dy = y[idx+1] - y[idx]
    s = np.sqrt(dx*dx+dy*dy)
    dx/=s
    dy/=s
    return x[idx], y[idx], dx, dy

for x0, y0, dx, dy in zip(*cal_arrow(x, y, 18)):
    pl.arrow(x0, y0, dx*0.01, dy*0.01, head_width=0.05)

pl.plot(x, y)
pl.axis("equal")
pl.show()

split_positions(x, y, n)计算曲线(x,y)上等距离的n个点,以及在这n个点上的曲线切线方向。首先计算曲线上每个小段的长度l,对l用np.cumsum()进行累加,得到到达曲线上每点时所走过的曲线长度cl。cl[-1]为曲线的总长度,将这个总长度等分为n-1等分,然后通过np.searchsorted()搜索每个等分点所对应的下标。最后通过等分处前后两个点计算出曲线在此处的切线方向。

对split_positions()得到的每个点调用pl.arrow()绘制箭头,将dx和dy乘以一个很小的数(此处为0.01)是为了只绘制箭头,好让箭头能刚好位于曲线之上。

修改缺省刻度数目

在matplotlib中,图表的刻度的位置由Axis对象的major.locator属性决定,而子图对象Axes的xaxis和yaxis属性分别表示X轴和Y轴。

>>> import pylab as pl
>>> pl.plot([1,2,3])
[]
>>> pl.gca().xaxis.major.locator

缺省使用AutoLocator计算刻度的位置,下面是其代码:

class AutoLocator(MaxNLocator):
    def __init__(self):
        MaxNLocator.__init__(self, nbins=9, steps=[1, 2, 5, 10])

可以看出刻度数目由nbins决定,缺省值为9。nbins决定的是最大刻度线数,根据当前的显示范围和steps的设置,AutoLocator会自动调节刻度线数。

当刻度文本较长时,过多的刻度数会导致刻度文本不易看清,因此我们希望能修改缺省的刻度数目。但由上面的代码可知,我们无法通过修改matplotlib的参数配置修改缺省刻度数。这时可以通过重新定义AutoLocator.__init__()实现这一功能。

import numpy as np
import pylab as pl
from matplotlib import ticker

def AutoLocatorInit(self):
    ticker.MaxNLocator.__init__(self, nbins=4, steps=[1, 2, 5, 10])
ticker.AutoLocator.__init__ = AutoLocatorInit

pl.rcParams["xtick.labelsize"] = 20
pl.rcParams["ytick.labelsize"] = 20
pl.plot(pl.randn(150), lw=2)
pl.show()

定义了一个和AutoLocator.__init__()类似的函数,只是将nbins参数改为4,然后将AutoLocator.__init__改为使用此函数。

Y轴不同区间使用不同颜色填充

要求实现的效果如下图所示,根据Y轴的范围,使用不同的颜色对区域进行填充。

填充区域的最小值和最大值由mintemps和maxtemps给出,而每个范围的颜色由colors给出:

maxtemps = [ 25, 24, 24, 25, 26, 27, 22, 21, 22, 19, 17, 14,
             13, 12, 11, 12, 11, 10, 9, 9, 9, 8, 9, 9, 8 ]
mintemps = [ 21, 22, 22, 22, 23, 24, 18, 17, 16, 14, 10, 8,
             8, 7, 7, 6, 5, 5, 5, 4, 4, 4, 3, 4, 3 ]
times = list(xrange(len(mintemps)))

colors = [
    (25, '#FF69B4'),
    (20, '#F08228'),
    (15, '#E6AF2D'),
    (10, '#E6DC32'),
    (5, '#A0E632'),
    (0, '#00DC00')
]

填充区域可使用fill_between()实现,但是它只支持一种颜色,因此需要对每个颜色范围调用一次fill_between()。本题的难点在于如何计算整个区域与某个范围相交的部分。我们可以使用NumPy的线性插值函数interp()和裁剪函数clip()快速实现这一运算。下面是完整的源程序:

import pylab as pl
import numpy as np
x = np.linspace(times[0], times[-1], 500)
maxt = np.interp(x, times, maxtemps)
mint = np.interp(x, times, mintemps)

last_level = np.inf
for level, color in colors:
    tmp_min = np.clip(mint, level, last_level)
    tmp_max = np.clip(maxt, level, last_level)
    pl.fill_between(x, tmp_min, tmp_max, lw=0, color=color)
    last_level = level

pl.grid(True)
pl.show()

对mintemps和maxtemps用np.interp()进行线性插值,得到更细分的数据。对colors中的每个区域进行循环,并使用np.clip()对细分之后的最大值和最小值进行剪裁。调用pl.fill_between()使用指定的颜色对裁剪之后的区域进行填充。

上一篇:用guiqwt制作实时波形绘图程序
下一篇:制作IPython notebook的便携环境