使用twinx()可以创建一个共用X轴的Axes对象,它使用右侧的Y轴,其刻度比例独立于左侧的Y轴。虽然左右两个Y轴的刻度比例不同,但是有时我们希望其中的某两个刻度是对齐的,例如两个Y轴的0刻度水平对齐。下面的align_yaxis()实现这一功能,它将ax1的Y轴中的v1与ax2的Y轴中的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) ❹
❶首先分别将两个Axes对象的数据坐标系中的坐标转换为屏幕坐标系,这样我们就可以计算v1和v2这两个刻度水平方向上在屏幕坐标系中的差值。我们将通过调整右侧Y轴的数据显示范围使它的与左侧Y轴对齐。❷inv是将屏幕坐标系转换为ax2的数据坐标系的坐标转换对象。
❸将屏幕坐标系的差值转换为ax2的数据坐标系中的值dy,然后通过set_ylim()将Y轴的显示范围平移dy。
下面是使用align_yaxis()对齐左右Y轴0刻度的例子。
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将使用颜色映射表将数值转换成其对应的颜色,有时候我们需要使用自定义的颜色表示各个数值,例如:
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()对下图左侧的二值图像中的各个白色区域进行编号,右侧的彩色图像显示的是编号之后结果。
❶左侧的图像使用cm.gray灰度颜色映射,❷使用配置文件中定义的缺省的颜色映射,可以通过pl.rcParams["image.cmap"]获得此配置,matplotlib的缺省配置为’jet’。此颜色映射将最小值显示为蓝色,而最大值显示为红色。而我们希望值为0的部分仍然使用黑色背景显示,而从1开始到最大的编号使用’jet’的颜色映射显示。
下面我们通过自定义颜色映射,将0映射为黑色,而将1到max_label使用’jet’中的颜色进行映射。
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()
❶我们需要创建一个颜色列表colors,其中各个下标的颜色为编号图像中各个编号所对应的颜色,其中编号0对应黑色。❷使用jet颜色映射创建从蓝色到红色的max_label个颜色。颜色映射对象可以被调用,获得其参数中各个数值对应的颜色。0对应蓝色,1对应红色,例如:
(0.0, 0.0, 0.5, 1.0)
这里通过np.linspace()创建从0到1的max_label个元素的等差数组,并传递给pl.cm.jet()转换为颜色,最后添加进colors列表。❸使用colors列表创建一个ListedColormap对象,它将数值转换为其中以此数值为下标的颜色。在用pl.imshow()显示编号图像时通过cmap参数将ListedColormap对象传递给它。最后的结果如下图所示:
通过对Figure.canvas.mpl_connect()可以处理图表中元素的点选事件,下面的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)
参数curve_points是一个形状为(N, 2)的数组,保存曲线上每个点的坐标。通常为了计算点到曲线的距离,需要计算点到构成曲线的所有线段的距离,并取其中的最小距离。点到线段的距离计算涉及到一些平面几何的运算,稍微有些复杂,这里采取了一种更加简洁的方式:对曲线进行线性插值。❶通过对曲线各点的X轴坐标和Y轴坐标分别进行线性插值,得到了曲线上更密集的点的坐标。❷然后计算point到这些插值点之间的距离,并返回所有距离的最小值。
有了上面的函数,我们可以很方便地写出选取曲线的程序:
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()
❶在调用plot()绘制曲线时,传递一个line_picker()给picker参数。当触发点选事件时,将对每条曲线依次调用line_picker(),它的第一参数为表示曲线的Line2D对象,第二个参数为表示鼠标事件的MouseEvent对象。
❷在line_picker()中,调用Line2D对象的get_data()获得曲线在数据坐标系中的坐标,并通过axes.transData.transform()将其转换成屏幕坐标系中的坐标。当在屏幕坐标系中,鼠标的点击坐标与曲线之间的距离小于5个像素时,返回True表示此曲线被选中。返回值是一个元组,其中的第二个元素用于保存额外的选取信息,这里直接使用空字典。
❹最后使用on_pick()处理’pick_event’事件,其参数是一个PickEvent对象,其中的artist属性保存被选中的对象。当鼠标距离多条曲线都小于5时,这些曲线将依次被选中,并调用on_pick()。
下面是程序的运行截图,以及点选曲线之后的输出:
Line2D(cos(x))
Line2D(sin(x))