對(duì)齊左右Y軸
使用twinx()可以創(chuàng)建一個(gè)共用X軸的Axes對(duì)象,,它使用右側(cè)的Y軸,,其刻度比例獨(dú)立于左側(cè)的Y軸。雖然左右兩個(gè)Y軸的刻度比例不同,,但是有時(shí)我們希望其中的某兩個(gè)刻度是對(duì)齊的,,例如兩個(gè)Y軸的0刻度水平對(duì)齊。下面的align_yaxis()實(shí)現(xiàn)這一功能,,它將ax1的Y軸中的v1與ax2的Y軸中的v2水平對(duì)齊,。
def align_yaxis(ax1, v1, ax2, v2):
"""adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1"""
_, y1 = ax1.transData.transform((0, v1)) ?
_, y2 = ax2.transData.transform((0, v2)) ?
inv = ax2.transData.inverted() ?
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2)) ?
miny, maxy = ax2.get_ylim()
ax2.set_ylim(miny+dy, maxy+dy) ?
首先分別將兩個(gè)Axes對(duì)象的數(shù)據(jù)坐標(biāo)系中的坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)系,這樣我們就可以計(jì)算v1和v2這兩個(gè)刻度水平方向上在屏幕坐標(biāo)系中的差值,。我們將通過(guò)調(diào)整右側(cè)Y軸的數(shù)據(jù)顯示范圍使它的與左側(cè)Y軸對(duì)齊,。?inv是將屏幕坐標(biāo)系轉(zhuǎn)換為ax2的數(shù)據(jù)坐標(biāo)系的坐標(biāo)轉(zhuǎn)換對(duì)象,。
將屏幕坐標(biāo)系的差值轉(zhuǎn)換為ax2的數(shù)據(jù)坐標(biāo)系中的值dy,然后通過(guò)set_ylim()將Y軸的顯示范圍平移dy,。
下面是使用align_yaxis()對(duì)齊左右Y軸0刻度的例子,。
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
x = np.linspace(0, 1, 100)
y1 = np.sin(10*x)
y2 = x**2-0.5
ax1.plot(x, y1, "r", lw=2)
ax2.plot(x, y2, "g", lw=2)
align_yaxis(ax1, 0, ax2, 0)
plt.show()
程序的輸出如下圖所示:
自定義colorbar
使用colorbar可以表示二維數(shù)組中各點(diǎn)的值,缺省的colorbar將使用顏色映射表將數(shù)值轉(zhuǎn)換成其對(duì)應(yīng)的顏色,,有時(shí)候我們需要使用自定義的顏色表示各個(gè)數(shù)值,,例如:
from scipy.ndimage import label, imread
import pylab as pl
img = imread("label_image.png")
img = img > 200
labeled, max_label = label(img)
pl.figure()
pl.subplot(121)
pl.imshow(img, cmap=pl.cm.gray) ?
pl.subplot(122)
pl.imshow(labeled) ?
pl.colorbar()
pl.show()
上面的程序使用scipy.ndimage.label()對(duì)下圖左側(cè)的二值圖像中的各個(gè)白色區(qū)域進(jìn)行編號(hào),右側(cè)的彩色圖像顯示的是編號(hào)之后結(jié)果,。
左側(cè)的圖像使用cm.gray灰度顏色映射,?使用配置文件中定義的缺省的顏色映射,,可以通過(guò)pl.rcParams["image.cmap"]獲得此配置,,matplotlib的缺省配置為’jet’。此顏色映射將最小值顯示為藍(lán)色,,而最大值顯示為紅色,。而我們希望值為0的部分仍然使用黑色背景顯示,而從1開始到最大的編號(hào)使用’jet’的顏色映射顯示,。
下面我們通過(guò)自定義顏色映射,,將0映射為黑色,而將1到max_label使用’jet’中的顏色進(jìn)行映射,。
import numpy as np
from scipy.ndimage import label, imread
from matplotlib.colors import ListedColormap
import pylab as pl
img = imread("label_image.png")
img = img > 200
labeled, max_label = label(img)
pl.figure()
colors = [(0.0,0.0,0.0)] ?
colors.extend(pl.cm.jet(np.linspace(0, 1, max_label))) ?
cmap = ListedColormap(colors) ?
im = pl.imshow(labeled, cmap=cmap)
cax = pl.colorbar()
pl.show()
我們需要?jiǎng)?chuàng)建一個(gè)顏色列表colors,,其中各個(gè)下標(biāo)的顏色為編號(hào)圖像中各個(gè)編號(hào)所對(duì)應(yīng)的顏色,其中編號(hào)0對(duì)應(yīng)黑色,。?使用jet顏色映射創(chuàng)建從藍(lán)色到紅色的max_label個(gè)顏色,。顏色映射對(duì)象可以被調(diào)用,獲得其參數(shù)中各個(gè)數(shù)值對(duì)應(yīng)的顏色,。0對(duì)應(yīng)藍(lán)色,,1對(duì)應(yīng)紅色,例如:
>>> pl.cm.jet(0)
(0.0, 0.0, 0.5, 1.0)
這里通過(guò)np.linspace()創(chuàng)建從0到1的max_label個(gè)元素的等差數(shù)組,,并傳遞給pl.cm.jet()轉(zhuǎn)換為顏色,,最后添加進(jìn)colors列表。?使用colors列表創(chuàng)建一個(gè)ListedColormap對(duì)象,,它將數(shù)值轉(zhuǎn)換為其中以此數(shù)值為下標(biāo)的顏色,。在用pl.imshow()顯示編號(hào)圖像時(shí)通過(guò)cmap參數(shù)將ListedColormap對(duì)象傳遞給它。最后的結(jié)果如下圖所示:
選擇曲線
通過(guò)對(duì)Figure.canvas.mpl_connect()可以處理圖表中元素的點(diǎn)選事件,,下面的curve_point_distance()計(jì)算曲線curve_points與點(diǎn)point之間的距離,。
def curve_point_distance(curve_points, point):
x, y = point
xdata, ydata = curve_points.T
index = np.arange(len(xdata))
index2 = np.linspace(0, index[-1], 2000)
xdata2 = np.interp(index2, index, xdata) ?
ydata2 = np.interp(index2, index, ydata)
d = np.sqrt((xdata2-x)**2. + (ydata2-y)**2.) ?
return np.min(d)
參數(shù)curve_points是一個(gè)形狀為(N, 2)的數(shù)組,保存曲線上每個(gè)點(diǎn)的坐標(biāo),。通常為了計(jì)算點(diǎn)到曲線的距離,,需要計(jì)算點(diǎn)到構(gòu)成曲線的所有線段的距離,,并取其中的最小距離。點(diǎn)到線段的距離計(jì)算涉及到一些平面幾何的運(yùn)算,,稍微有些復(fù)雜,,這里采取了一種更加簡(jiǎn)潔的方式:對(duì)曲線進(jìn)行線性插值。?通過(guò)對(duì)曲線各點(diǎn)的X軸坐標(biāo)和Y軸坐標(biāo)分別進(jìn)行線性插值,,得到了曲線上更密集的點(diǎn)的坐標(biāo),。?然后計(jì)算point到這些插值點(diǎn)之間的距離,并返回所有距離的最小值,。
有了上面的函數(shù),,我們可以很方便地寫出選取曲線的程序:
def line_picker(line, evt):
if evt.xdata is None: return False, dict()
curve_points = line.axes.transData.transform(np.array(line.get_data()).T) ?
distance = curve_point_distance(curve_points, (evt.x, evt.y))
if distance < 5:
return True, {} ?
else:
return False, {}
def on_pick(evt):
print evt.artist
x = np.linspace(0, 10, 1000)
pl.plot(x, np.sin(x), picker=line_picker, label="sin(x)") ?
pl.plot(x, np.cos(x), picker=line_picker, label="cos(x)")
pl.plot(x, np.sin(3*x), picker=line_picker, label="sin(3x)")
pl.legend()
pl.gcf().canvas.mpl_connect('pick_event', on_pick) ?
pl.show()
在調(diào)用plot()繪制曲線時(shí),傳遞一個(gè)line_picker()給picker參數(shù),。當(dāng)觸發(fā)點(diǎn)選事件時(shí),,將對(duì)每條曲線依次調(diào)用line_picker(),它的第一參數(shù)為表示曲線的Line2D對(duì)象,,第二個(gè)參數(shù)為表示鼠標(biāo)事件的MouseEvent對(duì)象,。
在line_picker()中,調(diào)用Line2D對(duì)象的get_data()獲得曲線在數(shù)據(jù)坐標(biāo)系中的坐標(biāo),,并通過(guò)axes.transData.transform()將其轉(zhuǎn)換成屏幕坐標(biāo)系中的坐標(biāo),。當(dāng)在屏幕坐標(biāo)系中,鼠標(biāo)的點(diǎn)擊坐標(biāo)與曲線之間的距離小于5個(gè)像素時(shí),,返回True表示此曲線被選中,。返回值是一個(gè)元組,其中的第二個(gè)元素用于保存額外的選取信息,,這里直接使用空字典,。
最后使用on_pick()處理’pick_event’事件,其參數(shù)是一個(gè)PickEvent對(duì)象,,其中的artist屬性保存被選中的對(duì)象,。當(dāng)鼠標(biāo)距離多條曲線都小于5時(shí),這些曲線將依次被選中,,并調(diào)用on_pick(),。
下面是程序的運(yùn)行截圖,以及點(diǎn)選曲線之后的輸出:
Line2D(sin(3x))
Line2D(cos(x))
Line2D(sin(x))
|