Как построить изображение с нелинейной осью Y с Matplotlib, используя imshow?

Как я могу построить двумерный массив как изображение с Matplotlib, имеющим масштаб y относительно степени двух значений y?

Например, первая строка моего массива будет иметь высоту в изображении 1, вторая строка будет иметь высоту 4 и т. Д. (Единицы измерения не имеют значения) Объяснить словами непросто, поэтому посмотрите на это изображение, пожалуйста (это какой результат я хочу)

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

Как видите, первый ряд в 2 раза меньше верхнего и так далее.

Для тех, кто интересуется, почему я пытаюсь это сделать:

У меня есть довольно большой массив (10, 700000) с плавающей точкой, представляющий дискретные коэффициенты вейвлет-преобразования звукового файла. Я пытаюсь построить скалограмму, используя эти коэффициенты. Я мог бы скопировать массив x раз, пока не получу желаемый размер строки изображения, но память не может хранить столько информации...

4 ответа

Решение

Вы пытались преобразовать ось? Например:

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

Лучший способ сделать скалограмму с использованием 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 - Несмотря на то, что этот вопрос довольно старый, я решил ответить здесь, потому что эта страница появилась в Google, когда я искал способ создания скалограмм с использованием MPL.

Вы можете посмотреть на matplotlib.image.NonUniformImage. Но это только помогает иметь неоднородную ось - я не думаю, что вы будете способны строить адаптивно, как вы хотите (я думаю, что каждая точка на изображении всегда будет иметь одну и ту же область - так что вы собираетесь должны иметь более широкие ряды несколько раз). Есть ли какая-то причина, по которой вам нужно построить полный массив? Очевидно, что полная детализация не будет отображаться ни на одном графике, поэтому я бы посоветовал значительно уменьшить исходную матрицу, чтобы вы могли копировать строки по мере необходимости, чтобы получить изображение без нехватки памяти.

Если вы хотите, чтобы оба имели возможность масштабировать и сохранять память, вы можете сделать рисунок "от руки". 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) действительно может делать то, что вы хотите, более простым способом, чем описанный здесь "ручной" метод!

Другие вопросы по тегам