Skip to content

Commit 70ededb

Browse files
committed
feat(expr-ir): Add concat
Child of #2572 Compliant-level was already there - just need to get the tests in place now
1 parent 2c1e9af commit 70ededb

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

narwhals/_plan/dataframe.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from narwhals._plan._expansion import expand_selector_irs_names, prepare_projection
77
from narwhals._plan._guards import is_series
88
from narwhals._plan.common import ensure_seq_str, normalize_target_file, temp
9+
from narwhals._plan.compliant.dataframe import EagerDataFrame
10+
from narwhals._plan.compliant.namespace import EagerNamespace
911
from narwhals._plan.group_by import GroupBy, Grouped
1012
from narwhals._plan.options import ExplodeOptions, SortMultipleOptions
1113
from narwhals._plan.series import Series
@@ -48,11 +50,7 @@
4850

4951
from narwhals._native import NativeSeries
5052
from narwhals._plan.arrow.typing import NativeArrowDataFrame
51-
from narwhals._plan.compliant.dataframe import (
52-
CompliantDataFrame,
53-
CompliantFrame,
54-
EagerDataFrame,
55-
)
53+
from narwhals._plan.compliant.dataframe import CompliantFrame, EagerDataFrame
5654
from narwhals._plan.compliant.namespace import EagerNamespace
5755
from narwhals._plan.compliant.series import CompliantSeries
5856
from narwhals._typing import Arrow, _EagerAllowedImpl
@@ -224,7 +222,17 @@ def _dataframe_from_dict(
224222
class DataFrame(
225223
BaseFrame[NativeDataFrameT_co], Generic[NativeDataFrameT_co, NativeSeriesT]
226224
):
227-
_compliant: CompliantDataFrame[IncompleteCyclic, NativeDataFrameT_co, NativeSeriesT]
225+
_compliant: EagerDataFrame[IncompleteCyclic, NativeDataFrameT_co, NativeSeriesT]
226+
227+
def __narwhals_namespace__(
228+
self,
229+
) -> EagerNamespace[
230+
EagerDataFrame[Any, NativeDataFrameT_co, NativeSeriesT],
231+
CompliantSeries[NativeSeriesT],
232+
Any,
233+
Any,
234+
]:
235+
return self._compliant.__narwhals_namespace__()
228236

229237
@property
230238
def implementation(self) -> _EagerAllowedImpl:

narwhals/_plan/functions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from narwhals._plan import _guards, _parse, common, expressions as ir, selectors as cs
1010
from narwhals._plan._dispatch import get_dispatch_name
1111
from narwhals._plan.compliant import io as _io
12+
from narwhals._plan.compliant.typing import namespace
1213
from narwhals._plan.exceptions import (
1314
list_literal_error,
1415
unsupported_backend_operation_error,
@@ -35,6 +36,8 @@
3536
from narwhals.exceptions import ComputeError, InvalidOperationError
3637

3738
if TYPE_CHECKING:
39+
from collections.abc import Iterable
40+
3841
import pyarrow as pa
3942
from typing_extensions import TypeAlias
4043

@@ -50,6 +53,7 @@
5053
from narwhals._plan.expr import Expr
5154
from narwhals._plan.series import Series
5255
from narwhals._plan.typing import (
56+
DataFrameT,
5357
IntoExpr,
5458
IntoExprColumn,
5559
NativeDataFrameT,
@@ -61,6 +65,7 @@
6165
from narwhals.typing import (
6266
Backend,
6367
ClosedInterval,
68+
ConcatMethod,
6469
EagerAllowed,
6570
FileSource,
6671
IntoBackend,
@@ -78,6 +83,9 @@
7883
t.Any,
7984
]
8085
CompliantDF: TypeAlias = CompliantDataFrame[t.Any, NativeDataFrameT, NativeSeriesT]
86+
87+
T = t.TypeVar("T")
88+
8189
Incomplete: TypeAlias = t.Any
8290
_dtypes: Final = Version.MAIN.dtypes
8391

@@ -429,6 +437,27 @@ def linear_space(
429437
)
430438

431439

440+
def _ensure_same_frame(items: list[T], /) -> list[T]:
441+
item_0_tp = type(items[0])
442+
if builtins.all(isinstance(item, item_0_tp) for item in items):
443+
return items
444+
msg = f"The items to concatenate should either all be eager, or all lazy, got: {[type(item) for item in items]}"
445+
raise TypeError(msg)
446+
447+
448+
def concat(items: Iterable[DataFrameT], *, how: ConcatMethod = "vertical") -> DataFrameT:
449+
elems = list(items)
450+
if not elems:
451+
msg = "Cannot concat empty list"
452+
raise ValueError(msg)
453+
if how not in {"horizontal", "vertical", "diagonal"}: # pragma: no cover
454+
msg = "Only vertical, horizontal and diagonal concatenations are supported."
455+
raise NotImplementedError(msg)
456+
elems = _ensure_same_frame(elems)
457+
compliant = namespace(elems[0]).concat((df._compliant for df in elems), how=how)
458+
return elems[0]._with_compliant(compliant)
459+
460+
432461
@t.overload
433462
def read_csv(
434463
source: FileSource, *, backend: Arrow, **kwds: t.Any

0 commit comments

Comments
 (0)