eegproc.featurization module
- eegproc.featurization.choose_dwt_level(n_samples, fs, wavelet, min_freq)[source]
Choose a discrete wavelet transform (DWT) level given data length and a target minimum frequency.
The selected level is bounded by PyWavelets’ maximum permissible level for the given signal length and wavelet filter length, and ensures that the detail subband captures energy at or above
min_freq.- Parameters:
n_samples (
int) – Number of time-domain samples in the signal.fs (
float) – Sampling rate in Hz. Must be positive.wavelet (
str) – Name of a PyWavelets wavelet (e.g.,"db4","sym5").min_freq (
float) – Target minimum frequency (Hz) you still wish to capture in a detail subband. If non-positive, a tiny floor (1e-6) is used to avoid takinglog2(0)and to return a conservative level.
- Return type:
int- Returns:
int – The chosen DWT level (>= 1) that balances the maximum allowable level with the goal of preserving content at or above
min_freq.
See also
pywt.dwt_max_level: Maximum level allowed by signal and filter length. pywt.Wavelet: Wavelet objects carrying filter lengths and properties.
- eegproc.featurization.dwt_subband_ranges(fs, level)[source]
Return nominal frequency ranges (Hz) for DWT subbands up to a level.
Uses paired ranges for detail bands
D{j}and the approximation bandA{level}.- Parameters:
fs (
float) – Sampling rate in Hz.level (
int) – Decomposition level (>= 1).
- Return type:
dict[str,tuple[float,float]]- Returns:
dict[str, tuple[float, float]] – Mapping like
{"D1": (f_lo, f_hi), ..., "A{level}": (0, f_c)}.
- eegproc.featurization.feature_grouped_by_metadata(eeg_df, target_function=<function psd_bandpowers>, fs=128, bands={'alpha': (8.0, 13.0), 'betaH': (20.0, 30.0), 'betaL': (13.0, 20.0), 'delta': (0.5, 4.0), 'gamma': (30.0, 45.0), 'theta': (4.0, 8.0)}, channels=None, group_by_metadata_columns=None, drop_metadata_for_fn=True, verbose=False, **fn_kwargs)[source]
Group eeg_df by group_by_metadata_columns, run target_function on each group’s EEG slice, and prepend the group keys to every output row.
- Parameters:
eeg_df (
DataFrame) – Input with EEG columns (and possibly metadata columns).target_function (
Callable[...,DataFrame], default:<function psd_bandpowers at 0x7f56c8836480>) – Function that takes(df, fs=..., bands=..., **kwargs)and returns apandas.DataFrameof features. Example: psd_bandpowers.fs (
int, default:128) – Sampling frequency to pass to target_function.bands (
dict[str,tuple[float,float]], default:{'delta': (0.5, 4.0), 'theta': (4.0, 8.0), 'alpha': (8.0, 13.0), 'betaL': (13.0, 20.0), 'betaH': (20.0, 30.0), 'gamma': (30.0, 45.0)}) – Bands mapping to pass to target_function (if it uses it).channels (
list[str] |None, default:None) – If provided, restrict input columns to these (metadata cols are always kept for grouping).group_by_metadata_columns (
list[str] |None, default:None) – Columns to group by (e.g., [“patient_id”, “video_id”]). If None, process the whole df once.drop_metadata_for_fn (
bool, default:True) – If True, drop the group-by columns before calling target_function. Set False if your function can safely ignore extra columns.verbose (
bool, default:False) – Print a single-line progress display while processing groups.**fn_kwargs – Extra keyword args forwarded to target_function.
- Return type:
DataFrame- Returns:
pd.DataFrame – Concatenation of per-group outputs, with metadata keys as leading columns.
Examples
Band power for each (subject, trial) group of a band-filtered table:
>>> import numpy as np, pandas as pd >>> from eegproc import (FREQUENCY_BANDS, bandpass_filter, ... feature_grouped_by_metadata, psd_bandpowers) >>> fs = 128 >>> raw = pd.DataFrame({"AF3": np.random.randn(8 * fs), "F7": np.random.randn(8 * fs)}) >>> clean = bandpass_filter(raw, fs, bands=FREQUENCY_BANDS).assign(subject="P01", trial=1) >>> feats = feature_grouped_by_metadata( ... clean, target_function=psd_bandpowers, fs=fs, ... group_by_metadata_columns=["subject", "trial"], ... ) >>> feats.columns[:3].tolist() ['subject', 'trial', 'AF3_delta']
- eegproc.featurization.generate_all_features(raw_eeg_df, fs, bands={'alpha': (8.0, 13.0), 'betaH': (20.0, 30.0), 'betaL': (13.0, 20.0), 'delta': (0.5, 4.0), 'gamma': (30.0, 45.0), 'theta': (4.0, 8.0)}, channels=None, group_by_metadata_columns=None, verbose=False)[source]
Compute the full feature set for an EEG table, optionally grouped by metadata.
Runs the whole featurization pipeline per group and concatenates the result: band-pass with notch, then PSD bandpowers, Shannon entropy, Hjorth parameters, wavelet band energy and entropy, and IMF band energy and entropy.
- Parameters:
raw_eeg_df (
DataFrame) – Raw EEG, one column per electrode, plus any metadata columns.fs (
int) – Sampling rate in Hz.bands (
dict[str,tuple[float,float]] |None, default:{'delta': (0.5, 4.0), 'theta': (4.0, 8.0), 'alpha': (8.0, 13.0), 'betaL': (13.0, 20.0), 'betaH': (20.0, 30.0), 'gamma': (30.0, 45.0)}) – Band vocabulary; defaults toFREQUENCY_BANDS.channels (
list[str] |None, default:None) – Restrict processing to these electrode columns. Grouping columns are always retained.group_by_metadata_columns (
list[str] |None, default:None) – Columns identifying a group, e.g.["subject", "trial"]. When omitted, the whole table is one group.verbose (
bool, default:False) – Print a single-line progress display while processing groups.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per (group, window): the metadata columns followed by every feature column.
Examples
>>> feats = generate_all_features( ... raw_df, fs=128, group_by_metadata_columns=["subject", "trial"], ... )
- eegproc.featurization.hjorth_params(df, fs, window_sec=4.0, overlap=0.5, detrend='constant', eps=1e-300)[source]
Compute Hjorth parameters (activity, mobility, complexity) per channel over windows.
Expects columns named
{channel}_{band}where eachbandis a key inbands(bandpass_filter) may be used to achieve the expected table. For each numeric column, the function computes: - Activity: variance of the signal - Mobility: sqrt(var(Δx) / var(x)) - Complexity: mobility(Δx) / mobility(x)- Parameters:
df (
DataFrame) – Bandpass Filtered EEG dataframe. Numeric columns must be named like{channel}_{band}(e.g.,AF3_alpha).fs (
float) – Sampling rate in Hz.window_sec (
float, default:4.0) – Window length in seconds.overlap (
float, default:0.5) – Fractional overlap in[0, 1)between windows.detrend (
str|None, default:'constant') – Detrending applied per column prior to differencing.eps (
float, default:1e-300) – Numerical guard to prevent division by zero.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window, with columns:
{channel}_activity,{channel}_mobility,{channel}_complexity.- Raises:
ValueError – If window is too small (needs >= 3 samples) or overlap is invalid.
- eegproc.featurization.imf_band_energy(df, fs, imf_to_band=['gamma', 'betaH', 'betaL', 'alpha', 'theta', 'delta'], window_sec=4.0, overlap=0.5, EMD_kwargs={})[source]
Compute Intrinsic Mode Function (IMF) band-energy distributions per channel using Empirical Mode Decomposition (EMD).
Expects columns named
{channel}where each row is a reading of raw EEG data. For each column, applies EMD decomposition to find mean m(t) of envelopes (cubic splines fit to extremas) and takes difference between raw data point and m(t) [d(t) = x(t) - m(t)]. For each window and channel, the IMF band-energy is based on the cumulative sums of the IMFs returned by the EMD function.- Parameters:
df (
DataFrame) – Raw data EEG dataframe. Numeric columns.fs (
float) – Sampling rate in Hz (used only for window sizing).imf_to_band (list[str], default=["gamma", "betaH", "betaL", "alpha", "theta", "delta"]) – Labels to assign to IMFs 1..K (K = number of labels).
window_sec (
float, default:4.0) – Window length in seconds.overlap (
float, default:0.5) – Fractional overlap in[0, 1).EMD_kwargs (
dict, default:{}) – Extra keyword arguments passed toPyEMD.EMD(e.g.,max_imfis internally set).
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window; columns:
{channel}_{band}_imfenergyfor each band label.- Raises:
ValueError – If window too small, overlap invalid, or window longer than available samples.
Notes
IMF indices are assigned in order to the names in
imf_to_band; if fewer IMFs are present than labels, missing energies are filled with 0.0.The EMD is computed once per full signal and reused for all windows via cumulative sums.
- eegproc.featurization.imf_entropy(imf_energy_df, bands=['gamma', 'betaH', 'betaL', 'alpha', 'theta', 'delta'], normalize=True, eps=1e-300)[source]
Compute (normalized) Shannon entropy of IMF energy distributions per channel.
For each window and channel, uses the vector of
_imfenergyvalues acrossbandsas a distribution and computes-Σplogp. Optionally normalizes bylog(K)whereK = len(bands).- Parameters:
imf_energy_df (
DataFrame) – Output fromimf_band_energy()with columns{ch}_{band}_imfenergy.bands (list[str], default=["gamma", "betaH", "betaL", "alpha", "theta", "delta"]) – Band labels (order defines component order).
normalize (
bool, default:True) – If True, divide entropy bylog(K)to obtain values in[0, 1].eps (
float, default:1e-300) – Numerical guard for zero energies.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window; columns
{ch}_imfentropy.
- eegproc.featurization.psd_bandpowers(df, fs, bands={'alpha': (8.0, 13.0), 'betaH': (20.0, 30.0), 'betaL': (13.0, 20.0), 'delta': (0.5, 4.0), 'gamma': (30.0, 45.0), 'theta': (4.0, 8.0)}, window_sec=4.0, overlap=0.5, detrend='constant')[source]
Compute Welch PSD band powers per channel-band column over selected window size.
Expects columns named
{channel}_{band}where eachbandis a key inbands(bandpass_filter) may be used to achieve the expected table. Each row of the expected df is a wave amplitude reading of the channel-band combination. For each window, integrates the Welch PSD within each band using the trapezoid rule.- Parameters:
df (
DataFrame) – Bandpass Filtered EEG dataframe. Numeric columns must be named like{channel}_{band}(e.g.,AF3_alpha).fs (
float) – Sampling rate in Hz.bands (
dict[str,tuple[float,float]], default:{'delta': (0.5, 4.0), 'theta': (4.0, 8.0), 'alpha': (8.0, 13.0), 'betaL': (13.0, 20.0), 'betaH': (20.0, 30.0), 'gamma': (30.0, 45.0)}) – Mapping of band name to inclusive frequency bounds (Hz)(lo, hi).window_sec (
float, default:4.0) – Window length in seconds used for Welch’s method (nperseg = round(fs*window_sec)).overlap (
float, default:0.5) – Fractional overlap in[0, 1)between consecutive windows.detrend (
str|None, default:'constant') – Detrending applied before PSD computation viaapply_detrend.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window, columns match the input band columns (e.g.,
AF3_alpha) and contain power spectral density-integrated band powers.- Raises:
ValueError – If no valid
{channel}_{band}columns are found, if the window is too small, or ifoverlapis outside[0, 1).
Notes
Uses
scipy.signal.welchwith a Hann window and no overlap inside the Welch call (windowing/overlap are controlled at the outer sliding-window level).
Examples
Minimal example with synthetic data (two channels, one band):
>>> import numpy as np, pandas as pd >>> fs = 128.0 >>> t = np.arange(int(8*fs)) / fs # 8 seconds >>> # Two synthetic signals with an ~10 Hz component (alpha band) >>> af3_alpha = 0.8*np.sin(2*np.pi*10*t) + 0.1*np.random.randn(t.size) >>> f7_alpha = 0.6*np.sin(2*np.pi*10*t + 0.7) + 0.1*np.random.randn(t.size) >>> df = pd.DataFrame({ ... "AF3_alpha": af3_alpha, ... "F7_alpha": f7_alpha, ... }) >>> bands = {"alpha": (8.0, 12.0)} >>> out = psd_bandpowers(df, fs=fs, bands=bands, window_sec=2.0, overlap=0.5) >>> out.head() AF3_alpha F7_alpha 0 ... ... 1 ... ... 2 ... ...
- eegproc.featurization.shannons_entropy(psd_df, bands={'alpha': (8.0, 13.0), 'betaH': (20.0, 30.0), 'betaL': (13.0, 20.0), 'delta': (0.5, 4.0), 'gamma': (30.0, 45.0), 'theta': (4.0, 8.0)}, eps=1e-300, assume_log=False)[source]
Measure how evenly a channel’s energy is spread across its frequency bands.
Takes a PSD table (the output of
psd_bandpowers()), not a raw signal. Each row is one window; for every channel the band energies in that row are normalized into a probability distribution and reduced to a single normalized Shannon entropy in[0, 1]. A value near 1 means energy is spread evenly across bands; near 0 means it is concentrated in one band.This is entropy across bands, one value per channel — not entropy within each band. A channel with fewer than two band columns has no distribution to measure and yields
NaN.- Parameters:
psd_df (
DataFrame) – PSD table with columns named{channel}_{band}, wherebandis a key ofbands(e.g.AF3_alpha). Columns that do not match this pattern (metadata, indices) are ignored.bands (
dict[str,tuple[float,float]], default:{'delta': (0.5, 4.0), 'theta': (4.0, 8.0), 'alpha': (8.0, 13.0), 'betaL': (13.0, 20.0), 'betaH': (20.0, 30.0), 'gamma': (30.0, 45.0)}) – Band vocabulary. Only the keys are used here, to decide which columns are band columns and in what order they are read.eps (
float, default:1e-300) – Numerical guard againstlog(0)and division by zero.assume_log (
bool|None, default:False) – Whetherpsd_dfholds log-power.Falsetreats values as linear power,Trueexponentiates first, andNoneinfers it from the fraction of negative values.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per input row, indexed like
psd_df. One column per channel, named{channel}_entropy.
Examples
>>> import pandas as pd >>> from eegproc import psd_bandpowers, shannons_entropy, bandpass_filter >>> clean = bandpass_filter(raw_df, fs=128) >>> psd = psd_bandpowers(clean, fs=128) >>> shannons_entropy(psd).columns.tolist() ['AF3_entropy', 'F7_entropy']
Energy spread evenly across bands is the maximum-entropy case:
>>> psd = pd.DataFrame({"AF3_delta": [1.0], "AF3_theta": [1.0], ... "AF3_alpha": [1.0], "AF3_betaL": [1.0], ... "AF3_betaH": [1.0], "AF3_gamma": [1.0]}) >>> float(shannons_entropy(psd)["AF3_entropy"].iloc[0]) 1.0
Notes
Entropy is normalized by
log2(n_bands), so results are comparable across channels only when they share the same band vocabulary.Changed in v2: this function previously accepted a raw signal with
fs/window_sec/overlapand emitted{channel}_{band}_entropy. It now consumes a PSD table and emits{channel}_entropy.
- eegproc.featurization.wavelet_band_energy(df, fs, bands, wavelet='db4', mode='periodization', window_sec=4.0, overlap=0.5)[source]
Compute band energies by distributing DWT subband energies into target bands.
Expects columns named
{channel}where each row is a reading of raw EEG data. For each window and channel, performs a multilevel DWT, computes energy in each detail/approximation subband, then proportionally assigns subband energy to user bands by frequency overlap.- Parameters:
df (
DataFrame) – Raw data EEG dataframe. Numeric columns.fs (
float) – Sampling rate in Hz.bands (
dict[str,tuple[float,float]]) – Target bands as{name: (lo, hi)}in Hz.wavelet (
str, default:'db4') – PyWavelets wavelet name forpywt.wavedec.mode (
str, default:'periodization') – Signal extension mode for DWT.window_sec (
float, default:4.0) – Window length in seconds.overlap (
float, default:0.5) – Fractional overlap in[0, 1).
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window; columns:
{channel}_{band}_wenergyfor eachchannelindfand each band inbands.- Raises:
ValueError – If window too small,
overlapinvalid, or window longer than available samples.
Notes
The DWT level is chosen automatically via
choose_dwt_level()to respect both data length and minimum target band frequency.Energy is computed as the sum of squared coefficients per subband.
Subband energy is apportioned to target bands by fractional frequency overlap.
- eegproc.featurization.wavelet_entropy(wv_band_energy_df, bands, normalize=True, eps=1e-300)[source]
Compute (optionally normalized) Shannon entropy of wavelet band-energy distributions per channel.
Expects columns
{channel}_{band}_wenergywhere each row is a windowed wavelet band energy. For each window and channel, treats the vector of_wenergyvalues acrossbandsas a distribution, then computes-Σplogp. Optionally normalizes bylog(K)whereK = len(bands).- Parameters:
wv_band_energy_df (
DataFrame) – Output ofwavelet_band_energy(); columns like{ch}_{band}_wenergy.bands (
dict[str,tuple[float,float]]) – Same band dictionary used for energy computation (order defines component order).normalize (
bool, default:True) – If True, divide entropy bylog(K)to obtain values in[0, 1].eps (
float, default:1e-300) – Numerical guard for zero energies.
- Return type:
DataFrame- Returns:
pandas.DataFrame – One row per window, columns
{ch}_wentropy.- Raises:
ValueError – If no matching
{channel}_{band}_wenergycolumns are found.