Remove the microseconds from matplotlib spectrogram - python

I've been trying to plot an spectogram based on a wav file of 15 minutes lenght. I think I managed to do this, but I can't remove the microseconds from my x axis ( time axis). Any help with this, please?
This is the spectrogram obtained:
This is my code:
import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile
import matplotlib.ticker as ticker
from matplotlib.dates import DateFormatter, MinuteLocator
import time
# Prettify
import matplotlib
import datetime
matplotlib.rc('figure', figsize=(17, 5))
cmap = plt.get_cmap('plasma') # this may fail on older versions of matplotlib
vmin = -40 # hide anything below -40 dB
cmap.set_under(color='k', alpha=None)
rate, frames = wavfile.read("audio_test.wav")
fig, ax = plt.subplots()
pxx, freq, t, cax = ax.specgram(frames[:, 0], # first channel
Fs=rate, # to get frequency axis in Hz
cmap=cmap, vmin=vmin)
cbar = fig.colorbar(cax)
cbar.set_label('Intensity dB')
ax.axis("tight")
ax.set_xlabel('time h:mm:ss')
ax.set_ylabel('frequency kHz')
scale = 1e3 # KHz
ticks = matplotlib.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))
ax.yaxis.set_major_formatter(ticks)
def timeTicks(x, pos):
d = datetime.timedelta(seconds=x)
return str(d)
#formatter = matplotlib.ticker.FuncFormatter(timeTicks)
#ax.xaxis.set_major_formatter(formatter)
majorFormatter = matplotlib.dates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(majorFormatter)
ax.xaxis.set_major_locator(ticker.IndexLocator(base=120, offset=60))
#ax.text(0.0, 0.1, "IndexLocator(base=0.5, offset=0.25)",
# fontsize=14, transform=ax.transAxes)
plt.show()

Using the code before your edit, you can change the return of def timeTicks(x, pos) in:
return str(d)[:7]

Related

No spectrogram generated in PyCharm but working in Spyder

I have written a code with librosa module, to generate spectrogram of an audio file and other works. Apparently it works fine with Spyder where the output is generated after taking a while, but for PyCharm, it just gets executed. No output spectrogram.
I am fairly new to PyCharm environment please help.
My code is:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
audio_name = 'C:\\Users\\USER\\Desktop\\Songs\\abc.wav'
hop_length = 512
window_size = 2048
import librosa
y, sr = librosa.load(audio_name) #y=time series(one-dimensional NumPy floating point array), sr= sampling rate of y, that is, the number of samples per second of audio. By default, all audio is mixed to mono and resampled to 22050 Hz at load time
window = np.hanning(window_size)
out = librosa.core.spectrum.stft(y, n_fft = window_size, hop_length = hop_length, window=window)
out = 2 * np.abs(out) / np.sum(window)
import librosa.display
librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), y_axis='log', x_axis='time')
''' To store spectrogram
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig = plt.figure()
canvas = FigureCanvasAgg(fig)
ax = fig.add_subplot(111)
p = librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), ax=ax, y_axis='log', x_axis='time')
fig.savefig('C:\\Users\\USER\\Desktop\\Songs\\spec.png')
'''
onset_env = librosa.onset.onset_strength(y=y, sr=sr, aggregate=np.median)
tempo, beats = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr)
fig, ax = plt.subplots(nrows=2, sharex=True)
times = librosa.times_like(onset_env, sr=sr, hop_length=hop_length)
M = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=hop_length)
librosa.display.specshow(librosa.power_to_db(M, ref=np.max), y_axis='mel', x_axis='time', hop_length=hop_length, ax=ax[0])
ax[0].label_outer()
ax[0].set(title='Mel spectrogram')
ax[1].plot(times, librosa.util.normalize(onset_env), label='Onset strength')
ax[1].vlines(times[beats], 0, 1, alpha=0.5, color='r', linestyle='--', label='Beats')
ax[1].legend()
The output in Spyder (After taking a bit of time):
The output of PyCharm after execution:
Update: Apparently it works when I give a plt.show() at the end as referred by #Mandera. But can someone tell me why it works in Spyder even without this?

How to put r- and theta-labels plus units on a polar Matplotlib plot (wedge diagram)?

I have plotted a wedge-diagram, but can not seem to figure out how to get labels on the axis and units on the values. The r-axis has to be Dec and the theta-axis has to be RA, both with units of degrees.
Here is my code, hope you can help:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
theta = (np.pi/180)*np.array([340.555906,3.592373,32.473440,33.171584,35.463857,44.268397,339.362504,345.211906,346.485567,346.811945,348.672405,349.180736,349.370850,353.098343])
r = np.array([-32.906663,-33.842402,-32.425917,-32.677975, -30.701083,-31.460307,-32.909861,-30.802969,-33.683759,-32.207783,-33.068686,-33.820102,-31.438195,-31.920375])
colors = 'black'
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
c = ax.scatter(theta, r,marker='.', c=colors, cmap='hsv', alpha=0.75)
ax.set_thetamin(55)
ax.set_thetamax(-45)
fmt = lambda x, pos: "{:g}".format(np.degrees(x if x >= 0 else x + 2 * np.pi))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(fmt))
plt.show()
EDIT: I have tried setting the lables with:
ax.text(0,-29.9,s='RA')
ax.text(2,-29.5,s='Dec')
Which sort of works, but I can still not get units (degrees) on my values. I think the problem might have something to do with my limits, which has to go around zero, but I am not sure.

Matplotlib share x-axis between imshow and plot

I am trying to plot two imshow and one plot above each other sharing their x-axis. The figure layout is set up using gridspec.
Here is a MWE:
import matplotlib as mpl
from matplotlib import pyplot as plt
import numpy as np
fig = plt.figure(figsize=(10,8))
gs = fig.add_gridspec(3,2,width_ratios=(1,2),height_ratios=(1,2,2), left=0.1,right=0.9,bottom=0.1,top=0.99, wspace=0.1, hspace=0.1)
ax=fig.add_subplot(gs[2,1])
ax2=fig.add_subplot(gs[2,0], sharey=ax)
ax3=fig.add_subplot(gs[1,0])
ax4=fig.add_subplot(gs[1,1], sharex=ax, sharey=ax3)
ax5=fig.add_subplot(gs[0,1], sharex=ax)
dates = pd.date_range("2020-01-01","2020-01-10 23:00", freq="H")
xs = mpl.dates.date2num(dates)
ys = np.random.random(xs.size)
N = 10
arr = np.random.random((N, N))
arr2 = np.random.random((N, N))
norm=mpl.colors.Normalize(0, arr.max()) # change the min to stretch the color spectrum
pcm = ax.imshow(arr, extent=[xs[0],xs[-1],10,0],norm=norm,aspect='auto')
cax = fig.colorbar(pcm, ax=ax, extend='max') # , location='left'
ax.set_xlabel('date')
cax.set_label('fraction [-]')
# ax.xaxis_date()
myFmt = mpl.dates.DateFormatter('%d.%m')
ax.xaxis.set_major_formatter(myFmt)
norm=mpl.colors.Normalize(0, arr2.max()) # change the min to stretch the color spectrum
pcm = ax4.imshow(arr2, extent=[xs[0],xs[-1],1,0],norm=norm,aspect='auto')
cax4 = fig.colorbar(pcm, ax=ax4, extend='max')
cax4.set_label('fraction [-]')
ax5.plot(xs,ys)
con1 = ConnectionPatch(xyA=(ax2.get_xlim()[0],1), xyB=(ax2.get_xlim()[0],1),
coordsA="data", coordsB="data", connectionstyle=mpl.patches.ConnectionStyle("Bar", fraction=-0.05),
axesA=ax2, axesB=ax3, arrowstyle="-", color='r')
con2 = ConnectionPatch(xyA=(ax2.get_xlim()[0],0), xyB=(ax2.get_xlim()[0],0),
coordsA="data", coordsB="data", connectionstyle=mpl.patches.ConnectionStyle("Bar", fraction=-0.02),
axesA=ax2, axesB=ax3, arrowstyle="-", color='r')
fig.add_artist(con1)
fig.add_artist(con2)
The plot ends up like this:
While the axes seem to be linked (date format applied to all of them), they do not have the same extent.
NOTE: The two left axes must not share the same x-axis.
EDIT: Added ConnectionPatch connections which break when using constrained_layout.
Constrained_layout was specifically designed with this case in mind. It will work with your gridspec solution above, but more idiomatically:
import datetime as dt
import matplotlib as mpl
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
fig, axs = plt.subplot_mosaic([['.', 'plot'], ['empty1', 'imtop'],
['empty2', 'imbottom']],
constrained_layout=True,
gridspec_kw={'width_ratios':(1,2),'height_ratios':(1,2,2)})
axs['imtop'].sharex(axs['imbottom'])
axs['plot'].sharex(axs['imtop'])
dates = pd.date_range("2020-01-01","2020-01-10 23:00", freq="H")
xs = mpl.dates.date2num(dates)
ys = np.random.random(xs.size)
N = 10
arr = np.random.random((N, N))
arr2 = np.random.random((N, N))
norm=mpl.colors.Normalize(0, arr.max()) # change the min to stretch the color spectrum
pcm = axs['imtop'].imshow(arr, extent=[xs[0],xs[-1],10,0],norm=norm,aspect='auto')
cax = fig.colorbar(pcm, ax=axs['imtop'], extend='max')
norm=mpl.colors.Normalize(0, arr2.max()) # change the min to stretch the color spectrum
pcm = axs['imbottom'].imshow(arr2, extent=[xs[0],xs[-1],1,0],norm=norm,aspect='auto')
cax4 = fig.colorbar(pcm, ax=axs['imbottom'], extend='max')
axs['plot'].plot(xs,ys)

FFT of resampled pandas Series

I am trying to take the Fast Fourier Transform of a resampled pandas Series:
signal = pd.Series(thick, index = pd.TimedeltaIndex(time_list_thick,unit = 's'))
resampled_signal = signal.resample('1S').mean()
However if I simply try (using scipy) and just do:
SAMPLE_RATE = 1
DURATION = len(resampled_signal)
N = SAMPLE_RATE * DURATION
yf = fft(resampled_signal[:,1])
print(yf)
xf = fftfreq(N, 1 / SAMPLE_RATE)
I obtain an error ValueError: Can only tuple-index with a MultiIndex due to the
way resampled_signal is constructed to include the index. resampled_signal looks like this for reference:
00:00:00.419175 206.080335
00:00:01.419175 206.084340
00:00:02.419175 206.087010
00:00:03.419175 206.089681
00:00:04.419175 206.095021
.
.
.
Is there anyway this can be done? I wish to include the pd.Series form since my final aim is to resample two datasets such that they have the same number of data points, take the FFT of both signals, then subtract one from the other.
My simplified code for 1 data set is given below:
import numpy as np
import pandas as pd
from datetime import datetime
from datetime import timedelta
import matplotlib
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
datathick = "20210728_rig_thick.csv"
with open(datathick) as f:
lines = f.readlines()
dates = [str(line.split(',')[0]) for line in lines]
thick = [float(line.split(',')[1]) for line in lines]
z = [float(line.split(',')[2]) for line in lines]
date_thick = [datetime.strptime(x,'%Y-%m-%dT%H:%M:%S.%f').time() for x in dates]
time_list_thick = []
for i in np.arange(0, len(date_thick)):
q = date_thick[i]
t = timedelta(hours= q.hour, minutes=q.minute,seconds=q.second, microseconds = q.microsecond).total_seconds()
time_list_thick.append(float(t))
#---RESCALE-----
signal = pd.Series(thick, index = pd.TimedeltaIndex(time_list_thick,unit = 's'))
resampled_signal = signal.resample('1S').mean()
resampled_signal = resampled_signal.interpolate(method='time')
print(resampled_signal.head())
exit()
#----FFT Transform of Output and Noise ----
# Number of samples in normalized_tone
SAMPLE_RATE = 1
DURATION = len(resampled_signal)
N = SAMPLE_RATE * DURATION
yf = fft(resampled_signal[:,1])
print(yf)
xf = fftfreq(N, 1 / SAMPLE_RATE)
#------------------------------------------------
fig=plt.figure(figsize=(7.,7.))
ax=fig.add_subplot(1,1,1)
ax.set_zorder(1)
ax.patch.set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Amplitude (a.u)')
ax.minorticks_on() # enable minor ticks
ax.xaxis.set_ticks_position('bottom')
ax.spines['left'].set_color('black')
ax.yaxis.label.set_color('black')
plt.yscale('log')
ax.tick_params(direction='out', axis='y', which='both', pad=4, colors='black')
ax.grid(b=True, which='major', color='#eeeeee', linestyle='-', zorder=1, linewidth=0.4) # turn on major grid
ax.grid(b=True, which='minor', color='#eeeeee', linestyle='-', zorder=1, linewidth=0.4) # turn on minor grid
ax.plot(np.abs(xf), np.abs(yf))
plt.savefig('fft.pdf', dpi=300, bbox_inches='tight', format='pdf')
plt.savefig('fft.png', dpi=300, bbox_inches='tight', format='png')
#----------------------------------------------

How to get a single colorbar?

In the following code for each for loop i'm getting a single colorbar. But I want to represent the following data with a single colorbar.
`import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig = plt.figure()
ax= fig.add_subplot(111)
h_1 = np.load("./Result_2D/disorder.npy")
h = h_1[0:2]
print("h: ",h)
for k in range(len(h)):
h_val = round(h[k],1)
KL=np.load("./KL_%s.npy"%h_val)
print("KL: ",KL[0:5])
E = np.load("./E_%s.npy"%h_val)
print("E_shape: ",E[0:5])
W =np.load("./W_%s.npy"%h_val)
print("W: ",W[0:5])
sc= ax.scatter(E,W,c=KL,cmap='RdBu_r')
plt.colorbar(sc)`
here is some example code of how to print multiple scatter sets with the same single colorbar
pltrange = np.logspace(1, 2, num=20) #or use np.linspace, or provide a range of values (based on the limits of your data)
lbrange = pltrange[::2] #labels for colorbar
ax.scatter(x=stream['Dist'], y=stream['Depth'], s=50,
c=stream['Sand Concentration (mg/l)'],
cmap='rainbow', edgecolor='k', linewidths=1,
vmin=pltrange[0],vmax=pltrange[-1]) #note the vmin and vmax, do this for all scatter sets
cb = fig.colorbar(ax=ax, ticks=lbrange, pad=0.01) #display colorbar, keep outside loop
cb.ax.set_yticklabels(['{:.1f}'.format(i) for i in lbrange]) #format labels if desired
I realize it's not exactly formatted for your code but..it's the exact same principle and I'm posting this from bed :) so I think you could make the necessary adaptations
try this
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig = plt.figure()
ax= fig.add_subplot(111)
h_1 = np.load("./Result_2D/disorder.npy")
h = h_1[0:2]
print("h: ",h)
for k in range(len(h)):
h_val = round(h[k],1)
KL=np.load("./KL_%s.npy"%h_val)
print("KL: ",KL[0:5])
E = np.load("./E_%s.npy"%h_val)
print("E_shape: ",E[0:5])
W =np.load("./W_%s.npy"%h_val)
print("W: ",W[0:5])
sc= ax.scatter(E,W,c=KL,cmap='RdBu_r')
plt.colorbar(sc)

Categories

Resources