|
9 | 9 | from narwhals._plan import _guards, _parse, common, expressions as ir, selectors as cs |
10 | 10 | from narwhals._plan._dispatch import get_dispatch_name |
11 | 11 | from narwhals._plan.compliant import io as _io |
| 12 | +from narwhals._plan.compliant.typing import namespace |
12 | 13 | from narwhals._plan.exceptions import ( |
13 | 14 | list_literal_error, |
14 | 15 | unsupported_backend_operation_error, |
|
35 | 36 | from narwhals.exceptions import ComputeError, InvalidOperationError |
36 | 37 |
|
37 | 38 | if TYPE_CHECKING: |
| 39 | + from collections.abc import Iterable |
| 40 | + |
38 | 41 | import pyarrow as pa |
39 | 42 | from typing_extensions import TypeAlias |
40 | 43 |
|
|
50 | 53 | from narwhals._plan.expr import Expr |
51 | 54 | from narwhals._plan.series import Series |
52 | 55 | from narwhals._plan.typing import ( |
| 56 | + DataFrameT, |
53 | 57 | IntoExpr, |
54 | 58 | IntoExprColumn, |
55 | 59 | NativeDataFrameT, |
|
61 | 65 | from narwhals.typing import ( |
62 | 66 | Backend, |
63 | 67 | ClosedInterval, |
| 68 | + ConcatMethod, |
64 | 69 | EagerAllowed, |
65 | 70 | FileSource, |
66 | 71 | IntoBackend, |
|
78 | 83 | t.Any, |
79 | 84 | ] |
80 | 85 | CompliantDF: TypeAlias = CompliantDataFrame[t.Any, NativeDataFrameT, NativeSeriesT] |
| 86 | + |
| 87 | + T = t.TypeVar("T") |
| 88 | + |
81 | 89 | Incomplete: TypeAlias = t.Any |
82 | 90 | _dtypes: Final = Version.MAIN.dtypes |
83 | 91 |
|
@@ -429,6 +437,27 @@ def linear_space( |
429 | 437 | ) |
430 | 438 |
|
431 | 439 |
|
| 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 | + |
432 | 461 | @t.overload |
433 | 462 | def read_csv( |
434 | 463 | source: FileSource, *, backend: Arrow, **kwds: t.Any |
|
0 commit comments