Skip to content

Commit ec26810

Browse files
solegalliclaude
andauthored
Return narwhals frame from check_X / check_X_y (#1019)
check_X now returns the validated narwhals DataFrame instead of converting back to the native frame, and check_X_y propagates that. The pandas index-consistency check in check_X_y is updated to reach the native frame via X.to_native(), and the now-unused IntoDataFrameT import / type hints are replaced with IntoDataFrame. Tests in test_dataframe_checks.py are updated for the new return contract: they assert the result is a narwhals.DataFrame and compare X.to_native() against the original. Note: downstream transformers still expect a native frame from these helpers; they will be adapted on their own narwhals-* branches. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent fd4f8ee commit ec26810

2 files changed

Lines changed: 24 additions & 18 deletions

File tree

feature_engine/dataframe_checks.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
import narwhals.dependencies as nwd
99
import narwhals.selectors as nws
1010
import numpy as np
11-
from narwhals.typing import IntoDataFrame, IntoDataFrameT, IntoSeries
11+
from narwhals.typing import IntoDataFrame, IntoSeries
1212
from sklearn.utils.validation import _check_y, check_consistent_length, column_or_1d
1313

1414

15-
def check_X(X: IntoDataFrameT) -> IntoDataFrameT:
15+
def check_X(X: IntoDataFrame) -> IntoDataFrame:
1616
"""
1717
Checks that X is a dataframe from any library supported by narwhals (for example
1818
pandas, polars, modin, cuDF, or PyArrow).
@@ -34,8 +34,8 @@ def check_X(X: IntoDataFrameT) -> IntoDataFrameT:
3434
3535
Returns
3636
-------
37-
X : dataframe.
38-
The validated dataframe in its native format.
37+
X : narwhals dataframe.
38+
The validated dataframe in narwhals format.
3939
"""
4040
if nwd.is_into_dataframe(X):
4141
# from_native() raises narwhals.exceptions.DuplicateError, a ValueError
@@ -53,7 +53,7 @@ def check_X(X: IntoDataFrameT) -> IntoDataFrameT:
5353
f"(e.g. pandas, polars, PyArrow). Got {type(X)} instead."
5454
)
5555

56-
return nw_X.to_native()
56+
return nw_X
5757

5858

5959
def check_y(
@@ -121,10 +121,10 @@ def check_y(
121121

122122

123123
def check_X_y(
124-
X: IntoDataFrameT,
124+
X: IntoDataFrame,
125125
y: Union[IntoSeries, IntoDataFrame, np.generic, np.ndarray, List],
126126
y_numeric: bool = False,
127-
) -> Tuple[IntoDataFrameT, Union[IntoSeries, IntoDataFrame, np.ndarray]]:
127+
) -> Tuple[IntoDataFrame, Union[IntoSeries, IntoDataFrame, np.ndarray]]:
128128
"""
129129
Ensures X and y are compatible dataframe/array-like objects with a consistent
130130
number of rows. If both are pandas objects, checks that their indexes match.
@@ -156,16 +156,16 @@ def check_X_y(
156156
157157
Returns
158158
-------
159-
X: dataframe
159+
X: narwhals dataframe
160160
y: Series, DataFrame, or numpy array
161161
"""
162162
X = check_X(X)
163163
y = check_y(y, y_numeric=y_numeric)
164164
check_consistent_length(X, y)
165165

166-
if nwd.is_pandas_dataframe(X):
166+
if X.implementation.is_pandas():
167167
if nwd.is_pandas_series(y) or nwd.is_pandas_dataframe(y):
168-
if not X.index.equals(y.index):
168+
if not X.to_native().index.equals(y.index):
169169
raise ValueError("The indexes of X and y do not match.")
170170

171171
return X, y

tests/test_dataframe_checks.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import narwhals as nw
12
import numpy as np
23
import pandas as pd
34
import polars as pl
@@ -28,8 +29,8 @@
2829
def test_check_X_returns_df_unchanged(make_df, assert_equal_fn):
2930
df = make_df({"a": [1, 2, 3], "b": [4.0, 5.0, 6.0]})
3031
X = check_X(df)
31-
assert isinstance(X, type(df))
32-
assert_equal_fn(X, df)
32+
assert isinstance(X, nw.DataFrame)
33+
assert_equal_fn(X.to_native(), df)
3334

3435

3536
@pytest.mark.parametrize(
@@ -45,7 +46,9 @@ def test_check_X_returns_df_with_mixed_dtypes(make_df, assert_equal_fn):
4546
"dob": pd.date_range("2020-02-24", periods=4, freq="min"),
4647
}
4748
df = make_df(data)
48-
assert_equal_fn(check_X(df), df)
49+
X = check_X(df)
50+
assert isinstance(X, nw.DataFrame)
51+
assert_equal_fn(X.to_native(), df)
4952

5053

5154
@pytest.mark.parametrize(
@@ -265,8 +268,8 @@ def test_check_X_y_returns_df_and_series_unchanged(
265268
df = make_df({"a": [1, 2, 3], "b": [4, 5, 6]})
266269
s = make_series([0, 1, 2])
267270
X, y = check_X_y(df, s)
268-
assert isinstance(X, type(df)) and isinstance(y, type(s))
269-
assert_frame_fn(X, df)
271+
assert isinstance(X, nw.DataFrame) and isinstance(y, type(s))
272+
assert_frame_fn(X.to_native(), df)
270273
assert_series_fn(y, s)
271274

272275

@@ -278,7 +281,8 @@ def test_check_X_y_returns_df_and_multioutput_y_unchanged(make_df, assert_frame_
278281
df = make_df({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]})
279282
d = make_df({"t1": [1, 2, 3, 4], "t2": [5, 6, 7, 8]})
280283
X, y = check_X_y(df, d)
281-
assert_frame_fn(X, df)
284+
assert isinstance(X, nw.DataFrame)
285+
assert_frame_fn(X.to_native(), df)
282286
assert_frame_fn(y, d)
283287

284288

@@ -299,7 +303,8 @@ def test_check_X_y_with_array_like_y_returns_check_y_output(
299303
):
300304
df = make_df({"a": [1, 2, 3], "b": [4, 5, 6]})
301305
X, y_out = check_X_y(df, y)
302-
assert_frame_fn(X, df)
306+
assert isinstance(X, nw.DataFrame)
307+
assert_frame_fn(X.to_native(), df)
303308
np.testing.assert_array_equal(y_out, check_y(y))
304309

305310

@@ -308,7 +313,8 @@ def test_check_X_y_returns_pandas_with_non_typical_index():
308313
df = pd.DataFrame({"0": [1, 2, 3, 4], "1": [5, 6, 7, 8]}, index=[22, 99, 101, 212])
309314
s = pd.Series([1, 2, 3, 4], index=[22, 99, 101, 212])
310315
x, y = check_X_y(df, s)
311-
assert_frame_equal(df, x)
316+
assert isinstance(x, nw.DataFrame)
317+
assert_frame_equal(df, x.to_native())
312318
assert_series_equal(s, y)
313319

314320

0 commit comments

Comments
 (0)