Skip to content

BUG: Preserve NumPy complex scalars in object data - #976

Closed
galafis wants to merge 1 commit into
bashtage:mainfrom
galafis:fix/object-complex-data
Closed

galafis wants to merge 1 commit into
bashtage:mainfrom
galafis:fix/object-complex-data

Conversation

@galafis

@galafis galafis commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

as_float64 first tries astype(float64) on object arrays. Python complex values raise TypeError and reach the complex conversion path, but NumPy complex scalars can instead cast to real with a ComplexWarning. An object array containing np.complex128(1+2j) therefore becomes [1.] even with allow_complex=True; with allow_complex=False it also passes instead of raising.

Detect complex scalar elements before casting, in both the ndarray and pandas conversion paths. Convert permitted complex object data to complex128 and reject it through the existing TypeError path when complex data are not allowed.

Add both cases to the dtype policy table and assert numerical values as well as output dtype for successful object conversions. Both NumPy-complex cases fail before the fix. The complete dtype policy module passes after the fix (485 tests), including 24 cases spanning four helpers and ndarray, Series and DataFrame inputs; Ruff lint and formatting pass for both changed files. This follows the documented complex-data policy in doc/source/contributing.rst.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-24T17:16:33.336347Z
We couldn’t complete this review. Comment @codex review to try again.
7209098 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@bashtage

Copy link
Copy Markdown
Owner

The other way would be to just reject object arrays and let the user handle conversion to a an inexact type.

@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.95%. Comparing base (5db69b0) to head (7209098).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #976   +/-   ##
=======================================
  Coverage   98.95%   98.95%           
=======================================
  Files         137      137           
  Lines       30099    30124   +25     
  Branches     2337     2343    +6     
=======================================
+ Hits        29785    29810   +25     
  Misses        256      256           
  Partials       58       58           
Flag Coverage Δ
adder 98.90% <100.00%> (+<0.01%) ⬆️
subtractor 98.90% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@galafis

galafis commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Rejecting object arrays would simplify this path. The existing dtype-policy tests allow conversion of object inputs, so this patch preserves that behavior and fixes the loss of imaginary components within it.

Would you prefer to retain those conversions for this fix, or reject object inputs here? The validation cases can be adjusted to the chosen input policy.

@bashtage bashtage left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I'm OK with this direction. I think the performance of the complex check can be improved using a 2-step approach, try astype complex128, if fail, it is not numeric, then if pass, check if imag are all 0. If all 0, then downcast to float64. Can avoid python loops with this method.

Comment thread arch/utility/array.py
f"({type(x).__name__})"
)
try:
if any(isinstance(item, (complex, np.complexfloating)) for item in arr.flat):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

How about this. Rather than a loop, if allowed, we try to cast astype(np.complex128). If this fails, then we know it is not numeric. If it succeeds, we can then do a single pass to check if all imag values are 0, and if they are, recast unsafe to float64. This would use more numpy and less python looping.

Comment thread arch/utility/array.py
"""
try:
dtypes = list(x.dtypes) if isinstance(x, DataFrame) else [x.dtype]
if target is np.float64 and any(dtype.kind == "O" for dtype in dtypes):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Should pull this up into a single helper function along with the other check of object could be complex. Then there is one definitive method, whether it is optimized or not.

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