Skip to content

Fix pandas-3 crash in get_contained_labels: set_index on string .values - #575

Merged
tompollard merged 2 commits into
MIT-LCP:mainfrom
wenh06:fix/set-index-pandas3
Nov 2, 2026
Merged

tompollard merged 2 commits into
MIT-LCP:mainfrom
wenh06:fix/set-index-pandas3

Conversation

@wenh06

@wenh06 wenh06 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #561 (v4.3.1). As analyzed in #574 (with a correction below), the module-level import crash is indeed fixed, but get_contained_labels still passes string columns' .values directly to set_index:

  • label_map.set_index(label_map["symbol"].values, inplace=True)
  • label_map.set_index(label_map["description"].values, inplace=True)

Under pandas' new string dtype (pandas 3.0, or 2.x with future.infer_string), Series.values returns an ArrowStringArrayNumpySemantics, which set_index rejects with TypeError. Passing the column label is equivalent on every pandas version and immune to the dtype change.

Correction to the analysis in #574 (verified by column dtype + a pandas sandbox): the other .values sites listed there (label_store columns at ~728/~1325/~1368/~1518/~3232) use integer columns, whose .values stays a numpy array and passes set_index today — they are not currently broken. This PR fixes the two sites that actually raise; the integer-column sites are left untouched to keep the diff minimal (they could be unified cosmetically later if preferred).

Verified in a pandas sandbox (future.infer_string = True):

  • df.set_index(df["str_col"].values) → TypeError (both sites' pattern)
  • df.set_index("str_col") → OK
  • integer-column variants → OK (unchanged behavior)

Fixes #574.

@cbrnr

cbrnr commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Just two quick notes after testing on pandas 3.1:

  • set_index("symbol") uses drop=True, so the symbol (or description) column no longer appears in contained_labels. The old .values version kept it, and drop=False restores that behavior.
  • inplace=True is deprecated in pandas 3.1 and raises Pandas4Warning. Reassigning avoids it:
label_map = label_map.set_index("symbol", drop=False)
label_map = label_map.set_index("description", drop=False)

@wenh06

wenh06 commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: while validating this fix downstream (pandas 3.1.0, wfdb 4.3.1), I noticed that set_index("symbol") consumes the column as the index (drop defaults to True), so the returned contained_labels loses the symbol/description column compared to the pandas<3 path, which indexed by an anonymous array and kept every column. Pushed 9af7b67 adding drop=False to both sites -- the output schema now matches the pandas<3 behavior exactly (verified: (1, 4) with label_store/symbol/description/n_occurrences).

@wenh06

wenh06 commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Both points are now addressed on this branch:

  • drop=False: pushed in 9af7b67 (we had independently hit the same schema change while validating the fix downstream against pandas 2.3.3 -- the returned contained_labels dropped the symbol column; verified identical output with drop=False).
  • inplace=True: pushed in 4166cfa -- both string-column set_index calls now use reassignment.

The remaining inplace=True calls on the integer label_store columns (lines ~1325 and ~1367) predate this PR; happy to convert those too (and the set_index at line 1472 in set_label_elements) if you prefer the whole methods cleaned up -- just say the word.

wenh06 added a commit to DeepPSP/torch_ecg that referenced this pull request Nov 1, 2026
…db shim

Second review note on MIT-LCP/wfdb-python#575 (cbrnr): pandas 3.1
deprecates the inplace keyword of DataFrame.set_index (Pandas4Warning,
removed in pandas 4) -- the shim would have broken on pandas 4.
Convert all four set_index calls in the patched method to the
reassignment form; the shim now emits no deprecation warnings of its
own (the one remaining Pandas4Warning in a get_contained_labels call
chain comes from wfdb's own set_label_elements, outside this shim's
method).  Output schema and values re-verified identical to the
pandas 2.3 reference.
@cbrnr

cbrnr commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Yes, please remove all inplace=True occurrences if possible.

@wenh06

wenh06 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Done in ccdf1fc -- all six set_index(..., inplace=True) call sites converted to reassignment (custom_labels in check_label_definitions and get_contained_labels, contained_labels, label_map in set_label_from_source, label_df in label_triplets_to_df, and the module-level ann_label_table). The remaining inplace occurrences are wfdb's own API parameters, not pandas calls.

Verified on pandas 3.1.0: rdann -> get_contained_labels -> set_label_elements now emits zero Pandas4Warning, and the contained_labels schema is unchanged.

@cbrnr

cbrnr commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

There's one more inplace=True here: https://github.com/wenh06/wfdb-python/blob/ccdf1fcf9da20acfc00d5c107e24962a3848283d/wfdb/io/annotation.py#L3062

Also, can you rebase please?

…ues)

Under pandas >= 3 (Arrow-backed strings), DataFrame.set_index rejects
the ArrowStringArray produced by <string column>.values, so
get_contained_labels raises TypeError whenever the annotation carries
symbol or description.  Pass the column names instead, with
drop=False so the symbol/description column survives in the returned
contained_labels (the old array-index path kept every column; plain
set_index("symbol") would consume it).

This is MIT-LCP#575 rebased onto the restructured main: main has since moved
to the src layout and independently converted all
set_index(inplace=True) call sites to reassignments, so the remaining
diff is exactly this two-line fix.  Verified on pandas 3.1.0:
rdann -> get_contained_labels -> set_label_elements emits zero
Pandas4Warning and the contained_labels schema is unchanged.
@wenh06
wenh06 force-pushed the fix/set-index-pandas3 branch from ccdf1fc to df9ccd7 Compare November 2, 2026 08:47
@wenh06

wenh06 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto the restructured main (df9ccd7). Since the branch was last updated, main moved to the src layout and independently converted all set_index(inplace=True) call sites (annotation.py including ann_class_table, and _header.py's drop) to reassignments -- so the diff is now exactly the two-line fix this PR was about:

label_map = label_map.set_index("symbol", drop=False)      # was label_map["symbol"].values
label_map = label_map.set_index("description", drop=False)  # was label_map["description"].values

(drop=False keeps the column in the returned contained_labels, matching the pandas<3 output schema.)

The crash itself is still reproducible on current main under pandas 3.1.0 (get_contained_labels on any annotation with symbol/description raises TypeError: ... ArrowStringArray); with this diff, rdann -> get_contained_labels -> set_label_elements runs warning-free and the schema is unchanged.

@cbrnr

cbrnr commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

@tompollard please merge if you're happy - I'm not sure about a changelog entry?

No test called get_contained_labels, so the crash with pandas 3 was not
caught. The test reads an annotation with symbols only, as rdann returns
by default, and fails on pandas 3 without the fix.
@tompollard

tompollard commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Thanks, looks good. I checked it with pandas 3.1 and 2.2.3: the crash is fixed, and the output is identical to before on pandas 2.

Yes, I think it's worth a changelog entry, since rdann(...).get_contained_labels() fails on main with pandas 3. I've added one in changelog.d/, along with a small test (nothing called get_contained_labels before, which is why CI didn't catch this). I'll merge once the checks pass.

@cbrnr thanks for your reviews on this!

@tompollard
tompollard merged commit f9d03e3 into MIT-LCP:main Nov 2, 2026
18 checks passed
@cbrnr

cbrnr commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Very nice! Thanks @wenh06 and @tompollard!

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.

pandas 3.0: three remaining set_index(<str column>.values) calls still raise TypeError (custom-labels paths)

3 participants