FFT of resampled pandas Series - python

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')
#----------------------------------------------

Related

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)

Extrapolation in python (temperature curve in bomb calorimeter)

The 2 red lines in the graph are the 2 extrapolated lines. The upper line works well but the lower line seems to take into account the data outside the range of time2 and temp2 which makes the line look awkward after the point t=420s. I would like to know how to fix this.
*On a less important note: how can i remove the extra ticks on x-axis on the left of the origin? Thanks a lot.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,
AutoMinorLocator)
from sklearn.linear_model import LinearRegression
from scipy.interpolate import InterpolatedUnivariateSpline
%matplotlib inline
file = pd.read_excel("T8.xlsx","Phthalansäureanhydrid",usecols=[2,3])
X = file['Zeit(s)']
Y = file['Temperatur(Celcius Grad)']
fig, ax = plt.subplots()
ax.plot(X,Y,'-',color='#10A5F3', label="Phthalansäureanhydrid")
ax.grid(True, which='major', axis='both', color='#F19211', linestyle='-')
#ax.grid(True, which='minor', axis='both', color='#F19211', linestyle='--')
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
#ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
#ax.legend(loc='upper center', frameon=True)
#major & minor ticks
ax.xaxis.set_major_locator(MultipleLocator(100))
ax.xaxis.set_major_formatter(FormatStrFormatter('%d'))
ax.xaxis.set_minor_locator(MultipleLocator(10))
#extrapolation - first line
temp1 = []
time1 = []
xnew1 = []
for i in file.index:
if i > 630:
temp1.append(file['Temperatur(Celcius Grad)'][i])
time1.append(file['Zeit(s)'][i])
else:
xnew1.append(file['Zeit(s)'][i])
order = 1
extrapo1 = InterpolatedUnivariateSpline(time1, temp1, k=1)
ynew1 = extrapo1(xnew1)
#extrapolation - second line
temp2 = []
time2 = []
xnew2 = []
for i in file.index:
if 100<i<400:
temp2.append(file['Temperatur(Celcius Grad)'][i])
time2.append(file['Zeit(s)'][i])
if i>200:
xnew2.append(file['Zeit(s)'][i])
ynew2 = []
f = interpolate.interp1d(time2, temp2, fill_value='extrapolate')
for i in xnew2:
ynew2.append(f(i))
plt.xlabel(r'Zeit[s]')
plt.ylabel(r'Temperatur[c]')
plt.plot(xnew1,ynew1,'-', color = '#B94A4D')
plt.plot(xnew2,ynew2,'-', color = '#B94A4D')
plt.savefig('kmn.pdf')
Link to the data: https://docs.google.com/spreadsheets/d/1xznXj-aA-Szq2s4KWb-qPWYxZbQNrA5FgUCQT6i7oVo/edit?usp=sharing

Remove the microseconds from matplotlib spectrogram

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]

Custom scale for radial contour plot in matplotlib

I have a sample script to generate a polar contour plot in matplotlib:
import os
import math
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist.grid_finder import FixedLocator, MaxNLocator, DictFormatter
import random
# ------------------------------------ #
def setup_arc_radial_axes(fig, rect, angle_ticks, radius_ticks, min_rad, max_rad):
tr = PolarAxes.PolarTransform()
pi = np.pi
grid_locator1 = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))
grid_locator2 = FixedLocator([a for a, b in radius_ticks])
tick_formatter2 = DictFormatter(dict(radius_ticks))
grid_helper = floating_axes.GridHelperCurveLinear(tr,
extremes=((370.0*(pi/180.0)), (170.0*(pi/180.0)), max_rad, min_rad),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=tick_formatter2,
)
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
ax1.grid(True)
# create a parasite axes whose transData in RA, cz
aux_ax = ax1.get_aux_axes(tr)
aux_ax.patch = ax1.patch
ax1.patch.zorder=0.9
#ax1.axis["left"].set_ticklabel_direction("+")
return ax1, aux_ax
# ------------------------------------ #
# write angle values to the plotting array
angles = []
for mic_num in range(38):
angle = float(mic_num)*(180.0/36.0)*(math.pi/180.0)+math.pi
angles.append(angle)
# ------------------------------------ #
### these are merely the ticks that appear on the plot axis
### these don't actually get plotted
angle_ticks = range(0,190,10)
angle_ticks_rads = [a*math.pi/180.0 for a in angle_ticks]
angle_ticks_rads_plus_offset = [a+math.pi for a in angle_ticks_rads]
angle_ticks_for_plot = []
for i in range(len(angle_ticks)):
angle_ticks_for_plot.append((angle_ticks_rads_plus_offset[i],r"$"+str(angle_ticks[i])+"$"))
# ------------------------------------ #
scale = 1.0
aspect = 1.50
height = 8.0
fig = plt.figure(1, figsize=(height*aspect*scale, height*scale))
fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95, top=0.84)
fig.subplots_adjust()
plot_real_min = 30.0
plot_real_max = 100.0
plot_fake_min = 0.0
plot_fake_max = 5000.0
rad_tick_increment = 500.0
radius_ticks = []
for i in range(int(plot_fake_min),int(plot_fake_max)+int(rad_tick_increment),int(rad_tick_increment)):
plot_fake_val = ((i-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min
radius_ticks.append((plot_fake_val, r"$"+str(i)+"$"))
ax2, aux_ax2 = setup_arc_radial_axes(fig, 111, angle_ticks_for_plot, radius_ticks, plot_real_min, plot_real_max)
azimuths = np.radians(np.linspace(0, 180, 91))
azimuths_adjusted = [ (x + math.pi) for x in azimuths ]
zeniths = np.arange(0, 5050, 50)
zeniths_adjusted = [((x-plot_fake_min)/(plot_fake_max-plot_fake_min))*(plot_real_max-plot_real_min)+plot_real_min for x in zeniths]
r, theta = np.meshgrid(zeniths_adjusted, azimuths_adjusted)
values = 90.0+5.0*np.random.random((len(azimuths), len(zeniths)))
aux_ax2.contourf(theta, r, values)
cbar = plt.colorbar(aux_ax2.contourf(theta, r, values), orientation='vertical')
cbar.ax.set_ylabel('Contour Value [Unit]', fontsize = 16)
plt.suptitle('Plot Title ', fontsize = 24, weight="bold")
plt.legend(loc=3,prop={'size':20})
plt.xlabel('Angle [deg]', fontsize=20, weight="bold")
plt.ylabel('Frequency [Hz]', fontsize=20, weight="bold")
# plt.show()
plt.savefig('test.png', dpi=100)
plt.close()
This script will generate a plot that looks something like:
My question is how can I plot with an alternate color bar scale? Is it possible to define a custom scale?
Something like a blue-white-red scale where deltas around a central value can easily be shown would be the best, something like:
You can create a custom scale, but matplotlib already has what you want. All you have to do is add an argument to contourf:
aux_ax2.contourf(theta, r, values, cmap = 'bwr')
If you don't like bwr, coolwarm and seismic are also blue to red. If you need to reverse the scale, just add _r to the colormap name. You can find more colormaps here: http://matplotlib.org/examples/color/colormaps_reference.html
I can't run your code, but I think you could solve your problem this way:
from matplotlib import pyplot as plt
import matplotlib as mpl
f = plt.figure(figsize=(5,10))
ax = f.add_axes([0.01, 0.01, 0.4, 0.95])
#here we create custom colors
cmap = mpl.colors.LinearSegmentedColormap.from_list(name='Some Data',colors=['b', 'w','w', 'r'])
cb = mpl.colorbar.ColorbarBase(ax, cmap=cmap, orientation='vertical')
cb.set_label('Some Data')
plt.show()
And if linear way is not what you are looking for here is some other types:
http://matplotlib.org/api/colors_api.html#module-matplotlib.colors

Graph Plot axes scaling / design / time format

My current Pandas / python plot looks like this:
What I like to have:
I want to get rid of the 1e7 and 1e9 on both y-axes. The values of the two time series are in the millions and billions, so a delimiter for the number would be a plus for readability.
I like to have a (light) grid in the background and at least normal lines on the axes.
I like to have a monthly scaling, not every 6 months on the x-axis
How can I add the legend below?
The current code is (transactions 1 and 2 are time series of trading volumes):
ax = data.transactions1.plot(figsize=(12, 3.5))
data.transactions2.plot(secondary_y=True)
The following code :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
import datetime
from matplotlib.ticker import ScalarFormatter
base = datetime.datetime.today()
numdays = 365
date_list = [base - datetime.timedelta(days=x) for x in range(0, numdays)]
x = np.arange(0, numdays, 1)
values1 = 0.05 * x**2*1e9
values2 = -1*values1*1e7
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
lns1 = ax1.plot(date_list, values1, 'g-', label='Foo')
lns2 = ax2.plot(date_list, values2, 'b-', label='Bar')
# We set the date format
dareFmt = mdates.DateFormatter('%b %Y')
# We then apply the format
ax1.xaxis.set_major_formatter(dareFmt)
ax1.set_xlabel('Dates')
#used to give the inclination
fig.autofmt_xdate()
# Dsiplay the grid
ax1.grid(True)
# To get rid of the 1eX on top i divide the values of the y axis by the exponent value
y_values = ax1.get_yticks().tolist()
y_values = [x / 1e12 for x in y_values]
ax1.set_yticklabels(y_values)
ax1.set_ylabel('10e12')
y_values = ax2.get_yticks().tolist()
y_values = [x / 1e19 for x in y_values]
ax2.set_yticklabels(y_values)
ax2.set_ylabel('10e19')
lns = lns1 + lns2
labs = [l.get_label() for l in lns]
ax1.legend(lns, labs,bbox_to_anchor=(0., -0.25, 1., .102), loc=3,
ncol=2, mode="expand", borderaxespad=0.)
plt.show()
gives you :

Categories

Resources