TF STFT
This demo demonstrates time-frequency analysis using the Short-Time Fourier Transform (STFT), a classic approach to spectrograms.
What is STFT?
STFT applies the Fast Fourier Transform (FFT) to windowed segments of the signal:
Window the signal: Apply time-limited window (e.g., Hann, Tukey)
Compute FFT: Calculate frequency spectrum for that segment
Slide window: Move forward in time and repeat
Build spectrogram: Stack spectra to create time-frequency representation
Key Parameters
Window length (window_length):
Controls time-frequency resolution:
tf_stft(epochs, window_length = 0.5) # 500 ms windowsLonger windows (e.g., 500 ms): Better frequency resolution, worse time resolution
Shorter windows (e.g., 200 ms): Better time resolution, worse frequency resolution
Cycles (alternative):
Instead of specifying window length, specify cycles at each frequency:
tf_stft(epochs, cycles = 7)Creates frequency-adaptive windows similar to Morlet wavelets.
Time steps:
Controls temporal resolution of output:
tf_stft(epochs, time_steps = 0.005) # 5 ms stepsSmaller = smoother time course, larger = faster computation.
Frequency Spacing
Linear (frequencies = 2:1:80):
Equal Hz spacing
Natural for narrow bands
Logarithmic (frequencies = logrange(2, 80, length = 100)):
Proportional spacing
Better for wide ranges
Use
ylogscale = truein plots
Baseline Correction
plot_tf(
tf_data,
baseline_interval = (-0.5, -0.2),
baseline_method = :db,
colorrange = (-3, 3)
)Shows relative power changes from pre-stimulus baseline.
Edge Effects
Filter edges (filter_edges = true):
Removes edge samples where windowing causes artifacts
Recommended for cleaner results
Workflow Summary
This demo shows:
Synthetic validation: Known frequencies (5, 25, 35 Hz)
Fixed window length: Constant resolution analysis
Cycles mode: Adaptive resolution
Linear vs log spacing: Different frequency sampling
Baseline correction: Event-related power changes
Edge filtering: Removing edge artifacts
Further Reading
Cohen, M. X. (2014). Analyzing Neural Time Series Data: Theory and Practice. Chapter 15: Short-Time FFT
Code Examples
Show Code
# Demo: Time-Frequency Analysis - STFT
# Shows Short-Time Fourier Transform for time-frequency analysis.
using EegFun
# Note: EegFun.example_path() resolves bundled example data paths.
# When using your own data, simply pass the file path directly, e.g.:
# dat = EegFun.read_raw_data("/path/to/your/data.bdf")
#######################################################################
@info EegFun.section("TEST 1: Synthetic Signal with Known Frequencies")
#######################################################################
# Generate synthetic signal
sample_rate = 1000.0
times, signal = EegFun.generate_signal(
400, # n_trials
[-1.0, 3.0], # time_interval
sample_rate, # sample_rate
[5.0, 25, 35.0], # frequencies
[5.0, 5.0, 5.0], # amplitudes
[[0.1, 0.5], [0.6, 1.0], [1.1, 1.5]], # time intervals for each freq
0.0, # noise amplitude
);
epochs_synthetic = EegFun.signal_to_data(times, signal, :Channel1, sample_rate)
EegFun.plot_epochs(epochs_synthetic, channel_selection = EegFun.channels([:Channel1]))
spectrum = EegFun.freq_spectrum(epochs_synthetic, max_freq = 80.0)
EegFun.plot_frequency_spectrum(spectrum, channel_selection = EegFun.channels([:Channel1]))
# tf_stft_fixed
tf_data = EegFun.tf_stft(epochs_synthetic, frequencies = 1:1:40, window_length = 0.5)
EegFun.plot_tf(tf_data, ylogscale = false)
tf_data = EegFun.tf_stft(epochs_synthetic, frequencies = 1:1:40, window_length = 0.5)
EegFun.plot_tf(tf_data, ylogscale = false)
tf_data = EegFun.tf_stft(epochs_synthetic, frequencies = logrange(1, 40, length = 30), window_length = 0.5)
EegFun.plot_tf(tf_data, ylogscale = true)
tf_data = EegFun.tf_stft(epochs_synthetic, frequencies = logrange(1, 40, length = 30), window_length = 0.5)
EegFun.plot_tf(tf_data, ylogscale = true)
#######################################################################
@info EegFun.section("TEST 2: Cohen Data Chapter 13")
#######################################################################
# This is some data that was presented in Cohen: Analyzin Neural Time Series Data
data_cohen = EegFun.read_data(EegFun.example_path("data/julia/tf/tf_test_epochs.jld2"));
# Figure 13.11 A)
tf_data =
EegFun.tf_stft(data_cohen, frequencies = logrange(2, 80, length = 100), window_length = 0.5, time_steps = 0.001, filter_edges = true)
EegFun.plot_tf(tf_data; baseline_interval = (-0.5, -0.2), baseline_method = :db, colorrange = (-3, 3), ylogscale = true, colormap = :jet)
# Figure 13.11 A)
tf_data = EegFun.tf_stft(data_cohen, frequencies = 2:1:80, cycles = 7, time_steps = 0.005, filter_edges = false)
EegFun.plot_tf(tf_data; baseline_interval = (-0.5, -0.2), baseline_method = :db, colorrange = (-3, 3), ylogscale = false, colormap = :jet)
# Figure 13.14 A)
tf_data =
EegFun.tf_stft(data_cohen, frequencies = logrange(2, 80, length = 30), window_length = 0.5, time_steps = 0.005, filter_edges = true)
EegFun.plot_tf(tf_data; baseline_interval = (-0.5, -0.2), baseline_method = :db, colorrange = (-3, 3), ylogscale = true, colormap = :jet)
# Figure 13.14 B)
tf_data =
EegFun.tf_stft(data_cohen, frequencies = logrange(2, 80, length = 30), window_length = 0.5, time_steps = 0.005, filter_edges = true)
EegFun.plot_tf(tf_data; baseline_interval = (-0.5, -0.2), baseline_method = :db, colorrange = (-3, 3), ylogscale = true, colormap = :jet)
# Figure 13.14 C)
tf_data = EegFun.tf_stft(data_cohen, frequencies = logrange(2, 80, length = 30), cycles = 5, time_steps = 0.005, filter_edges = true)
EegFun.plot_tf(tf_data; baseline_interval = (-0.5, -0.2), baseline_method = :db, colorrange = (-3, 3), ylogscale = true, colormap = :jet)