Chinaunix首页 | 论坛 | 博客
  • 博客访问: 962385
  • 博文数量: 33
  • 博客积分: 803
  • 博客等级: 军士长
  • 技术积分: 1755
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-05 18:58
个人简介

《Python科学计算》的作者

文章分类

全部博文(33)

文章存档

2016年(1)

2014年(2)

2013年(3)

2012年(27)

分类: Python/Ruby

2012-05-29 07:12:16

对齐左右Y轴

使用twinx()可以创建一个共用X轴的Axes对象,它使用右侧的Y轴,其刻度比例独立于左侧的Y轴。虽然左右两个Y轴的刻度比例不同,但是有时我们希望其中的某两个刻度是对齐的,例如两个Y轴的0刻度水平对齐。下面的align_yaxis()实现这一功能,它将ax1的Y轴中的v1与ax2的Y轴中的v2水平对齐。

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)

首先分别将两个Axes对象的数据坐标系中的坐标转换为屏幕坐标系,这样我们就可以计算v1和v2这两个刻度水平方向上在屏幕坐标系中的差值。我们将通过调整右侧Y轴的数据显示范围使它的与左侧Y轴对齐。inv是将屏幕坐标系转换为ax2的数据坐标系的坐标转换对象。

将屏幕坐标系的差值转换为ax2的数据坐标系中的值dy,然后通过set_ylim()将Y轴的显示范围平移dy。

下面是使用align_yaxis()对齐左右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可以表示二维数组中各点的值,缺省的colorbar将使用颜色映射表将数值转换成其对应的颜色,有时候我们需要使用自定义的颜色表示各个数值,例如:

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()对下图左侧的二值图像中的各个白色区域进行编号,右侧的彩色图像显示的是编号之后结果。

左侧的图像使用cm.gray灰度颜色映射,使用配置文件中定义的缺省的颜色映射,可以通过pl.rcParams["image.cmap"]获得此配置,matplotlib的缺省配置为’jet’。此颜色映射将最小值显示为蓝色,而最大值显示为红色。而我们希望值为0的部分仍然使用黑色背景显示,而从1开始到最大的编号使用’jet’的颜色映射显示。

下面我们通过自定义颜色映射,将0映射为黑色,而将1到max_label使用’jet’中的颜色进行映射。

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()

我们需要创建一个颜色列表colors,其中各个下标的颜色为编号图像中各个编号所对应的颜色,其中编号0对应黑色。使用jet颜色映射创建从蓝色到红色的max_label个颜色。颜色映射对象可以被调用,获得其参数中各个数值对应的颜色。0对应蓝色,1对应红色,例如:

>>> pl.cm.jet(0)
(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之间的距离。

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)

参数curve_points是一个形状为(N, 2)的数组,保存曲线上每个点的坐标。通常为了计算点到曲线的距离,需要计算点到构成曲线的所有线段的距离,并取其中的最小距离。点到线段的距离计算涉及到一些平面几何的运算,稍微有些复杂,这里采取了一种更加简洁的方式:对曲线进行线性插值。通过对曲线各点的X轴坐标和Y轴坐标分别进行线性插值,得到了曲线上更密集的点的坐标。然后计算point到这些插值点之间的距离,并返回所有距离的最小值。

有了上面的函数,我们可以很方便地写出选取曲线的程序:

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()

在调用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(sin(3x))
Line2D(cos(x))
Line2D(sin(x))
阅读(6372) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~