Skip to content

Select ann2rr intervals by time in the record - #622

Merged
tompollard merged 4 commits into
MIT-LCP:mainfrom
TheNetherWatcher:ann2rr-time-range
Nov 1, 2026
Merged

tompollard merged 4 commits into
MIT-LCP:mainfrom
TheNetherWatcher:ann2rr-time-range

Conversation

@TheNetherWatcher

Copy link
Copy Markdown
Contributor

Fixes #617.

ann2rr's start_time and stop_time were compared with the length of each interval, not its position in the record. They now select intervals by when they end in the record, matching the WFDB ann2rr program's -f and -t options:

  • annotations from start_time to stop_time (both inclusive, as in ann2rr) are used
  • the first interval is measured from start_time (from 0 when it isn't given, as before)

With record 100 (360 Hz):

Call Before After
start_time=0.9 30 intervals, all longer than 0.9 s 2272 intervals, first one 46 samples (0.9 s → first beat at 370)
stop_time=0.5 2 intervals, both shorter than 0.5 s 1 interval, the beat at sample 77

A parametrized test covers start only, stop only, both, a beat exactly at the start time (a zero-length first interval, as in ann2rr) and a range with no beats. Four of the cases fail without the change.

The start_time and stop_time arguments of ann2rr were compared with the
length of each interval rather than with its position in the record, so
start_time=0.9 dropped every interval shorter than 0.9 seconds.

Select annotations between start_time and stop_time instead, as the WFDB
ann2rr program's -f and -t options do, and measure the first interval
from start_time.

Fixes MIT-LCP#617.

@tompollard tompollard left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great! I compared it with the WFDB ann2rr program (-f and -t, with and without -A) on records 100, 1003, 201, 203 and 232, and the intervals match exactly for every normal time range.

Two optional suggestions:

  1. With start_time after stop_time, the result is silently empty (the C program swaps them). Raising a ValueError might be clearer:

    if start_time is not None and stop_time is not None and start_time > stop_time:
        raise ValueError("start_time must not be after stop_time")
  2. round() rounds halves to even, while the WFDB library rounds them up ((long)(x*f + 0.5)), so a time exactly half a sample in can give a different sample (for example start_time=0.002 at 250 Hz gives 0 here and 1 in ann2rr). int(start_time * ann.fs + 0.5) would match exactly. This is a very small edge case, so feel free to leave it.

(stop_time=0 gives an empty result here, while -t 0 means "no limit" in the C program. I think the behaviour here is the right one.)

Suggested in review: raise a ValueError instead of returning nothing when
the range is reversed, and round half a sample up as the WFDB library does.
@TheNetherWatcher

Copy link
Copy Markdown
Contributor Author

Thanks for checking against the C program! I took both suggestions:

  1. start_time after stop_time now raises a ValueError, checked before the annotation file is read.
  2. Times now round half a sample up, as in the WFDB library. A new test case uses start_time=0.125 at 100 Hz, which gives sample 13 rather than 12.

I agree on stop_time=0, so I left it as it is. I also merged main and moved the changelog entry to changelog.d/ann2rr-time-range.rst.

@tompollard

Copy link
Copy Markdown
Member

PhysioNet is having a moment, which I assume is why tests are failing. Sorting it out now...

@tompollard tompollard left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great! I re-ran the comparison with ann2rr and everything still matches.

@tompollard
tompollard merged commit cdfc64a into MIT-LCP:main Nov 1, 2026
19 of 50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ann2rr filters intervals by length instead of by time in the record

2 participants