Skip to content

fix: preserve column order in PointsModel.parse() - #1192

Merged
LucaMarconato merged 1 commit into
scverse:mainfrom
dylanpulver:fix/points-column-order
Aug 25, 2026
Merged

fix: preserve column order in PointsModel.parse()#1192
LucaMarconato merged 1 commit into
scverse:mainfrom
dylanpulver:fix/points-column-order

Conversation

@dylanpulver

Copy link
Copy Markdown
Contributor

PointsModel.parse() appends the non-coordinate columns by iterating set(data.columns) - {...}, so their order in the returned element follows string hash order and varies with PYTHONHASHSEED.

The reordering safeguard at the end of parse() only fires when the column set is unchanged, which hides this on the common path. Passing coordinates= to rename coordinate columns changes that set, which skips the safeguard and leaves the order shuffled. The np.ndarray path has no safeguard.

This looks like the root cause of #486, closed as not reproducible and suspected to be a dask bug. Column order is not load-bearing for correctness, but it lands in the written Zarr, so two runs of one pipeline produce byte-different stores.

Both loops now walk data.columns / annotation.columns in order. I left the safeguard in place because it normalizes the case where the set is unchanged but the coordinate columns were reordered.

Sorting the extra columns alphabetically would also be deterministic. I did not, because it discards input order and would change output on the path the safeguard covers.

Measured on the pandas path with six extra columns: PYTHONHASHSEED 0 through 5 gave six distinct column orders before, one order after. The new test fails on 30 of 30 seeds with the source change reverted.

Stores written before this keep whatever order they were written with.

@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.40%. Comparing base (908ae5e) to head (d5a3a8a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1192   +/-   ##
=======================================
  Coverage   92.39%   92.40%           
=======================================
  Files          52       52           
  Lines        7868     7872    +4     
=======================================
+ Hits         7270     7274    +4     
  Misses        598      598           
Files with missing lines Coverage Δ
src/spatialdata/models/models.py 88.13% <100.00%> (+0.08%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LucaMarconato

Copy link
Copy Markdown
Member

Thanks @dylanpulver, well spotted and great fix!

@LucaMarconato
LucaMarconato merged commit 7944486 into scverse:main Aug 25, 2026
10 checks passed
@LucaMarconato

Copy link
Copy Markdown
Member

@dylanpulver how did you discover this bug? Was a part of your code dependent of the order of the columns and it changed as the seed (for some reasons) changed?

I mention this because the fix will make the code seed invariant, but may change the order of the returned column (in a predictable way this time). This will not impact data correctness, as columns are labeled and can be used independently of the order; but if users were computing some hashes based depending on the column names, for instance to cache/store some intermediate results, those hashes may change (potentially leading to precomputed data being recomputed).

@LucaMarconato

Copy link
Copy Markdown
Member

For the maintainers: I created and added the label "todo-add-extra-comments-on-release-notes", so when we auto-generate release notes we remember to add a comment mentioning potential order implications to users.

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