Skip to content

Commit b308987

Browse files
committed
add snr and kurtosis metrics functions for data curation
some photometry signal measurements are inevitably noisy. those two metrics--snr and kurtosis-- would be useful when curating sessions with "good photometry recordings" We should feed preprocessed dF/F as the "trace" argument.
1 parent ed3e9fe commit b308987

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import numpy as np
2+
import warnings
3+
from scipy.signal import find_peaks
4+
from scipy.stats import kurtosis
5+
6+
7+
def estimate_snr(trace, fps):
8+
"""
9+
Estimate the signal-to-noise ratio (SNR) of a trace.
10+
11+
Parameters
12+
----------
13+
trace : np.ndarray
14+
The input trace.
15+
fps : float
16+
Frames per second of the trace.
17+
18+
Returns
19+
-------
20+
snr : float
21+
Estimated signal-to-noise ratio.
22+
noise : float
23+
Estimated noise level.
24+
peaks : np.ndarray
25+
Indices of detected peaks in the trace.
26+
"""
27+
# Replace NaNs with the median of the trace
28+
trace = np.nan_to_num(trace, nan=np.nanmedian(trace))
29+
30+
# Noise estimation based on derivative, assuming random noise
31+
dfdt = np.diff(trace)
32+
noise = np.std(dfdt) / np.sqrt(2)
33+
34+
# Estimate signal as the third peak using scipy's find_peaks
35+
peaks, _ = find_peaks(
36+
trace,
37+
height=3 * noise, # Minimum peak height (adjust based on your signal scale)
38+
distance=fps * 0.1, # Minimum number of samples between peaks
39+
prominence=0.05, # How much a peak stands out relative to neighbors
40+
width=5 # Optional: minimum width of peak
41+
)
42+
43+
if len(peaks) < 3:
44+
# Warning if not enough peaks are found
45+
warnings.warn("Not enough peaks found to estimate SNR. Returning NaN values.")
46+
return np.nan, noise, np.nan
47+
48+
# Take the 95th percentile of peak amplitudes as the signal
49+
amplitudes = np.sort(trace[peaks])
50+
signal = np.percentile(amplitudes, 95)
51+
52+
# Calculate SNR
53+
snr = signal / noise
54+
55+
return snr, noise, peaks
56+
57+
58+
def estimate_kurtosis(trace):
59+
"""
60+
Estimate the kurtosis of a trace distribution.
61+
62+
Parameters
63+
----------
64+
trace : np.ndarray
65+
The input trace.
66+
67+
Returns
68+
-------
69+
kurt : float
70+
Estimated excess kurtosis of the distribution.
71+
(Normal distribution = 0, leptokurtic > 0, platykurtic < 0)
72+
"""
73+
# Replace NaNs with the median of the trace
74+
trace = np.nan_to_num(trace, nan=np.nanmedian(trace))
75+
76+
# Excess kurtosis (normal distribution = 0)
77+
kurt = kurtosis(trace, fisher=True, bias=False)
78+
79+
return kurt

0 commit comments

Comments
 (0)