fix(polars): keep integers exact when a polars column holds nulls - #467
Merged
Merged
Conversation
pl.DataFrame.to_pandas() renders an integer column that has nulls as float64, because a NumPy integer array cannot hold one. That silently rounds every value a float64 cannot represent: 2**53 + 1 came back as 2**53, and a UInt64 beyond 2**63 lost far more. This is not limited to engine="polars". Every public entry point reads a polars source through adapters.polars.to_pandas(), so fd.clean(pl_df) on the DEFAULT engine already returned the rounded value. Integer columns that hold nulls are now rebuilt from the raw integers plus a null mask, giving the pandas nullable dtype of the same width (Int64, UInt64, Int32, ...) — lossless, and what the same data already looked like when passed in as pandas. Columns without nulls round-trip exactly today and are untouched. The native polars engine converts its result through the same adapter instead of calling frame.to_pandas() directly. Default-output change: a polars input whose integer column holds nulls now cleans as a nullable integer column instead of float64. Closes #444
Contributor
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
FreshData benchmark report —
|
| fixture | n_rows | n_cols | p50 s | p95 s | peak MB | repair % | false-repair % | preserve % | trust | monotonic | export % |
|---|
Authored-code reduction (Metric 6)
# Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #444.
pl.DataFrame.to_pandas()renders an integer column that has nulls asfloat64, because a NumPy integer array cannot hold one. That silently rounds every value a float64 cannot represent.This is not limited to
engine="polars". Every public entry point reads a polars source throughadapters.polars.to_pandas(), so the rounding happens on the default engine too:A
UInt64beyond2**63loses considerably more.The fix: integer columns that hold nulls are rebuilt from the raw integers plus a null mask, giving the pandas nullable dtype of the same width (
Int64,UInt64,Int32, …) — lossless, and what the same data already looked like when passed in as pandas. Integer columns without nulls round-trip exactly today and are left untouched (int64staysint64). The native polars engine now converts its result through the same adapter instead of callingframe.to_pandas()directly.Default-output change: a polars input whose integer column holds nulls now cleans as a nullable integer column instead of
float64. This is the one behaviour change and it is the point of the fix — it belongs in the 2.2.0 tally.Out of scope, filed separately as #465: native ingestion also drops extension dtypes without changing any value (
Int64-without-nulls →int64,Float64→float64,string→object). This PR fixes only the case where data is lost.Verification — py3.12/pandas 2.3.3/polars 1.42.1 and py3.9/pandas 1.5.3/polars 1.36.1:
tests/test_polars_adapter.py; all 5 fail onmainInt64/UInt64/Int32with nulls, on the adapter, the default engine and the native polars engine