pupil_signal_quality#
- pypillometry.signal.pupil_signal_quality(signal, fs, mask=None, lowpass_cutoff=4.0, window_size_ms=500, metric='snr')[source]#
Compute local signal quality metric for pupil signal.
Estimates signal quality by separating the signal into a slow “true signal” component (via lowpass filtering) and a fast “noise” component (the residual). Quality is then computed in sliding windows.
- Parameters:
signal (np.ndarray or np.ma.MaskedArray) –
Pupil signal. Can be either:
A regular numpy array, in which case mask must be provided
A numpy masked array, in which case its mask is used
fs (float) – Sampling rate in Hz
mask (np.ndarray, optional) – Boolean mask where True indicates invalid/masked samples. Required if signal is a regular numpy array. Ignored if signal is a masked array.
lowpass_cutoff (float, optional) – Cutoff frequency for separating signal from noise (default: 4.0 Hz). Pupil responses are typically < 4 Hz, so higher frequencies are noise.
window_size_ms (float, optional) – Window size for local quality estimation in milliseconds (default: 500 ms)
metric (str, optional) –
Quality metric to return:
”snr”: signal-to-noise ratio (signal_power / noise_power)
”snr_db”: SNR in decibels, 10 * log10(snr) (0 dB = equal, +10 dB = 10x better, -10 dB = 10x worse)
”noise_power”: raw noise power (signal units squared)
”noise_cv”: coefficient of variation (noise_std / signal), interpretable as fractional noise (0.05 = 5% noise)
- Returns:
quality (np.ma.MaskedArray) – Local quality metric as a masked array (same length as input). Masked at the same locations as the input.
- Raises:
ValueError – If metric is not one of the supported options. If signal is a regular array and mask is not provided. If there are NaN values in the unmasked (valid) portion of the signal.
Notes
The signal-noise separation assumes pupil dynamics are slow (< lowpass_cutoff Hz). The “signal” estimate is the lowpass-filtered version, and “noise” is the high-frequency residual (original - lowpass).
Local power is computed as the mean of squared values in a sliding window. SNR = signal_power / noise_power indicates how much true signal there is relative to measurement noise.
Examples
>>> rng = np.random.default_rng(0) >>> pupil = 4000 + 100 * np.sin(np.arange(5000) / 500) + rng.normal(0, 10, 5000) >>> pupil[1000:1100] = 0 # a blink >>> # With explicit mask >>> mask = pupil == 0 # e.g., blinks marked as 0 >>> snr = pupil_signal_quality(pupil, fs=500, mask=mask, metric="snr")
>>> # With masked array >>> pupil_ma = np.ma.array(pupil, mask=(pupil == 0)) >>> snr = pupil_signal_quality(pupil_ma, fs=500, metric="snr")
>>> # Get noise as coefficient of variation (fraction) >>> noise_cv = pupil_signal_quality(pupil, fs=500, mask=mask, metric="noise_cv") >>> bool(np.mean(noise_cv[~mask]) < 0.01) # below 1% noise True