Skip to content

Fix rr2ann with times and with a symbol column - #612

Merged
tompollard merged 2 commits into
MIT-LCP:mainfrom
TheNetherWatcher:rr2ann-fixes
Oct 31, 2026
Merged

tompollard merged 2 commits into
MIT-LCP:mainfrom
TheNetherWatcher:rr2ann-fixes

Conversation

@TheNetherWatcher

@TheNetherWatcher TheNetherWatcher commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

rr2ann has two problems:

import numpy as np, wfdb
from wfdb.processing.hr import rr2ann

rr2ann(np.array([0.29, 0.57, 1.13]), "t", "atr", fs=100, as_time=True)
wfdb.rdann("t", "atr").sample
# array([ 28,  56, 112])   (expected [29, 57, 113])

rr2ann(np.array([[4, 17], ["V", "N"]]).T, "t", "atr")
# TypeError: the resolved dtypes are not compatible with add.accumulate. Resolved
# (dtype('<U21'), dtype('<U21'), dtype('<U42'))
  1. With as_time=True, times are converted to samples with .astype(np.int64), which truncates. 0.29 * 100 is 28.999999999999996, so the annotation lands one sample early. This now rounds to the nearest sample.
  2. The docstring says a second column can give the annotation symbols. That makes the array hold strings, and without as_time the intervals go straight into np.cumsum, which fails on strings. They are now converted to integers first (the as_time path already converted to float).

Tests: times that truncate wrongly, intervals with a symbol column (both fail on main), and the docstring example with times and symbols. pytest -m "not network" passes (99 tests), and the docs build passes.

@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 good! Rounding the times fixes the truncation, and the symbol column now works.

One small thing: converting to integers before cumsum changes the result for non-integer intervals, because each interval is truncated and the error builds up:

rr2ann(np.array([4.5, 4.5, 4.5]), "t", "atr")
# main: [4, 9, 13]   this PR: [4, 8, 12]

Intervals like "4.5" with a symbol column also raise ValueError: invalid literal for int(). Summing as floats and then rounding, as the as_time branch does, handles both:

ann_sample = np.round(np.cumsum(ann_sample.astype(np.float64))).astype(np.int64)

That gives [4, 9, 14] for the example above, and your three tests still pass. ann2rr returns integers, so the usual case isn't affected either way.

@TheNetherWatcher

Copy link
Copy Markdown
Contributor Author

Thanks, good point! I've switched to summing as floats and rounding once, as you suggested. That also fixes "4.5" with a symbol column. I added a test for [4.5, 4.5, 4.5] with and without symbols ([4, 9, 14]), which fails with the integer-first version, and mentioned it in the changelog. I also rebased onto main. Tests pass (103).

Times were converted to samples by truncation, so floating-point error
moved some annotations one sample early (0.29 s at 100 Hz gave 28).
Round instead. With a symbol column the array holds strings, and
np.cumsum failed on them, so convert the intervals to integers first.
Converting each interval to an integer first truncated fractional
intervals, and the error built up over the record.

@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.

lgtm

@tompollard
tompollard merged commit a4f4a14 into MIT-LCP:main Oct 31, 2026
18 checks passed
@TheNetherWatcher
TheNetherWatcher deleted the rr2ann-fixes branch November 1, 2026 06:13
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.

2 participants