-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix to_numpy/to_cupy failures on pandas nullable extension dtypes
#23772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,12 @@ | |
|
|
||
| import cudf | ||
| from cudf.api.extensions import no_default | ||
| from cudf.api.types import is_dtype_equal, is_scalar, is_string_dtype | ||
| from cudf.api.types import ( | ||
| is_dtype_equal, | ||
| is_float_dtype, | ||
| is_scalar, | ||
| is_string_dtype, | ||
| ) | ||
| from cudf.core._internals import copying, sorting | ||
| from cudf.core.abc import Serializable | ||
| from cudf.core.column import ( | ||
|
|
@@ -642,6 +647,10 @@ def to_array( | |
| and dtype is not None | ||
| and not is_string_dtype(dtype) | ||
| and na_value is no_default | ||
| and not ( | ||
| is_pandas_nullable_extension_dtype(col.dtype) | ||
| and is_float_dtype(dtype) | ||
| ) | ||
| ): | ||
| raise ValueError( | ||
| f"cannot convert to '{dtype}'-dtype NumPy array " | ||
|
|
@@ -707,8 +716,15 @@ def to_array( | |
| if ncol == 1: | ||
| to_dtype = next(self._dtypes)[1] | ||
| if (na_value is no_default or na_value is None) and ( | ||
| isinstance(to_dtype, np.dtype) | ||
| and to_dtype.kind in "iu" | ||
| module is np | ||
| and is_pandas_nullable_extension_dtype(to_dtype) | ||
| and getattr(to_dtype, "kind", None) in ("i", "u", "f") | ||
| and self._columns[0].has_nulls() | ||
| and pd.options.future.distinguish_nan_and_na | ||
| ): | ||
| to_dtype = np.dtype(object) | ||
|
Comment on lines
+719
to
+725
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
python - <<'PY'
import numpy as np
import pandas as pd
import cudf
with pd.option_context("future.distinguish_nan_and_na", True):
pser = pd.Series([1.0, pd.NA], dtype="Float32")
gser = cudf.Series(pser)
expected = pser.to_numpy(na_value=None)
actual = gser.to_numpy(na_value=None)
assert actual.dtype == expected.dtype, (actual.dtype, expected.dtype)
np.testing.assert_equal(actual, expected)
pdf = pd.DataFrame({"a": pser})
gdf = cudf.DataFrame(pdf)
expected_df = pdf.to_numpy(na_value=None)
actual_df = gdf.to_numpy(na_value=None)
assert actual_df.dtype == expected_df.dtype, (
actual_df.dtype,
expected_df.dtype,
)
np.testing.assert_equal(actual_df, expected_df)
PYRepository: NVIDIA/cudf Length of output: 268 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- frame.py changed ranges ---'
sed -n '1,40p;620,740p;875,925p' python/cudf/cudf/core/frame.py
printf '%s\n' '--- direct helper definitions/usages ---'
rg -n -C 4 'def (has_nulls|to_numpy)|has_nulls\(|is_pandas_nullable_extension_dtype|future\.distinguish_nan_and_na|na_value' \
python/cudf/cudf/core python/cudf/cudf | head -240
printf '%s\n' '--- declared pandas dependency ---'
rg -n -C 3 'pandas|pandas==|pandas>=' pyproject.toml setup.py setup.cfg requirements\* python -g '*.toml' -g '*.txt' -g '*.yaml' -g '*.yml' -g '*.py' 2>/dev/null | head -180Repository: NVIDIA/cudf Length of output: 38596 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository conventions and learnings ---'
find /tmp/coderabbit-repo-knowledge/nvidia-cudf-5d380a13 -mindepth 2 -maxdepth 2 -type f -name '*.md' -print \
-exec sh -c 'printf "\n--- %s ---\n" "$1"; head -80 "$1"' _ {} \;
printf '%s\n' '--- sentinel definition and direct conversion contracts ---'
rg -n -C 8 'no_default\s*=|class _NoDefault|def to_numpy\(|def fillna\(' \
python/cudf/cudf/api python/cudf/cudf/core python/cudf/cudf | head -260
printf '%s\n' '--- pandas dependency metadata ---'
rg -n -C 4 'pandas' \
conda recipes ci dependencies.yaml dependencies.yml environment.yml pyproject.toml setup.py setup.cfg \
python -g '*.yaml' -g '*.yml' -g '*.toml' -g '*.py' -g '*.txt' 2>/dev/null | head -220Repository: NVIDIA/cudf Length of output: 50369 🌐 Web query:
💡 Result: In pandas 3.0.3, the Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/arrays/base.py > "$tmpdir/base.py"
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/internals/blocks.py > "$tmpdir/blocks.py"
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/frame.py > "$tmpdir/frame.py"
printf '%s\n' '--- pandas ExtensionArray.to_numpy ---'
rg -n -C 24 'def to_numpy\(' "$tmpdir/base.py" | head -130
printf '%s\n' '--- pandas nullable floating conversion helpers ---'
rg -n -C 18 'na_value|to_numpy|_values_for_numpy' "$tmpdir/base.py" "$tmpdir/blocks.py" | grep -E 'base.py|blocks.py|def to_numpy|def get_values|_values_for_numpy|na_value|isna' | head -220
printf '%s\n' '--- pandas DataFrame.to_numpy ---'
rg -n -C 30 'def to_numpy\(' "$tmpdir/frame.py" | head -120Repository: NVIDIA/cudf Length of output: 16562 🏁 Script executed: #!/bin/bash
set -u
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
for path in \
pandas/core/arrays/masked.py \
pandas/core/arrays/floating.py \
pandas/core/internals/managers.py \
pandas/core/internals/array_manager.py
do
file="$tmpdir/$(basename "$path")"
curl -fsSL "https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/$path" > "$file" || exit 1
done
printf '%s\n' '--- masked array conversion ---'
rg -n -C 28 'def __array__|def to_numpy|na_value' "$tmpdir/masked.py" | head -180 || true
printf '%s\n' '--- floating array conversion ---'
rg -n -C 16 'def __array__|def to_numpy|na_value' "$tmpdir/floating.py" | head -120 || true
printf '%s\n' '--- pandas manager conversion ---'
rg -n -C 35 'def as_array|na_value' "$tmpdir/managers.py" "$tmpdir/array_manager.py" | head -260 || trueRepository: NVIDIA/cudf Length of output: 198 🏁 Script executed: #!/bin/bash
set -euo pipefail
curl -fsSL 'https://api.github.com/repos/pandas-dev/pandas/git/trees/v3.0.3?recursive=1' |
jq -r '.tree[].path' |
rg '(^|/)(masked|floating|managers|array_manager|base)\.py$|internals' |
head -160Repository: NVIDIA/cudf Length of output: 958 🏁 Script executed: #!/bin/bash
set -euo pipefail
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
for path in pandas/core/arrays/masked.py pandas/core/arrays/floating.py pandas/core/internals/managers.py; do
curl -fsSL "https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/$path" \
> "$tmpdir/$(basename "$path")"
done
printf '%s\n' '--- masked array conversion ---'
rg -n -C 32 'def __array__|def to_numpy|na_value' "$tmpdir/masked.py" | head -220 || true
printf '%s\n' '--- floating array conversion ---'
rg -n -C 20 'def __array__|def to_numpy|na_value' "$tmpdir/floating.py" | head -160 || true
printf '%s\n' '--- manager conversion ---'
rg -n -C 38 'def as_array|na_value' "$tmpdir/managers.py" | head -300 || trueRepository: NVIDIA/cudf Length of output: 16685 🏁 Script executed: #!/bin/bash
set -euo pipefail
for spec in \
'pandas/core/arrays/masked.py:508:590' \
'pandas/core/arrays/base.py:680:705' \
'pandas/core/arrays/base.py:300:380'
do
path="${spec%%:*}"
range="${spec#*:}"
start="${range%%:*}"
end="${range##*:}"
printf '\n--- %s lines %s-%s ---\n' "$path" "$start" "$end"
curl -fsSL "https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/$path" |
sed -n "${start},${end}p"
doneRepository: NVIDIA/cudf Length of output: 6518 🏁 Script executed: #!/bin/bash
set -euo pipefail
for spec in \
'pandas/core/arrays/masked.py:570:650' \
'pandas/core/arrays/masked.py:1:120' \
'pandas/core/arrays/floating.py:1:120'
do
path="${spec%%:*}"
range="${spec#*:}"
start="${range%%:*}"
end="${range##*:}"
printf '\n--- %s lines %s-%s ---\n' "$path" "$start" "$end"
curl -fsSL "https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/$path" |
sed -n "${start},${end}p"
doneRepository: NVIDIA/cudf Length of output: 8933 🏁 Script executed: #!/bin/bash
set -euo pipefail
for spec in \
'pandas/core/arrays/_utils.py:1:220' \
'pandas/tests/arrays/masked/test_to_numpy.py:1:260'
do
path="${spec%%:*}"
range="${spec#*:}"
start="${range%%:*}"
end="${range##*:}"
printf '\n--- %s lines %s-%s ---\n' "$path" "$start" "$end"
curl -fsSL "https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/$path" |
sed -n "${start},${end}p"
doneRepository: NVIDIA/cudf Length of output: 2800 🏁 Script executed: #!/bin/bash
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/dtypes/cast.py |
rg -n -C 35 'def np_can_hold_element' | head -180Repository: NVIDIA/cudf Length of output: 2920 🏁 Script executed: #!/bin/bash
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/dtypes/cast.py |
sed -n '1688,1768p'Repository: NVIDIA/cudf Length of output: 3675 🏁 Script executed: #!/bin/bash
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/pandas-dev/pandas/v3.0.3/pandas/core/dtypes/cast.py |
sed -n '1760,1845p'Repository: NVIDIA/cudf Length of output: 3597 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- ColumnBase.fillna ---'
sed -n '1915,1995p' python/cudf/cudf/core/column/column.py
printf '%s\n' '--- column numpy conversion ---'
sed -n '1180,1235p' python/cudf/cudf/core/column/column.py
printf '%s\n' '--- cudf NA and fillna(None) handling ---'
rg -n -C 8 'fillna\(None\)|fill_value is None|fill_value.*None|cudf\.NA|def fillna' \
python/cudf/cudf/core/column python/cudf/cudf/core/frame.py python/cudf/cudf/core/missing.py \
2>/dev/null | head -220Repository: NVIDIA/cudf Length of output: 16849 Preserve explicit With 🤖 Prompt for AI Agents |
||
| elif (na_value is no_default or na_value is None) and ( | ||
| getattr(to_dtype, "kind", None) in ("i", "u") | ||
| and self._columns[0].has_nulls() | ||
| ): | ||
| # A single nullable integer column must be promoted to | ||
|
|
@@ -884,11 +900,13 @@ def to_cupy( | |
| elif self._num_columns == 1: | ||
| col = self._columns[0] | ||
| final_dtype = col.dtype if dtype is None else dtype | ||
| final_dtype = getattr(final_dtype, "numpy_dtype", final_dtype) | ||
| col_dtype = getattr(col.dtype, "numpy_dtype", col.dtype) | ||
|
|
||
| if ( | ||
| not copy | ||
| and col.dtype.kind in {"i", "u", "f", "b"} | ||
| and cupy.can_cast(col.dtype, final_dtype) | ||
| and col_dtype.kind in {"i", "u", "f", "b"} | ||
| and cupy.can_cast(col_dtype, final_dtype) | ||
| ): | ||
| if col.has_nulls(): | ||
| if na_value is not None: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.