Post 8: FFTs??
No clue exactly what I'll be working on since it has been so long...
- cumulative CGM?
- FFT. This one will be weird and I'll need to make a function that fills in any time gaps (probably using just linear interpolation
- interactive holoviews plots (note that plots made with holoviews will not render in github pages)
- (eventually??) make a dashboard using panel? And deploy in the browser https://towardsdatascience.com/how-to-deploy-a-panel-visualization-dashboard-to-github-pages-2f520fd8660
- Overlapping KDE plots (seaborn) https://seaborn.pydata.org/examples/kde_ridgeplot
- Look at carb ratios. Split days into segments (breakfast, lunch, dinner, sleep) and link each part with a carb ratio. Link carb ratios with stats within each segment, like high point, low point.
- principal component analysis
- clustering
- kalman filtering - predictive algorithm
- Just a side thought - I wish there was a better way to collect data on pre-bolus time. An easier way to log it perhaps?
# since this notebook lives in a sub-folder of the main project, I'll add the main folder to the python path
import sys
sys.path.append("../")
import tools.glooko as gl
# plotting etc
import pandas as pd
import holoviews as hv
from holoviews import opts
from holoviews.plotting.links import RangeToolLink
hv.extension('bokeh')
import matplotlib.pyplot as plt
%matplotlib widget
import numpy as np
# Import data
data_folder = r"../data"
df_cgm, df_bolus, df_basal, df_insulin, df_cgm_daily = gl.merge_data(data_folder)
# Sort by time (shouldn't this happen in the merge_data function?? MW check this
df_cgm.sort_values(by="time", inplace=True)
# Convert the time array to seconds
dt = np.insert(np.cumsum(np.diff(df_cgm["time"]) / 1e9).astype(float), 0, 0)
bg = df_cgm["bg"]
# Now we need to interpolate bc there's a bunch of missing data.
dt_full = np.arange(0,dt[-1],300)
# interpolation
bg_full = np.interp(dt_full, dt, bg)
Taking the Fast Fourier Transform (FFT)¶
bg_norm = bg_full - np.mean(bg_full)
time_step = 300 # Time step is 300 seconds (also 1/fs)
time_step_days = time_step/86400
n = len(dt_full)
# Now take the discrete Fourier Transform (using the Fast Fourier Transform algorithm, aka the FFT)
sig1_fft = np.fft.fft(bg_norm)
fvec1 = np.fft.fftfreq(n,time_step_days)
plt.close()
plt.plot(fvec1, np.abs(sig1_fft))
plt.xlim([0,30])
plt.ylim([0,200000])
plt.show()
plt.xlabel("Frequency (cycles per day)");
OK, Well. I think what we have here is a good old-fashioned case of when you've got a hammer, everything looks like a nail. There's not much to see here as far as I can tell. There are a few things I could dig into more - The spikes below 10 Hz (oops not really Hz? cycles per DAY, not per second...) are so regularly spaced I suspect they may just be artifacts of some sort. If I was really committed to the FFT as an informative tool here, I might try some additional things - like splitting up and looking at a spectrogram? I might also try windowing in the time domain before taking the FFT. But ultimately - what can a frequency analysis actually tell me? Blood glucose definitely has periodicity, but I can't think of how it might be informative. I'm going to let this one go. Bye for now, Fast Fourier Transform!