Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 59 additions & 68 deletions ci/cudf_pandas_scripts/run_tests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -eoxu pipefail
Expand Down Expand Up @@ -81,72 +81,63 @@ python -m pytest -p cudf.pandas \
-k "profiler" \
./python/cudf/cudf_pandas_tests/

version_lte() {
[ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]
}

read -r -a versions <<< "$(python ci/utils/get_matrix_values.py dependencies.yaml test_cudf_pandas_compat pandas_compat_version)"

if version_lte "${RAPIDS_PY_VERSION}" "3.13"; then
for version in "${versions[@]}"; do
rapids-logger "Testing cudf.pandas compatibility with pandas ${version}.*"

# Generate requirements for this pandas compat version.
# Each entry pins numpy<2 + the specific pandas minor line + the CUDA-appropriate cupy<14.
# cupy>=14 dropped support for numpy<2 (see https://github.com/cupy/cupy/pull/9406).
pandas_requirements_txt="pandas-compat-${version}-requirements.txt"
rapids-dependency-file-generator \
--config dependencies.yaml \
--file-key test_cudf_pandas_compat \
--output requirements \
--matrix "cuda=${RAPIDS_CUDA_VERSION};pandas_compat_version=${version}" \
> "${pandas_requirements_txt}"

env_name="venv_pandas_${version}"
python -m venv --clear "${env_name}"
# shellcheck disable=SC1090
source "${env_name}/bin/activate"

# notes:
#
# * echo to expand wildcard before adding `[test,cudf-pandas-tests]` requires for pip
# * need to provide --constraint="${PIP_CONSTRAINT}" because that environment variable is
# ignored if any other --constraint are passed via the CLI
#
rapids-pip-retry install \
-v \
--constraint ./constraints.txt \
--constraint "${PIP_CONSTRAINT}" \
"$(echo "${CUDF_WHEELHOUSE}"/cudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)[test,cudf-pandas-tests]" \
"$(echo "${LIBCUDF_WHEELHOUSE}"/libcudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)" \
"$(echo "${PYLIBCUDF_WHEELHOUSE}"/pylibcudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)" \
-r "${pandas_requirements_txt}"

python -m pytest -p cudf.pandas \
--ignore=./python/cudf/cudf_pandas_tests/third_party_integration_tests/ \
--numprocesses=8 \
--dist=worksteal \
-k "not profiler" \
-m "not serial" \
--config-file=./python/cudf/pyproject.toml \
--cov-config=./python/cudf/.coveragerc \
--cov=cudf \
--cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cudf-pandas-coverage.xml" \
--cov-report=term \
./python/cudf/cudf_pandas_tests/

# NOTE: We don't currently run serial tests (only 1 as of 2025-07-25)
# with multiple versions of pandas.

python -m pytest -p cudf.pandas \
--ignore=./python/cudf/cudf_pandas_tests/third_party_integration_tests/ \
--numprocesses=0 \
-k "profiler" \
./python/cudf/cudf_pandas_tests/

deactivate
rm -rf "${env_name}" "${pandas_requirements_txt}"
done
else
rapids-logger "Python ${RAPIDS_PY_VERSION} detected (>= 3.13). Skipping cudf.pandas compatibility tests with numpy<2"
fi
for version in "${versions[@]}"; do
rapids-logger "Testing cudf.pandas compatibility with pandas ${version}.*"

# Generate requirements for this pandas compat version.
# Each entry pins the specific pandas minor line and the CUDA-appropriate cupy.
pandas_requirements_txt="pandas-compat-${version}-requirements.txt"
rapids-dependency-file-generator \
--config dependencies.yaml \
--file-key test_cudf_pandas_compat \
--output requirements \
--matrix "cuda=${RAPIDS_CUDA_VERSION};pandas_compat_version=${version}" \
> "${pandas_requirements_txt}"

env_name="venv_pandas_${version}"
python -m venv --clear "${env_name}"
# shellcheck disable=SC1090
source "${env_name}/bin/activate"

# notes:
#
# * echo to expand wildcard before adding `[test,cudf-pandas-tests]` requires for pip
# * need to provide --constraint="${PIP_CONSTRAINT}" because that environment variable is
# ignored if any other --constraint are passed via the CLI
#
rapids-pip-retry install \
-v \
--constraint ./constraints.txt \
--constraint "${PIP_CONSTRAINT}" \
"$(echo "${CUDF_WHEELHOUSE}"/cudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)[test,cudf-pandas-tests]" \
"$(echo "${LIBCUDF_WHEELHOUSE}"/libcudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)" \
"$(echo "${PYLIBCUDF_WHEELHOUSE}"/pylibcudf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)" \
-r "${pandas_requirements_txt}"

python -m pytest -p cudf.pandas \
--ignore=./python/cudf/cudf_pandas_tests/third_party_integration_tests/ \
--numprocesses=8 \
--dist=worksteal \
-k "not profiler" \
-m "not serial" \
--config-file=./python/cudf/pyproject.toml \
--cov-config=./python/cudf/.coveragerc \
--cov=cudf \
--cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cudf-pandas-coverage.xml" \
--cov-report=term \
./python/cudf/cudf_pandas_tests/

# NOTE: We don't currently run serial tests (only 1 as of 2025-07-25)
# with multiple versions of pandas.

python -m pytest -p cudf.pandas \
--ignore=./python/cudf/cudf_pandas_tests/third_party_integration_tests/ \
--numprocesses=0 \
-k "profiler" \
./python/cudf/cudf_pandas_tests/

deactivate
rm -rf "${env_name}" "${pandas_requirements_txt}"
done
16 changes: 8 additions & 8 deletions python/cudf/cudf/api/extensions/accessor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

import warnings
Expand Down Expand Up @@ -60,7 +60,7 @@

>>> df = cudf.DataFrame({'x': [1,2,3,4,5,6], 'y':[7,6,5,4,3,2]})
>>> df.point.bounding_box
(1, 2, 6, 7)
(np.int64(1), np.int64(2), np.int64(6), np.int64(7))

"""

Expand All @@ -79,11 +79,11 @@

>>> gs = cudf.Index(list(range(0, 50)))
>>> gs.odd[1]
1
np.int64(1)
>>> gs.odd[2]
3
np.int64(3)
>>> gs.odd[3]
5
np.int64(5)

"""

Expand All @@ -102,11 +102,11 @@

>>> gs = cudf.Series(list(range(0, 50)))
>>> gs.odd[1]
1
np.int64(1)
>>> gs.odd[2]
3
np.int64(3)
>>> gs.odd[3]
5
np.int64(5)

"""

Expand Down
2 changes: 1 addition & 1 deletion python/cudf/cudf/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1945,7 +1945,7 @@ def min(
b 7
dtype: int64
>>> min_series.min()
1
np.int64(1)

.. pandas-compat::
:meth:`pandas.DataFrame.min`, :meth:`pandas.Series.min`
Expand Down
10 changes: 5 additions & 5 deletions python/cudf/cudf/core/indexed_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,7 @@ def dot(self, other, reflect=False):
1 26
dtype: int64
>>> [1, 2, 3, 4] @ s
10
np.int64(10)
"""
# TODO: This function does not currently support nulls.
lhs = self.values
Expand Down Expand Up @@ -1644,7 +1644,7 @@ def median(
5 6
dtype: int64
>>> ser.median()
17.0
np.float64(17.0)
"""
if "overwrite_input" in kwargs:
raise ValueError(
Expand Down Expand Up @@ -1800,7 +1800,7 @@ def kurtosis(
>>> import cudf
>>> series = cudf.Series([1, 2, 3, 4])
>>> series.kurtosis()
-1.200000000000001
np.float64(-1.200000000000001)

**DataFrame**

Expand Down Expand Up @@ -2583,7 +2583,7 @@ def squeeze(self, axis: Literal["index", "columns", 0, 1, None] = None):
dtype: int64

>>> even_primes.squeeze()
2
np.int64(2)

Squeezing objects with more than one value in every axis does nothing:

Expand Down Expand Up @@ -2641,7 +2641,7 @@ def squeeze(self, axis: Literal["index", "columns", 0, 1, None] = None):
Squeezing all axes will project directly into a scalar:

>>> df_0a.squeeze()
1
np.int64(1)
"""
axes = (
range(self.ndim)
Expand Down
12 changes: 6 additions & 6 deletions python/cudf/cudf/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2886,7 +2886,7 @@ def cov(self, other, min_periods=None, ddof: int | None = None):
>>> ser1 = cudf.Series([0.9, 0.13, 0.62])
>>> ser2 = cudf.Series([0.12, 0.26, 0.51])
>>> ser1.cov(ser2)
-0.015750000000000004
np.float64(-0.015750000000000004)

.. pandas-compat::
:meth:`pandas.Series.cov`
Expand Down Expand Up @@ -3021,9 +3021,9 @@ def corr(self, other, method="pearson", min_periods=None):
>>> ser1 = cudf.Series([0.9, 0.13, 0.62])
>>> ser2 = cudf.Series([0.12, 0.26, 0.51])
>>> ser1.corr(ser2, method="pearson")
-0.20454263717316126
np.float64(-0.20454263717316126)
>>> ser1.corr(ser2, method="spearman")
-0.5
np.float64(-0.5)
"""

if method not in {"pearson", "spearman"}:
Expand Down Expand Up @@ -3069,9 +3069,9 @@ def autocorr(self, lag=1):
>>> import cudf
>>> s = cudf.Series([0.25, 0.5, 0.2, -0.05, 0.17])
>>> s.autocorr()
0.1438853844...
np.float64(0.1438853844...)
>>> s.autocorr(lag=2)
-0.9647548490...
np.float64(-0.9647548490...)
"""
return self.corr(self.shift(lag))

Expand Down Expand Up @@ -3417,7 +3417,7 @@ def quantile(
3 4
dtype: int64
>>> series.quantile(0.5)
2.5
np.float64(2.5)
>>> series.quantile([0.25, 0.5, 0.75])
0.25 1.75
0.50 2.50
Expand Down
8 changes: 1 addition & 7 deletions python/cudf/cudf/pandas/_wrappers/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import cupy
import cupy._core.flags
import numpy
from packaging import version
from numpy._core.multiarray import flagsobj as _numpy_flagsobj

from cudf.options import _env_get_bool

Expand Down Expand Up @@ -324,12 +324,6 @@ def _ndarray_fsproxy_fast_to_slow(self):
},
)

if version.parse(numpy.__version__) >= version.parse("2.0"):
# NumPy 2 introduced `_core` and gives warnings for access to `core`.
from numpy._core.multiarray import flagsobj as _numpy_flagsobj
else:
from numpy.core.multiarray import flagsobj as _numpy_flagsobj

# Mapping flags between slow and fast types
_ndarray_flags = make_intermediate_proxy_type(
"_ndarray_flags",
Expand Down
31 changes: 1 addition & 30 deletions python/cudf/cudf/tests/indexes/test_interval.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import numpy as np
import pandas as pd
import pyarrow as pa
import pytest
from packaging.version import parse

import cudf
from cudf.core.index import IntervalIndex, interval_range
Expand Down Expand Up @@ -121,14 +120,6 @@ def test_interval_range_periods_basic_dtype(start_t, end_t, periods_t):
gindex = cudf.interval_range(
start=start, end=end, periods=periods, closed="left"
)
if (
parse(np.__version__) < parse("2")
and pindex.dtype.subtype != gindex.dtype.subtype
):
# NEP 50 in numpy 2 changes pandas' subtype result to match cudf
pindex = pindex.astype(
pd.IntervalDtype(gindex.dtype.subtype, pindex.dtype.closed)
)

assert_eq(pindex, gindex)

Expand Down Expand Up @@ -172,16 +163,6 @@ def test_interval_range_periods_freq_end_dtype(periods_t, freq_t, end_t):
gindex = cudf.interval_range(
end=end, freq=freq, periods=periods, closed="left"
)
if (
parse(np.__version__) < parse("2")
and pindex.dtype.subtype != gindex.dtype.subtype
):
# NEP 50 in numpy 2 changes pandas' subtype result to match cudf
pindex = pindex.astype(
pd.IntervalDtype(
subtype=gindex.dtype.subtype, closed=pindex.dtype.closed
)
)
assert_eq(pindex, gindex)


Expand Down Expand Up @@ -211,16 +192,6 @@ def test_interval_range_periods_freq_start_dtype(periods_t, freq_t, start_t):
gindex = cudf.interval_range(
start=start, freq=freq, periods=periods, closed="left"
)
if (
parse(np.__version__) < parse("2")
and pindex.dtype.subtype != gindex.dtype.subtype
):
# NEP 50 in numpy 2 changes pandas' subtype result to match cudf
pindex = pindex.astype(
pd.IntervalDtype(
subtype=gindex.dtype.subtype, closed=pindex.dtype.closed
)
)
assert_eq(pindex, gindex)


Expand Down
Loading
Loading