Skip to content
Open
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
16 changes: 5 additions & 11 deletions src/narwhals/_compliant/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,20 +279,14 @@ def __or__(

def series(df: FrameT) -> Sequence[SeriesOrExprT]:
lhs_names, rhs_names = _eval_lhs_rhs(df, self, other)
rhs_set = frozenset(rhs_names)
return [
*(
x
for x, name in zip(self(df), lhs_names, strict=True)
if name not in rhs_set
),
*other(df),
]
by_name = dict(zip(lhs_names, self(df), strict=True))
by_name.update(zip(rhs_names, other(df), strict=True))
return [by_name[name] for name in df.columns if name in by_name]
Comment on lines +282 to +284

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

names already filters df.columns, so series can filter the frame's columns too, without evaluating self(df) and other(df). It's simpler and up to 2x faster on wide frames with chained unions (numbers in the summary):

Suggested change
by_name = dict(zip(lhs_names, self(df), strict=True))
by_name.update(zip(rhs_names, other(df), strict=True))
return [by_name[name] for name in df.columns if name in by_name]
selected = {*lhs_names, *rhs_names}
return [
ser
for ser, name in self.selectors._iter_columns_names(df)
if name in selected
]


def names(df: FrameT) -> Sequence[str]:
lhs_names, rhs_names = _eval_lhs_rhs(df, self, other)
rhs_set = frozenset(rhs_names)
return [*(x for x in lhs_names if x not in rhs_set), *rhs_names]
selected = {*lhs_names, *rhs_names}
return [name for name in df.columns if name in selected]

return self.selectors._selector.from_callables(series, names, context=self)
return self._to_expr() | other
Expand Down
3 changes: 2 additions & 1 deletion tests/selectors_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def test_datetime_no_tz(constructor: Constructor) -> None:
("selector", "expected"),
[
(ncs.numeric() | ncs.boolean(), ["a", "c", "d"]),
(ncs.boolean() | ncs.numeric(), ["a", "c", "d"]),
(ncs.numeric() & ncs.boolean(), []),
(ncs.numeric() & ncs.by_dtype(nw.Int64), ["a"]),
(ncs.numeric() | ncs.by_dtype(nw.Int64), ["a", "c"]),
Expand All @@ -295,7 +296,7 @@ def test_set_ops(
request.applymarker(pytest.mark.xfail)
df = nw.from_native(constructor(data))
result = df.select(selector).collect_schema().names()
assert sorted(result) == expected
assert result == expected


def test_subtract_expr(constructor: Constructor) -> None:
Expand Down