Skip to content

Commit fe5c317

Browse files
committed
Merge branch 'main' into feat/with-row-index-by
2 parents 6eaf0ea + f17acfc commit fe5c317

File tree

9 files changed

+72
-205
lines changed

9 files changed

+72
-205
lines changed

narwhals/_arrow/utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,11 @@ def extract_native(
237237
If one of the two sides has a `_broadcast` flag, then extract the scalar
238238
underneath it so that PyArrow can do its own broadcasting.
239239
"""
240-
from narwhals._arrow.dataframe import ArrowDataFrame
241240
from narwhals._arrow.series import ArrowSeries
242241

243242
if rhs is None: # pragma: no cover
244243
return lhs.native, lit(None, type=lhs._type)
245244

246-
if isinstance(rhs, ArrowDataFrame):
247-
return NotImplemented
248-
249245
if isinstance(rhs, ArrowSeries):
250246
if lhs._broadcast and not rhs._broadcast:
251247
return lhs.native[0], rhs.native

narwhals/_dask/utils.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,19 @@ def narwhals_to_native_dtype(dtype: IntoDType, version: Version) -> Any: # noqa
144144
return "timedelta64[ns]"
145145
if isinstance_or_issubclass(dtype, dtypes.List): # pragma: no cover
146146
msg = "Converting to List dtype is not supported yet"
147-
return NotImplementedError(msg)
147+
raise NotImplementedError(msg)
148148
if isinstance_or_issubclass(dtype, dtypes.Struct): # pragma: no cover
149149
msg = "Converting to Struct dtype is not supported yet"
150-
return NotImplementedError(msg)
150+
raise NotImplementedError(msg)
151151
if isinstance_or_issubclass(dtype, dtypes.Array): # pragma: no cover
152152
msg = "Converting to Array dtype is not supported yet"
153-
return NotImplementedError(msg)
153+
raise NotImplementedError(msg)
154154
if isinstance_or_issubclass(dtype, dtypes.Time): # pragma: no cover
155155
msg = "Converting to Time dtype is not supported yet"
156-
return NotImplementedError(msg)
156+
raise NotImplementedError(msg)
157157
if isinstance_or_issubclass(dtype, dtypes.Binary): # pragma: no cover
158158
msg = "Converting to Binary dtype is not supported yet"
159-
return NotImplementedError(msg)
159+
raise NotImplementedError(msg)
160160

161161
msg = f"Unknown dtype: {dtype}" # pragma: no cover
162162
raise AssertionError(msg)

narwhals/_duckdb/namespace.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def _expr(self) -> type[DuckDBExpr]:
4949
def _lazyframe(self) -> type[DuckDBLazyFrame]:
5050
return DuckDBLazyFrame
5151

52-
def _with_elementwise(
52+
def _expr_from_elementwise(
5353
self, func: Callable[[Iterable[Expression]], Expression], *exprs: DuckDBExpr
5454
) -> DuckDBExpr:
5555
def call(df: DuckDBLazyFrame) -> list[Expression]:
@@ -119,31 +119,31 @@ def all_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
119119
def func(cols: Iterable[Expression]) -> Expression:
120120
return reduce(operator.and_, cols)
121121

122-
return self._with_elementwise(func, *exprs)
122+
return self._expr_from_elementwise(func, *exprs)
123123

124124
def any_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
125125
def func(cols: Iterable[Expression]) -> Expression:
126126
return reduce(operator.or_, cols)
127127

128-
return self._with_elementwise(func, *exprs)
128+
return self._expr_from_elementwise(func, *exprs)
129129

130130
def max_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
131131
def func(cols: Iterable[Expression]) -> Expression:
132132
return FunctionExpression("greatest", *cols)
133133

134-
return self._with_elementwise(func, *exprs)
134+
return self._expr_from_elementwise(func, *exprs)
135135

136136
def min_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
137137
def func(cols: Iterable[Expression]) -> Expression:
138138
return FunctionExpression("least", *cols)
139139

140-
return self._with_elementwise(func, *exprs)
140+
return self._expr_from_elementwise(func, *exprs)
141141

142142
def sum_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
143143
def func(cols: Iterable[Expression]) -> Expression:
144144
return reduce(operator.add, (CoalesceOperator(col, lit(0)) for col in cols))
145145

146-
return self._with_elementwise(func, *exprs)
146+
return self._expr_from_elementwise(func, *exprs)
147147

148148
def mean_horizontal(self, *exprs: DuckDBExpr) -> DuckDBExpr:
149149
def func(cols: Iterable[Expression]) -> Expression:
@@ -152,7 +152,7 @@ def func(cols: Iterable[Expression]) -> Expression:
152152
operator.add, (CoalesceOperator(col, lit(0)) for col in cols)
153153
) / reduce(operator.add, (col.isnotnull().cast(BIGINT) for col in cols))
154154

155-
return self._with_elementwise(func, *exprs)
155+
return self._expr_from_elementwise(func, *exprs)
156156

157157
def when(self, predicate: DuckDBExpr) -> DuckDBWhen:
158158
return DuckDBWhen.from_expr(predicate, context=self)

narwhals/_ibis/namespace.py

Lines changed: 37 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import operator
44
from functools import reduce
55
from itertools import chain
6-
from typing import TYPE_CHECKING, Any, cast
6+
from typing import TYPE_CHECKING, Any, Callable, cast
77

88
import ibis
99
import ibis.expr.types as ir
@@ -33,6 +33,21 @@ def __init__(self, *, backend_version: tuple[int, ...], version: Version) -> Non
3333
self._backend_version = backend_version
3434
self._version = version
3535

36+
def _expr_from_callable(
37+
self, func: Callable[[Iterable[ir.Value]], ir.Value], *exprs: IbisExpr
38+
) -> IbisExpr:
39+
def call(df: IbisLazyFrame) -> list[ir.Value]:
40+
cols = (col for _expr in exprs for col in _expr(df))
41+
return [func(cols)]
42+
43+
return self._expr(
44+
call=call,
45+
evaluate_output_names=combine_evaluate_output_names(*exprs),
46+
alias_output_names=combine_alias_output_names(*exprs),
47+
backend_version=self._backend_version,
48+
version=self._version,
49+
)
50+
3651
@property
3752
def selectors(self) -> IbisSelectorNamespace:
3853
return IbisSelectorNamespace.from_namespace(self)
@@ -86,94 +101,44 @@ def func(df: IbisLazyFrame) -> list[ir.Value]:
86101
)
87102

88103
def all_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
89-
def func(df: IbisLazyFrame) -> list[ir.Value]:
90-
cols = chain.from_iterable(expr(df) for expr in exprs)
91-
return [reduce(operator.and_, cols)]
104+
def func(cols: Iterable[ir.Value]) -> ir.Value:
105+
return reduce(operator.and_, cols)
92106

93-
return self._expr(
94-
call=func,
95-
evaluate_output_names=combine_evaluate_output_names(*exprs),
96-
alias_output_names=combine_alias_output_names(*exprs),
97-
backend_version=self._backend_version,
98-
version=self._version,
99-
)
107+
return self._expr_from_callable(func, *exprs)
100108

101109
def any_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
102-
def func(df: IbisLazyFrame) -> list[ir.Value]:
103-
cols = chain.from_iterable(expr(df) for expr in exprs)
104-
return [reduce(operator.or_, cols)]
110+
def func(cols: Iterable[ir.Value]) -> ir.Value:
111+
return reduce(operator.or_, cols)
105112

106-
return self._expr(
107-
call=func,
108-
evaluate_output_names=combine_evaluate_output_names(*exprs),
109-
alias_output_names=combine_alias_output_names(*exprs),
110-
backend_version=self._backend_version,
111-
version=self._version,
112-
)
113+
return self._expr_from_callable(func, *exprs)
113114

114115
def max_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
115-
def func(df: IbisLazyFrame) -> list[ir.Value]:
116-
cols = chain.from_iterable(expr(df) for expr in exprs)
117-
return [ibis.greatest(*cols)]
116+
def func(cols: Iterable[ir.Value]) -> ir.Value:
117+
return ibis.greatest(*cols)
118118

119-
return self._expr(
120-
call=func,
121-
evaluate_output_names=combine_evaluate_output_names(*exprs),
122-
alias_output_names=combine_alias_output_names(*exprs),
123-
backend_version=self._backend_version,
124-
version=self._version,
125-
)
119+
return self._expr_from_callable(func, *exprs)
126120

127121
def min_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
128-
def func(df: IbisLazyFrame) -> list[ir.Value]:
129-
cols = chain.from_iterable(expr(df) for expr in exprs)
130-
return [ibis.least(*cols)]
122+
def func(cols: Iterable[ir.Value]) -> ir.Value:
123+
return ibis.least(*cols)
131124

132-
return self._expr(
133-
call=func,
134-
evaluate_output_names=combine_evaluate_output_names(*exprs),
135-
alias_output_names=combine_alias_output_names(*exprs),
136-
backend_version=self._backend_version,
137-
version=self._version,
138-
)
125+
return self._expr_from_callable(func, *exprs)
139126

140127
def sum_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
141-
def func(df: IbisLazyFrame) -> list[ir.Value]:
142-
cols = [e.fill_null(lit(0)) for _expr in exprs for e in _expr(df)]
143-
return [reduce(operator.add, cols)]
128+
def func(cols: Iterable[ir.Value]) -> ir.Value:
129+
cols = (col.fill_null(lit(0)) for col in cols)
130+
return reduce(operator.add, cols)
144131

145-
return self._expr(
146-
call=func,
147-
evaluate_output_names=combine_evaluate_output_names(*exprs),
148-
alias_output_names=combine_alias_output_names(*exprs),
149-
backend_version=self._backend_version,
150-
version=self._version,
151-
)
132+
return self._expr_from_callable(func, *exprs)
152133

153134
def mean_horizontal(self, *exprs: IbisExpr) -> IbisExpr:
154-
def func(df: IbisLazyFrame) -> list[ir.Value]:
155-
expr = (
156-
cast("ir.NumericColumn", e.fill_null(lit(0)))
157-
for _expr in exprs
158-
for e in _expr(df)
159-
)
160-
non_null = (
161-
cast("ir.NumericColumn", e.isnull().ifelse(lit(0), lit(1)))
162-
for _expr in exprs
163-
for e in _expr(df)
135+
def func(cols: Iterable[ir.Value]) -> ir.Value:
136+
cols = list(cols)
137+
return reduce(operator.add, (col.fill_null(lit(0)) for col in cols)) / reduce(
138+
operator.add, (col.isnull().ifelse(lit(0), lit(1)) for col in cols)
164139
)
165140

166-
return [
167-
(reduce(lambda x, y: x + y, expr) / reduce(lambda x, y: x + y, non_null))
168-
]
169-
170-
return self._expr(
171-
call=func,
172-
evaluate_output_names=combine_evaluate_output_names(*exprs),
173-
alias_output_names=combine_alias_output_names(*exprs),
174-
backend_version=self._backend_version,
175-
version=self._version,
176-
)
141+
return self._expr_from_callable(func, *exprs)
177142

178143
@requires.backend_version((10, 0))
179144
def when(self, predicate: IbisExpr) -> IbisWhen:

narwhals/_pandas_like/series.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,7 @@ def _align_full_broadcast(cls, *series: Self) -> Sequence[Self]:
229229
for s in series:
230230
if s._broadcast:
231231
native = Series(
232-
np.repeat(s.native.iloc[0], max_length),
233-
index=idx,
234-
name=s.name,
235-
dtype=s.native.dtype,
232+
s.native.iloc[0], index=idx, name=s.name, dtype=s.native.dtype
236233
)
237234
compliant = s._with_native(native)
238235
elif s.native.index is not idx:

narwhals/_pandas_like/utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,10 @@ def align_and_extract_native(
9393
If the comparison isn't supported, return `NotImplemented` so that the
9494
"right-hand-side" operation (e.g. `__radd__`) can be tried.
9595
"""
96-
from narwhals._pandas_like.dataframe import PandasLikeDataFrame
9796
from narwhals._pandas_like.series import PandasLikeSeries
9897

9998
lhs_index = lhs.native.index
10099

101-
if isinstance(rhs, PandasLikeDataFrame):
102-
return NotImplemented
103-
104100
if lhs._broadcast and isinstance(rhs, PandasLikeSeries) and not rhs._broadcast:
105101
return lhs.native.iloc[0], rhs.native
106102

narwhals/_spark_like/namespace.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def _native_dtypes(self): # type: ignore[no-untyped-def] # noqa: ANN202
7373
else:
7474
return import_native_dtypes(self._implementation)
7575

76-
def _with_elementwise(
76+
def _expr_from_callable(
7777
self, func: Callable[[Iterable[Column]], Column], *exprs: SparkLikeExpr
7878
) -> SparkLikeExpr:
7979
def call(df: SparkLikeLazyFrame) -> list[Column]:
@@ -135,33 +135,33 @@ def all_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
135135
def func(cols: Iterable[Column]) -> Column:
136136
return reduce(operator.and_, cols)
137137

138-
return self._with_elementwise(func, *exprs)
138+
return self._expr_from_callable(func, *exprs)
139139

140140
def any_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
141141
def func(cols: Iterable[Column]) -> Column:
142142
return reduce(operator.or_, cols)
143143

144-
return self._with_elementwise(func, *exprs)
144+
return self._expr_from_callable(func, *exprs)
145145

146146
def max_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
147147
def func(cols: Iterable[Column]) -> Column:
148148
return self._F.greatest(*cols)
149149

150-
return self._with_elementwise(func, *exprs)
150+
return self._expr_from_callable(func, *exprs)
151151

152152
def min_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
153153
def func(cols: Iterable[Column]) -> Column:
154154
return self._F.least(*cols)
155155

156-
return self._with_elementwise(func, *exprs)
156+
return self._expr_from_callable(func, *exprs)
157157

158158
def sum_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
159159
def func(cols: Iterable[Column]) -> Column:
160160
return reduce(
161161
operator.add, (self._F.coalesce(col, self._F.lit(0)) for col in cols)
162162
)
163163

164-
return self._with_elementwise(func, *exprs)
164+
return self._expr_from_callable(func, *exprs)
165165

166166
def mean_horizontal(self, *exprs: SparkLikeExpr) -> SparkLikeExpr:
167167
def func(cols: Iterable[Column]) -> Column:
@@ -182,7 +182,7 @@ def func(cols: Iterable[Column]) -> Column:
182182
),
183183
)
184184

185-
return self._with_elementwise(func, *exprs)
185+
return self._expr_from_callable(func, *exprs)
186186

187187
def concat(
188188
self, items: Iterable[SparkLikeLazyFrame], *, how: ConcatMethod

narwhals/_utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,7 +1770,8 @@ def __get__(
17701770
# NOTE: Prefer not exposing the actual class we're defining in
17711771
# `_implementation` may not be available everywhere
17721772
who = getattr(instance, "_implementation", self._name_owner)
1773-
raise _not_implemented_error(self._name, who)
1773+
_raise_not_implemented_error(self._name, who)
1774+
return None # pragma: no cover
17741775

17751776
def __call__(self, *args: Any, **kwds: Any) -> Any:
17761777
# NOTE: Purely to duck-type as assignable to **any** instance method
@@ -1793,13 +1794,13 @@ def deprecated(cls, message: LiteralString, /) -> Self:
17931794
return deprecated(message)(obj)
17941795

17951796

1796-
def _not_implemented_error(what: str, who: str, /) -> NotImplementedError:
1797+
def _raise_not_implemented_error(what: str, who: str, /) -> NotImplementedError:
17971798
msg = (
17981799
f"{what!r} is not implemented for: {who!r}.\n\n"
17991800
"If you would like to see this functionality in `narwhals`, "
18001801
"please open an issue at: https://github.com/narwhals-dev/narwhals/issues"
18011802
)
1802-
return NotImplementedError(msg)
1803+
raise NotImplementedError(msg)
18031804

18041805

18051806
class requires: # noqa: N801

0 commit comments

Comments
 (0)