如何使用 imshow 使用 Matplotlib 绘制具有非线性 y 轴的图像?

发布于 2024-08-09 14:06:11 字数 547 浏览 5 评论 0原文

如何使用 Matplotlib 将 2D 数组绘制为图像,其 y 比例相对于 y 值的两个幂?

例如,我的数组的第一行在图像中的高度为 1,第二行的高度为 4,等等(单位无关) 用文字解释并不简单,所以请看这张图片(这就是我想要的结果):

替代文本 http://support.sas.com/rnd/app/da/new/802ce/iml/chap1/images/wavex1k.gif

如您所见,第一个第 1 行比上面第 1 行小 2 倍,以此类推。

对于那些对我为什么要尝试这样做感兴趣的人:

我有一个相当大的浮点数数组 (10, 700000),表示声音文件的离散小波变换系数。我正在尝试使用这些系数绘制尺度图。 我可以复制数组 x 次,直到获得所需的图像行大小,但内存无法容纳这么多信息......

How can I plot an 2D array as an image with Matplotlib having the y scale relative to the power of two of the y value?

For instance the first row of my array will have a height in the image of 1, the second row will have a height of 4, etc. (units are irrelevant)
It's not simple to explain with words so look at this image please (that's the kind of result I want):

alt text http://support.sas.com/rnd/app/da/new/802ce/iml/chap1/images/wavex1k.gif

As you can see the first row is 2 times smaller that the upper one, and so on.

For those interested in why I am trying to do this:

I have a pretty big array (10, 700000) of floats, representing the discrete wavelet transform coefficients of a sound file. I am trying to plot the scalogram using those coefficients.
I could copy the array x times until I get the desired image row size but the memory cannot hold so much information...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

从﹋此江山别 2024-08-16 14:06:11

您是否尝试过变换轴?例如:

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

这是一个示例(带有包含随机值的数组):

替代文本http://img691.imageshack.us/img691/8883/clipboard01f.png

Have you tried to transform the axis? For example:

ax = subplot(111)
ax.yaxis.set_ticks([0, 2, 4, 8])
imshow(data)

This means there must be gaps in the data for the non-existent coordinates, unless there is a way to provide a transform function instead of just lists (never tried).

Edit:

I admit it was just a lead, not a complete solution. Here is what I meant in more details.

Let's assume you have your data in an array, a. You can use a transform like this one:

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)

Basically it will transform the first coordinate of an array or list with the mylog2 function (that you can transform as you wish - it's home-made as a simplification of log2). The advantage is, you can re-use that for another transform should you need it, and you can easily control it too.

Then map your array to this one, which doesn't make a copy but a local reference in the instance:

b = arr(a)

Now you can display it, for example:

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

Here is a sample (with an array containing random values):

alt text http://img691.imageshack.us/img691/8883/clipboard01f.png

池予 2024-08-16 14:06:11

我发现使用 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 在级别之间创建了一个细如发丝的空间。我仍在寻找解决此问题的方法。

PS - 尽管这个问题现在已经很老了,但我想我应该在这里回答,因为当我在寻找使用 MPL 创建尺度图的方法时,这个页面出现在 Google 上。

The best way I've found to make a scalogram using matplotlib is to use imshow, similar to the implementation of specgram. Using rectangles is slow, because you're having to make a separate glyph for each value. Similarly, you don't want to have to bake things into a uniform NumPy array, because you'll probably run out of memory fast, since your highest level is going to be about as long as half your signal.

Here's an example using SciPy and 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()

You may also want to scale values adaptively per-level.

This works pretty well for me. The only problem I have is that matplotlib creates a hairline-thin space between levels. I'm still looking for a way to fix this.

P.S. - Even though this question is pretty old now, I figured I'd respond here, because this page came up on Google when I was looking for a method of creating scalograms using MPL.

紫南 2024-08-16 14:06:11

您可以查看 matplotlib.image.NonUniformImage。但这只会有助于拥有不均匀的轴 - 我认为你无法像你想要的那样自适应地绘制(我认为图像中的每个点总是具有相同的面积 - 所以你要必须多次使用更宽的行)。您有什么理由需要绘制完整的数组吗?显然,完整的细节不会显示在任何图中 - 因此我建议对原始矩阵进行大量下采样,以便您可以根据需要复制行以获得图像而不会耗尽内存。

You can look at matplotlib.image.NonUniformImage. But that only assists with having nonuniform axis - I don't think you're going to be able to plot adaptively like you want to (I think each point in the image is always going to have the same area - so you are going to have to have the wider rows multiple times). Is there any reason you need to plot the full array? Obviously the full detail isn't going to show up in any plot - so I would suggest heavily downsampling the original matrix so you can copy rows as required to get the image without running out of memory.

心不设防 2024-08-16 14:06:11

如果您希望两者都能够缩放节省内存,您可以“手动”绘图。 Matplotlib 允许您绘制矩形(它们将是您的“矩形像素”):

from matplotlib import patches
axes = subplot(111)
axes.add_patch(patches.Rectangle((0.2, 0.2), 0.5, 0.5))

请注意,轴的范围不是由 add_patch() 设置的,但您可以自己将它们设置为您想要的值(axes.set_xlim,...) 。

PS:我认为 thrope 的响应(matplotlib.image.NonUniformImage)实际上可以做你想做的事情,以比这里描述的“手动”方法更简单的方式!

If you want both to be able to zoom and save memory, you could do the drawing "by hand". Matplotlib allows you to draw rectangles (they would be your "rectangular pixels"):

from matplotlib import patches
axes = subplot(111)
axes.add_patch(patches.Rectangle((0.2, 0.2), 0.5, 0.5))

Note that the extents of the axes are not set by add_patch(), but you can set them yourself to the values you want (axes.set_xlim,…).

PS: I looks to me like thrope's response (matplotlib.image.NonUniformImage) can actually do what you want, in a simpler way that the "manual" method described here!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文