Guard CoW read-only .to_numpy()/.values mutations under pandas 3 - #3416
Guard CoW read-only .to_numpy()/.values mutations under pandas 3#3416AxelNoun wants to merge 9 commits into
Conversation
Under pandas 3.0 Copy-on-Write, .to_numpy()/.values return read-only arrays for single-dtype selections; in-place mutation raises ValueError. Use copy=True at the five affected sites. Pin unchanged.
There was a problem hiding this comment.
Pull request overview
This PR improves forward-compatibility with pandas 3.0 Copy-on-Write by ensuring NumPy arrays extracted from pandas objects remain writable at the specific sites where the code performs in-place mutations.
Changes:
- Use
to_numpy(copy=True)where extracted arrays are mutated in place (instead of relying on.to_numpy()/.valuesviews that can be read-only under pandas 3 CoW). - Add inline comments documenting the pandas 3 CoW read-only-view behavior at each updated site.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
deeplabcut/refine_training_dataset/tracklets.py |
Make gap-filling array extraction writable under pandas 3 CoW. |
deeplabcut/post_processing/filtering.py |
Ensure spline-filter branch extracts writable xy/prob arrays before in-place filling. |
deeplabcut/pose_estimation_pytorch/data/ctd.py |
Ensure per-row pose arrays are writable before masking/cleanup. |
deeplabcut/pose_estimation_3d/triangulation.py |
Ensure triangulation masking operates on writable arrays extracted from DataFrames. |
deeplabcut/pose_estimation_3d/plotting3D.py |
Ensure in-place NaN masking for visualization operates on writable arrays. |
Comments suppressed due to low confidence (1)
deeplabcut/refine_training_dataset/tracklets.py:230
- In
load_tracklets_from_hdf,data = df.to_numpy(copy=True)fixes the first read-only array issue, but laterself.data = df.values.reshape(...)can still return a read-only NumPy view under pandas 3 CoW for homogeneous float DataFrames. Sinceself.xy/self.probare mutated later (e.g. viaswap_tracklets), this can still raiseValueError: assignment destination is read-onlyafter loading from HDF.
data = df.to_numpy(copy=True)
mask = ~df.columns.get_level_values(level="coords").str.contains("likelihood")
xy = data[:, mask]
prob = data[:, ~mask]
missing = np.isnan(xy)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thanks for the review ? agreed on the remaining Pushed a follow-up that guards the three remaining CoW-sensitive mutations we could reproduce under pandas 3:
Also ran Considered, left unchanged
Out of scope (optional follow-up)In |
Follow-up to the pandas 3.0 CoW sweep (DeepLabCut#3362). Three more sites extract a NumPy array from a pandas object and mutate it in place; under pandas 3 CoW these can be read-only views, raising "assignment destination is read-only". - refine_training_dataset/tracklets.py: self.data backs self.xy/self.prob, which swap_tracklets and the refine GUI mutate in place (HDF load path; flagged in review). - utils/make_labeled_video.py: coords is a view into xyp, masked in place in both the first-frame and per-frame branches. - pose_estimation_tensorflow/.../pose_multianimal_imgaug.py: a single-row float Series is read-only; kpts is masked in place when mask_kpts_below_thresh is set. Co-authored-by: Cursor <cursoragent@cursor.com>
deruyter92
left a comment
There was a problem hiding this comment.
@AxelNoun
Apologies, I forgot to respond myself before requesting a CoPilot review. Thanks for addressing CoPilots suggestions as well.
I've reviewed your changes, and they are definitely correct and very helpful!
Thanks for catching these exceptions for the future-tooling in utils/pandas_future_mode.py. When extracting numpy arrays from the DataFrame, the warning is circumvented. I'm changing the mode from "warn" to True, to fully simulate the 3.0 behavior, and adding some tests as well.
|
Thanks Jaap, glad it's useful! And good call flipping the mode to |
Guards against the in-place write on the next line producing a read-only view when the intermediate frame becomes single-block.
|
@deruyter92 OK, perfect! Thank you, if you need me to change anything, I am available, thank you for your time! |
Guard in-place mutations of
.to_numpy()/.valuesresults (Copy-on-Write, pandas 3.0)Follow-up to #3360. Related to the tracking issue #3362.
Summary
Under pandas 3.0's Copy-on-Write,
.to_numpy()and.valuesreturn aread-only array for single-dtype selections; mutating that array in place
raises
ValueError: assignment destination is read-only. This PR guards the fivesites where such an array is mutated, using
to_numpy(copy=True).The
pandas<3upper bound is intentionally left unchanged — this is aforward-compatibility fix, not the pin removal discussed in #3362. It complements
the preparatory work already merged in #3360 and is safe on the currently pinned
pandas 2.x.
The pattern
copy=Truereturns a writable array and is behavior-preserving on pandas 2.x, sothe change is correct under both major versions.
Changes
to_numpy(copy=True)at the sites that mutate the extracted array in place:pose_estimation_pytorch/data/ctd.pypose_estimation_3d/triangulation.pypose_estimation_3d/plotting3D.pyrefine_training_dataset/tracklets.pypost_processing/filtering.py— spline-filter branch (same interpolationpattern as
tracklets.py; only thetrackletscopy is covered by the currenttests, so this one was located by a static scan rather than a failing test)
Considered and left unchanged:
pose_estimation_tensorflow/core/evaluate_multianimal.pyextracts a mixed-dtype selection (
["sample", "y", "x", "bodyparts"]), whichyields a writable object array and is therefore not affected.
Testing
the fix, and the spline-filter path is smoke-tested (writable arrays, correct
gap-filling).
behavior-preserving.
Notes for #3362
runtime under pandas 3.0, so it isn't caught by the 2.3
future-mode tooling.Migration to Pandas 3.0 #3362 ("PyTables cannot serialize a MultiIndex whose levels use extension
dtypes → change all dataframes back to
objectbefore saving") does notreproduce: pandas 3.0 explicitly whitelists
StringDtypein that guard(
io/pytables.py,write_multi_index:isinstance(lev.dtype, ExtensionDtype) and not isinstance(lev.dtype, StringDtype)),and DeepLabCut's string index/column levels serialize fine (verified for the
df_with_missing/tracks/predictions/asskeys). So noto_hdfobject-conversion wrapper appears necessary on the released 3.0. (The pandas 2.3
future.infer_stringmode does raise there, since the exemption only lands in3.0 — so that mode shouldn't be used to validate HDF writes.)