wfdb-python
wfdb-python copied to clipboard
Improvements for correct_peaks
- [ ] Correct peaks to the same side of the signal if it is possible:
- [ ] Handle slow_bpm_gap
- [x] Add docstring to correct_peaks
Why is this function needed? It is if you want to get a peak position exactly on the R peak. Indeed, algorithms such as GQRS and Pan&Tompkins do not return the exact position of the R peak, but instead the approximative position of the QRS complex. It is also needed to correct physiologically impossible peaks (too close peaks for example).
How this function works:
It is based on slow_bpm_gap and fast_bpm_gap parameters that are computed as follows:
slow_bpm = 10
fast_bpm = 350
slow_bpm_gap = record.fs*60/slow_bpm # for fs=125, slow_bpm_gap=750
fast_bpm_gap = record.fs*60/fast_bpm # for fs=125, fast_bpm_gap=21
- It takes as input the signal itself and a list of peak indexes (from an algorithm such as GQRS)
- First, it computes the moving average (smooth) of the signal (using the smooth_window parameter)
- For peaks that are next to each other, we select the best one (the one that has the largest distance to the smoothed curve)
- For peaks b,c that are too close to each other (dist(b,c)<fast_bpm_gap) we choose the one that has the largest distance to the smoothed curve. This poses a problem in that maybe the previous and next peaks a,d are also too close and that maybe we should have based our selection on c,d rather than b,c. This has to be fixed.
This function also uses the find_peaks function from wfdb.processing.peaks that detect what I called "hard" and "soft" peaks. Those are really signal peaks, not QRS peaks:
Soft peaks are in blue and hard peaks are in red.
- Hard peak: a peak that is either /\ or \/
- Soft peak: a peak that is either /-*\ or \-*/ (In that case we define the middle of it as the peak)
At the end, the result of correct_peaks is:
I'm removing the min_gap argument for now since it's not being used.
Edit: I think I know what it's meant to be doing. I can implement it. So not removing it :)
I wanted to rename min_gap and max_gap to slow_bpm_gap and fast_bpm_gapas it is more explicit. (min_gap->slow_bpm_gap & max_gap->fast_bpm_gap)
The thing about slow_bpm_gap is that it is physiologically possible to see two peaks at a distance superior to slow_bpm_gap (e.g. https://www.hrsonline.org/Patient-Resources/Symptoms-Diagnosis/Skipped-Beats, or even a temporary cardiac arrest). Maybe slow_bpm_gap can be an option instead of a requirement for this function. However, what is the correction to do in the case of two peaks too distant from each other? I don't think it is relevant to propose and implement this option. In contrary, fast_bpm_gap is a real physiologically limit, one cannot have a heart rate faster than a certain limit. I found this article talking about someone having a record-breaking 600bpm rate (https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3273956/), the usual limit is somewhere around 350bpm for fetus (https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2016910/), and for adults, it is around 220-age so I think this option is relevant.
Did you think about something else for the slow_bpm_gap?
Right now your function seems to work as a correct_peaks
kind of function, which just shifts items to local maxima within a search radius.
Perhaps we should split the concepts, and add a new function called correct_beats
which does more investigation regarding actual heartbeats?
I'd like to push out a new version, so do you think it's fine if I release a correct_peaks
function?
It not only shifts to local maxima, it also delete one peak when two are too close from each other.
Spliting the concepts is a good idea!
Yes sure ;)