如何用imshow在Matplotlib中绘制非线性y轴的图像?
我想用Matplotlib把一个二维数组画成图片,并且让y轴的刻度是y值的平方的关系。
举个例子,我的数组第一行在图片中的高度是1,第二行的高度是4,依此类推(单位不重要)。用文字说不太好理解,所以请看这张图片(这就是我想要的效果):
alt text http://support.sas.com/rnd/app/da/new/802ce/iml/chap1/images/wavex1k.gif
从图中可以看到,第一行的高度是上面一行的两倍,依此类推。
对于那些想知道我为什么要这样做的人:
我有一个很大的数组(10, 700000),里面是浮点数,表示一个音频文件的离散小波变换系数。我想用这些系数画出标度图。虽然我可以把数组复制多次,直到得到想要的图片行高,但内存装不下这么多信息……
4 个回答
你可以看看 matplotlib.image.NonUniformImage。不过这个工具只是帮你处理不均匀的坐标轴而已——我觉得你想要的那种自适应绘图可能实现不了(因为图像中的每个点的面积总是相同的,所以你可能需要重复使用更宽的行)。你真的需要绘制完整的数组吗?显然,完整的细节在任何图表中都不会完全显示出来,所以我建议你大幅度减少原始矩阵的数据量,这样你就可以根据需要复制行,得到图像而不会耗尽内存。
我发现用matplotlib制作标度图的最好方法是使用imshow
,这和specgram
的实现方式类似。用矩形来做会很慢,因为你需要为每个值单独制作一个图形。同样,你也不想把所有东西都放进一个统一的NumPy数组里,因为这样很快就会耗尽内存,特别是你的最高级别大约会和信号的一半一样长。
这里有一个使用SciPy和PyWavelets的例子:
from pylab import *
import pywt
import scipy.io.wavfile as wavfile
# Find the highest power of two less than or equal to the input.
def lepow2(x):
return 2 ** floor(log2(x))
# Make a scalogram given an MRA tree.
def scalogram(data):
bottom = 0
vmin = min(map(lambda x: min(abs(x)), data))
vmax = max(map(lambda x: max(abs(x)), data))
gca().set_autoscale_on(False)
for row in range(0, len(data)):
scale = 2.0 ** (row - len(data))
imshow(
array([abs(data[row])]),
interpolation = 'nearest',
vmin = vmin,
vmax = vmax,
extent = [0, 1, bottom, bottom + scale])
bottom += scale
# Load the signal, take the first channel, limit length to a power of 2 for simplicity.
rate, signal = wavfile.read('kitten.wav')
signal = signal[0:lepow2(len(signal)),0]
tree = pywt.wavedec(signal, 'db5')
# Plotting.
gray()
scalogram(tree)
show()
你可能还想根据每个级别自适应地调整值。
这个方法对我来说效果不错。唯一的问题是matplotlib在级别之间会留下一条非常细的空隙。我还在寻找解决这个问题的方法。
附言 - 尽管这个问题现在已经有点旧了,但我还是想在这里回应一下,因为当我在谷歌上寻找使用MPL创建标度图的方法时,这个页面出现了。
你有没有尝试过转换坐标轴?比如说:
ax = subplot(111)
ax.yaxis.set_ticks([0, 2, 4, 8])
imshow(data)
这意味着在那些不存在的坐标位置,数据中必须有空缺,除非有办法提供一个转换函数,而不仅仅是列表(我从来没有尝试过)。
编辑:
我承认这只是一个线索,并不是完整的解决方案。下面我会详细解释我的意思。
假设你的数据在一个数组中,叫做 a
。你可以使用这样的转换:
class arr(object):
@staticmethod
def mylog2(x):
lx = 0
while x > 1:
x >>= 1
lx += 1
return lx
def __init__(self, array):
self.array = array
def __getitem__(self, index):
return self.array[arr.mylog2(index+1)]
def __len__(self):
return 1 << len(self.array)
基本上,它会用 mylog2
函数来转换数组或列表的第一个坐标(你可以根据自己的需要修改这个函数 - 它是一个简化版的 log2 函数)。这样做的好处是,如果你需要的话,可以重复使用这个转换,而且你也能很容易地控制它。
然后把你的数组映射到这个新的数组上,这样不会复制数据,而是创建一个在当前实例中的本地引用:
b = arr(a)
现在你可以显示它,比如说:
ax = subplot(111)
ax.yaxis.set_ticks([16, 8, 4, 2, 1, 0])
axis([-0.5, 4.5, 31.5, 0.5])
imshow(b, interpolation="nearest")
这里有一个示例(包含随机值的数组):
alt text http://img691.imageshack.us/img691/8883/clipboard01f.png