2017-01-04 15 views
2

Используя matplotlib.pyplot, у меня есть два графика. Один из них представляет собой сигнал аудиофайла. Вторая - спектрограмма одного и того же звука. Я хочу, чтобы форма волны была непосредственно над спектрограммой (такая же ось X и выровнялась вместе). Я также хочу использовать цветную панель для спектрограммы.Сделать matplotlib.pyplot диапазон цветовых полос двумя рядами вдоль формы волны и specgram

Проблема: когда я помещаю цветную панель, она присоединяется к ряду спектрограмм, и форма волны распространяется по цветной панели (т. Е. Больше не выравнивается по времени со спектрограммой и шире, чем спектрограмма).

Я близка к решению, я думаю, но я просто не могу понять, что я делаю неправильно или что изменить, чтобы заставить его работать так, как я хочу. Надеюсь, кто-то может указать мне в правильном направлении!

Используя следующий код питона (я сделал код как MWE как можно):

import matplotlib 
matplotlib.use("TkAgg") 
from scipy.io import wavfile 
from matplotlib import mlab 
from matplotlib import pyplot as plt 
import numpy as np 
from numpy.lib import stride_tricks 

samplerate, data = wavfile.read('FILENAME.wav') 

times = np.arange(len(data))/float(samplerate) 

plt.close("all") 

#### 
#Waveform 
#### 
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(13.6, 7.68)) 

plt.subplot(211) 
plt.plot(times, data, color='k') 

plt.xlabel('time (s)') 
plt.xlim(times[0], times[-1]) 

max_amp = max(abs(np.amin(data)), abs(np.amax(data))) 
min_amp = (max_amp * -1) - abs(np.amin(data) - np.amax(data))/50 
max_amp = max_amp + abs(np.amin(data) - np.amax(data))/50 

plt.ylim(min_amp, max_amp) 

ax = plt.gca() 
ax.set_yticks(np.array([min_amp, min_amp/2, 0, max_amp/2, max_amp])) 

ax.spines['bottom'].set_position('center') 
ax.spines['right'].set_color('none') 
ax.spines['top'].set_color('none') 

ax.xaxis.set_ticks_position('none') 
ax.yaxis.set_ticks_position('none') 

ax.xaxis.set_tick_params(pad=115) 

#### 
#Spectrogram 
#### 

Fs = 5000*2.#10000. 
NFFT = min(512, len(data)) 
noverlap = NFFT/2 
pad_to = NFFT * 16 
dynamicRange = 27.5 
vmin = 20*np.log10(np.max(data)) - dynamicRange 

cmap = plt.get_cmap('inferno') 

plt.subplot(212) 
Pxx, freqs, times, cax = plt.specgram(data, NFFT=NFFT, Fs=samplerate, noverlap=noverlap, mode='magnitude', scale='dB', vmin=vmin, pad_to=pad_to, cmap=cmap) 

axes_spec = plt.gca() 
axes_spec.set_xlim([0., max(times)]) 
axes_spec.set_ylim([0, 5000]) 

plt.xlabel("Time (s)") 
plt.ylabel("Frequency (hz)") 

plt.colorbar(cax, label='(dB)').ax.yaxis.set_label_position('left') 


plt.tight_layout() 
plt.show() 

я могу получить следующий сюжет:

Waveform above spectrogram plus colorbar

Делая эти незначительные изменения ниже, я может получить участок, чтобы посмотреть почти как я хочу. Проблема в том, что она создает пустую цифру рядом с цветной панелью. Эта версия, минус пустая цифра, является тем, что я пытаюсь создать.

#Replace this for waveform 
plt.subplot(221) 
#Replace this for spectrogram 
plt.subplot(223) 
#Add this before colorbar 
plt.subplot(122) 

Новая версия сюжета:

Waveform above spectrogram - both next to blank figure plus colorbar

EDIT: (! Или, возможно, и для хорошей мерой) Существует еще одна возможность, что я также хорошо с Waveform above spectrogram plus colorbar (but waveform/spectrogram are time-aligned)

+0

Вы видели эти ответы: http://stackoverflow.com/questions/13784201/matplotlib-2-subplots-1-colorbar –

+0

Да, но я не мог понять, как адаптировать это к этой ситуации. Я сделал много попыток включить подобные вещи из Интернета и вопросы здесь, но спектрограмма меня немного смущает, как включить ее в другую неспектрограмму. – whatisit

+0

Когда я пытаюсь использовать метод, указанный в ответе на эту ссылку, @ pablo-reyes, тогда я получаю это изображение: [два сюжета, перекрывающие цветную полосу] (http://imgur.com/a/uT9C3) – whatisit

ответ

2

Здесь является примером colorbar на основе одного из ответов в matplotlib-2-subplots-1-colorbar. Параметр pad в fig.colorbar используется для указания пространства между графиками и цветовой панелью, а aspect используется для указания соотношения сторон между высотой и шириной цветной панели. Specgram выводит изображение в качестве 4-го выходного параметра, поэтому я использую его для colorbar.

fig,axs = matplotlib.pyplot.subplots(ncols=1, nrows=2) 
N=1000; fs=10e3 
x = np.sin(np.arange(N))+np.random.random(N) 
spectrum, freqs, t, im = axs[1].specgram(x,Fs=fs, 
        cmap=matplotlib.cm.inferno,noverlap=255) 
axs[0].plot(np.arange(0,N)/fs,x,'-'); 
axs[0].set_xlim(t[0],t[-1]);axs[1].set_xlim(t[0],t[-1]) 
axcb = fig.colorbar(im, ax=axs.ravel().tolist(), pad=0.04, aspect = 30) 

enter image description here

Важно заметить, что когда fig.colorbar функция вызывается с помощью параметра ax, исходные участки будут изменены, чтобы освободить место для colorbar. Если он применяется только к одному из графиков, будет изменена только эта ось. Se ниже:

fig,axs = matplotlib.pyplot.subplots(ncols=1, nrows=2) 
N=1000; fs=10e3 
x = np.sin(np.arange(N))+np.random.random(N) 
spectrum, freqs, t, im = axs[1].specgram(x,Fs=fs, 
        cmap=matplotlib.cm.inferno,noverlap=255) 
axs[0].plot(np.arange(0,N)/fs,x,'-') 
axs[0].set_xlim(t[0],t[-1]);axs[1].set_xlim(t[0],t[-1]) 
axcb = fig.colorbar(im, ax=axs[1], pad=0.04, aspect = 30) 

enter image description here

Внизу показан способ управления изменением размера ваших исходных осей для того, чтобы освободить место для Colorbar с помощью fig.colorbar с параметром cax, что не будет изменять размеры в дальнейшем оригинал участки.Такой подход требует, чтобы вручную сделать некоторую комнату для вашего Colorbar с указанием параметра right внутри функции fig.subplots_adjust:

fig,axs = matplotlib.pyplot.subplots(ncols=1, nrows=2) 
N=1000; fs=10e3 
x = np.sin(np.arange(N))+np.random.random(N) 
spectrum, freqs, t, im = axs[1].specgram(x,Fs=fs, 
        cmap=matplotlib.cm.inferno,noverlap=255) 
axs[0].plot(np.arange(0,N)/fs,x,'-') 
axs[0].set_xlim(t[0],t[-1]);axs[1].set_xlim(t[0],t[-1]) 
fig.subplots_adjust(right=0.85) # making some room for cbar 
# getting the lower left (x0,y0) and upper right (x1,y1) corners: 
[[x10,y10],[x11,y11]] = axs[1].get_position().get_points() 
pad = 0.01; width = 0.02 
cbar_ax = fig.add_axes([x11+pad, y10, width, y11-y10]) 
axcb = fig.colorbar(im, cax=cbar_ax) 

enter image description here

И делает то же самое, чтобы охватить две строки, читая координаты исходных двух участков:

fig,axs = matplotlib.pyplot.subplots(ncols=1, nrows=2) 
N=1000; fs=10e3 
x = np.sin(np.arange(N))+np.random.random(N) 
spectrum, freqs, t, im = axs[1].specgram(x,Fs=fs, 
        cmap=matplotlib.cm.inferno,noverlap=255) 
axs[0].plot(np.arange(0,N)/fs,x,'-') 
axs[0].set_xlim(t[0],t[-1]);axs[1].set_xlim(t[0],t[-1]) 
fig.subplots_adjust(right=0.85) # making some room for cbar 
# getting the lower left (x0,y0) and upper right (x1,y1) corners: 
[[x00,y00],[x01,y01]] = axs[0].get_position().get_points() 
[[x10,y10],[x11,y11]] = axs[1].get_position().get_points() 
pad = 0.01; width = 0.02 
cbar_ax = fig.add_axes([x11+pad, y10, width, y01-y10]) 
axcb = fig.colorbar(im, cax=cbar_ax) 

enter image description here

+0

Это похоже на то, что находится в ссылке. Это отлично подходит для сгенерированных/случайных данных, но я до сих пор не вижу, как это можно легко адаптировать к случаю спектрограммы. Мне действительно нужно видеть и пример этого с помощью 'specgram', чтобы убедиться, что он адаптируется. Я также нашел хороший способ сделать это, используя 'matplotlib.GridSpec' - это не большой объем дополнительного кода, но больше, чем этот ответ, предоставлен. Метод «GridSpec» также позволяет переключаться между полной цветной панелью только на сторону спектрограммы. Мне было бы очень интересно узнать, может ли этот метод справиться с обоими этими вещами! – whatisit

+0

Раньше я использовал 'GridSpec'. Я также выделял некоторое пространство для colorbar. Но теперь мне нравится меньше строк кода, поэтому вот решение также использует вывод изображения specgram (первый пользователь 'specgram'. Я буду использовать это в своих исследованиях сейчас).Я тоже поставил синусоидальный сигнал. –

+0

Да, я определенно не против меньше кода! Можно ли изменить ваше редактирование, чтобы ограничить высоту цветной панели спектрограммой? Если да, можете ли вы включить оба примера? – whatisit

1

лучшее решение, которое я кулачок e up с есть subplot2grid() функция. Это требует использования subplots, который я не использовал изначально. Следуя этому методу, мне нужно было изменить все: от использования plt (matplotlib.pyplot) до использования осей для данного графика для каждого вызова .plot() или .specgram(). Соответствующие изменения включены здесь:

#No rows or columns need to be specified, because this is handled within a the `subplot2grid()` details 
fig, axes = plt.subplots(figsize=(13.6, 7.68)) 

#Setup for waveform 
ax1 = plt.subplot2grid((2, 60), (0, 0), rowspan=1, colspan=56) 
####WAVEFORM PLOTTING 

#Setup for spectrogram 
ax2 = plt.subplot2grid((2, 60), (1, 0), rowspan=1, colspan=56) 
####SPECTROGRAM PLOTTING 

#Setup for colorbar 
ax3 = plt.subplot2grid((2, 60), (0, 59), rowspan=1, colspan=1) 
cbar = plt.colorbar(cax, cax=ax3, ax=ax2) 

И MWE чего все это вместе:

import matplotlib as mpl 
mpl.use("TkAgg") 
from scipy.io import wavfile 
from matplotlib import mlab 
from matplotlib import pyplot as plt 
import matplotlib.gridspec as gridspec 
import numpy as np 
from numpy.lib import stride_tricks 

samplerate, data = wavfile.read('FILENAME.wav') 

times = np.arange(len(data))/float(samplerate) 

plt.close("all") 

fig, axes = plt.subplots(figsize=(13.6, 7.68))#nrows=2, ncols=2, 

gs = gridspec.GridSpec(2, 60) 

#### 
#Waveform 
#### 
ax1 = plt.subplot2grid((2, 60), (0, 0), rowspan=1, colspan=56) 
ax1.plot(times, data, color='k') 

ax1.xaxis.set_ticks_position('none') 
ax1.yaxis.set_ticks_position('none') 

#### 
#Spectrogram 
#### 
maxFrequency = 5000 
Fs = maxFrequency*2.#10000. 
NFFT = min(512, len(data)) 
noverlap = NFFT/2 
pad_to = NFFT * 16 
dynamicRange = 27.5 
vmin = 20*np.log10(np.max(data)) - dynamicRange 

cmap = plt.get_cmap('inferno') 

ax2 = plt.subplot2grid((2, 60), (1, 0), rowspan=1, colspan=56) 
Pxx, freqs, times, cax = ax2.specgram(data, NFFT=NFFT, Fs=samplerate, noverlap=noverlap, mode='magnitude', scale='dB', vmin=vmin, pad_to=pad_to, cmap=cmap) 

ax2.set_ylim([0, maxFrequency]) 
ax2.xaxis.set_ticks_position('none') 
ax2.yaxis.set_ticks_position('none') 

#### 
#Colorbar (for spectrogram) 
#### 
ax3 = plt.subplot2grid((2, 60), (1, 59), rowspan=1, colspan=1) 
cbar = plt.colorbar(cax, cax=ax3, ax=ax2) 
cbar.ax.yaxis.set_tick_params(pad=3, left='off', right='off', labelleft='on', labelright='off') 

plt.show() 

Вот пример вывода этой MWE:

Waveform above spectrogram plus colorbar on right side (of both)

Лучшая часть ! Вам нужно только изменить 0 к 1 и rowspan быть в этой строке 1 (то есть :)

ax3 = plt.subplot2grid((2, 60), (1, 59), rowspan=1, colspan=1) 

сделать пролет Colorbar только по высоте спектрограммы. Это означает, что изменение между двумя вариантами невероятно просто. Вот пример вывода из этого изменения:

Waveform above spectrogram plus colorbar on right side (of spectrogram only)

EDIT: GridSpec был на самом деле не используется, и поэтому я редактировал его. Единственные необходимые детали, которые мне нужны, заключались в вызове subplot2grid() для настройки подзаговоров.