Skip to content

Commit 07c2624

Browse files
authored
refactor: Use .from_iterable() in new_series (#2302)
1 parent 1a5e53a commit 07c2624

File tree

8 files changed

+71
-89
lines changed

8 files changed

+71
-89
lines changed

narwhals/_arrow/series.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,20 @@ def _from_native_series(
147147

148148
@classmethod
149149
def from_iterable(
150-
cls, data: Iterable[Any], *, context: _FullContext, name: str = ""
150+
cls,
151+
data: Iterable[Any],
152+
*,
153+
context: _FullContext,
154+
name: str = "",
155+
dtype: DType | type[DType] | None = None,
151156
) -> Self:
157+
version = context._version
158+
dtype_pa = narwhals_to_native_dtype(dtype, version) if dtype else None
152159
return cls(
153-
chunked_array([data]),
160+
chunked_array([data], dtype_pa),
154161
name=name,
155162
backend_version=context._backend_version,
156-
version=context._version,
163+
version=version,
157164
)
158165

159166
def _from_scalar(self, value: Any) -> Self:

narwhals/_arrow/utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
# NOTE: stubs don't allow for `ChunkedArray[StructArray]`
3636
# Intended to represent the `.chunks` property storing `list[pa.StructArray]`
3737
ChunkedArrayStructArray: TypeAlias = ArrowChunkedArray
38+
ArrayAny: TypeAlias = "ArrowArray | ArrowChunkedArray"
3839

3940
_T = TypeVar("_T")
4041

@@ -74,12 +75,12 @@ def extract_py_scalar(value: Any, /) -> Any:
7475

7576

7677
def chunked_array(
77-
arr: ArrowArray | list[Iterable[pa.Scalar[Any]]] | ArrowChunkedArray,
78+
arr: ArrayAny | list[Iterable[Any]], dtype: pa.DataType | None = None, /
7879
) -> ArrowChunkedArray:
7980
if isinstance(arr, pa.ChunkedArray):
8081
return arr
8182
if isinstance(arr, list):
82-
return pa.chunked_array(cast("Any", arr))
83+
return pa.chunked_array(arr, dtype)
8384
else:
8485
return pa.chunked_array([arr], arr.type)
8586

narwhals/_compliant/series.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ def _to_expr(self) -> CompliantExpr[Any, Self]: ...
7070
def from_numpy(cls, data: Into1DArray, /, *, context: _FullContext) -> Self: ...
7171
@classmethod
7272
def from_iterable(
73-
cls, data: Iterable[Any], /, *, context: _FullContext, name: str = ""
73+
cls,
74+
data: Iterable[Any],
75+
/,
76+
*,
77+
context: _FullContext,
78+
name: str = "",
79+
dtype: DType | type[DType] | None = None,
7480
) -> Self: ...
7581
def _change_version(self, version: Version) -> Self: ...
7682

narwhals/_pandas_like/group_by.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from narwhals._compliant import EagerGroupBy
1313
from narwhals._expression_parsing import evaluate_output_names_and_aliases
1414
from narwhals._pandas_like.utils import horizontal_concat
15-
from narwhals._pandas_like.utils import native_series_from_iterable
1615
from narwhals._pandas_like.utils import select_columns_by_name
1716
from narwhals._pandas_like.utils import set_columns
1817
from narwhals.utils import find_stacklevel
@@ -283,12 +282,8 @@ def func(df: Any) -> Any:
283282
for result_keys in results_keys:
284283
out_group.append(result_keys.native.iloc[0])
285284
out_names.append(result_keys.name)
286-
return native_series_from_iterable(
287-
out_group,
288-
index=out_names,
289-
name="",
290-
implementation=implementation,
291-
)
285+
ns = self.compliant.__narwhals_namespace__()
286+
return ns._series.from_iterable(out_group, index=out_names, context=ns).native
292287

293288
if implementation.is_pandas() and backend_version >= (2, 2):
294289
result_complex = self._grouped.apply(func, include_groups=False)

narwhals/_pandas_like/series.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from narwhals._pandas_like.utils import align_and_extract_native
2222
from narwhals._pandas_like.utils import get_dtype_backend
2323
from narwhals._pandas_like.utils import narwhals_to_native_dtype
24-
from narwhals._pandas_like.utils import native_series_from_iterable
2524
from narwhals._pandas_like.utils import native_to_narwhals_dtype
2625
from narwhals._pandas_like.utils import object_native_to_narwhals_dtype
2726
from narwhals._pandas_like.utils import rename
@@ -178,18 +177,28 @@ def from_iterable(
178177
*,
179178
context: _FullContext,
180179
name: str = "",
180+
dtype: DType | type[DType] | None = None,
181181
index: Any = None,
182182
) -> Self:
183+
implementation = context._implementation
184+
backend_version = context._backend_version
185+
version = context._version
186+
ns = implementation.to_native_namespace()
187+
kwds: dict[str, Any] = {}
188+
if dtype:
189+
kwds["dtype"] = narwhals_to_native_dtype(
190+
dtype, None, implementation, backend_version, version
191+
)
192+
else:
193+
if implementation.is_pandas():
194+
kwds["copy"] = False
195+
if index is not None and len(index):
196+
kwds["index"] = index
183197
return cls(
184-
native_series_from_iterable(
185-
data,
186-
name=name,
187-
index=[] if index is None else index,
188-
implementation=context._implementation,
189-
),
190-
implementation=context._implementation,
191-
backend_version=context._backend_version,
192-
version=context._version,
198+
ns.Series(data, name=name, **kwds),
199+
implementation=implementation,
200+
backend_version=backend_version,
201+
version=version,
193202
)
194203

195204
@classmethod

narwhals/_pandas_like/utils.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from contextlib import suppress
77
from typing import TYPE_CHECKING
88
from typing import Any
9-
from typing import Iterable
109
from typing import Sized
1110
from typing import TypeVar
1211
from typing import cast
@@ -219,26 +218,6 @@ def diagonal_concat(
219218
raise TypeError(msg)
220219

221220

222-
def native_series_from_iterable(
223-
data: Iterable[Any],
224-
name: str,
225-
index: Any,
226-
implementation: Implementation,
227-
) -> Any:
228-
"""Return native series."""
229-
if implementation in PANDAS_LIKE_IMPLEMENTATION:
230-
extra_kwargs = {"copy": False} if implementation is Implementation.PANDAS else {}
231-
if len(index) == 0:
232-
index = None
233-
return implementation.to_native_namespace().Series(
234-
data, name=name, index=index, **extra_kwargs
235-
)
236-
237-
else: # pragma: no cover
238-
msg = f"Expected pandas-like implementation ({PANDAS_LIKE_IMPLEMENTATION}), found {implementation}"
239-
raise TypeError(msg)
240-
241-
242221
def set_index(
243222
obj: T,
244223
index: Any,

narwhals/_polars/series.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import TYPE_CHECKING
44
from typing import Any
5+
from typing import Iterable
56
from typing import Sequence
67
from typing import cast
78
from typing import overload
@@ -74,6 +75,28 @@ def _change_version(self: Self, version: Version) -> Self:
7475
self.native, backend_version=self._backend_version, version=version
7576
)
7677

78+
@classmethod
79+
def from_iterable(
80+
cls,
81+
data: Iterable[Any],
82+
*,
83+
context: _FullContext,
84+
name: str = "",
85+
dtype: DType | type[DType] | None = None,
86+
) -> Self:
87+
version = context._version
88+
backend_version = context._backend_version
89+
dtype_pl = (
90+
narwhals_to_native_dtype(dtype, version, backend_version) if dtype else None
91+
)
92+
# NOTE: `Iterable` is fine, annotation is overly narrow
93+
# https://github.com/pola-rs/polars/blob/82d57a4ee41f87c11ca1b1af15488459727efdd7/py-polars/polars/series/series.py#L332-L333
94+
return cls(
95+
pl.Series(name=name, values=cast("Sequence[Any]", data), dtype=dtype_pl),
96+
backend_version=backend_version,
97+
version=version,
98+
)
99+
77100
@classmethod
78101
def from_numpy(cls, data: Into1DArray, /, *, context: _FullContext) -> Self:
79102
return cls(

narwhals/functions.py

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
if TYPE_CHECKING:
4545
from types import ModuleType
4646

47-
import pyarrow as pa
4847
from typing_extensions import Self
4948
from typing_extensions import TypeAlias
5049
from typing_extensions import TypeIs
@@ -260,58 +259,21 @@ def _new_series_impl(
260259
version: Version,
261260
) -> Series[Any]:
262261
implementation = Implementation.from_backend(backend)
263-
native_namespace = implementation.to_native_namespace()
264-
265-
if implementation is Implementation.POLARS:
266-
if dtype:
267-
from narwhals._polars.utils import (
268-
narwhals_to_native_dtype as polars_narwhals_to_native_dtype,
269-
)
270-
271-
backend_version = parse_version(native_namespace.__version__)
272-
dtype_pl = polars_narwhals_to_native_dtype(
273-
dtype, version=version, backend_version=backend_version
274-
)
275-
else:
276-
dtype_pl = None
277-
278-
native_series = native_namespace.Series(name=name, values=values, dtype=dtype_pl)
279-
elif implementation.is_pandas_like():
280-
if dtype:
281-
from narwhals._pandas_like.utils import (
282-
narwhals_to_native_dtype as pandas_like_narwhals_to_native_dtype,
283-
)
284-
285-
backend_version = parse_version(native_namespace)
286-
pd_dtype = pandas_like_narwhals_to_native_dtype(
287-
dtype, None, implementation, backend_version, version
288-
)
289-
native_series = native_namespace.Series(values, name=name, dtype=pd_dtype)
290-
else:
291-
native_series = native_namespace.Series(values, name=name)
292-
293-
elif implementation is Implementation.PYARROW:
294-
pa_dtype: pa.DataType | None = None
295-
if dtype:
296-
from narwhals._arrow.utils import (
297-
narwhals_to_native_dtype as arrow_narwhals_to_native_dtype,
298-
)
299-
300-
pa_dtype = arrow_narwhals_to_native_dtype(dtype, version=version)
301-
native_series = native_namespace.chunked_array([values], type=pa_dtype)
302-
262+
if is_eager_allowed(implementation):
263+
ns = _into_compliant_namespace(implementation, version)
264+
series = ns._series.from_iterable(values, name=name, context=ns, dtype=dtype)
265+
return from_native(series, series_only=True)
303266
elif implementation is Implementation.DASK: # pragma: no cover
304267
msg = "Dask support in Narwhals is lazy-only, so `new_series` is not supported"
305268
raise NotImplementedError(msg)
306269
else: # pragma: no cover
270+
native_namespace = implementation.to_native_namespace()
307271
try:
308-
# implementation is UNKNOWN, Narwhals extension using this feature should
309-
# implement `from_dict` function in the top-level namespace.
310272
native_series = native_namespace.new_series(name, values, dtype)
273+
return from_native(native_series, series_only=True).alias(name)
311274
except AttributeError as e:
312-
msg = "Unknown namespace is expected to implement `Series` constructor."
275+
msg = "Unknown namespace is expected to implement `new_series` constructor."
313276
raise AttributeError(msg) from e
314-
return from_native(native_series, series_only=True).alias(name)
315277

316278

317279
@deprecate_native_namespace(warn_version="1.26.0")

0 commit comments

Comments
 (0)