Skip to content
Merged
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
13 changes: 10 additions & 3 deletions narwhals/_arrow/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,20 @@ def _from_native_series(

@classmethod
def from_iterable(
cls, data: Iterable[Any], *, context: _FullContext, name: str = ""
cls,
data: Iterable[Any],
*,
context: _FullContext,
name: str = "",
dtype: DType | type[DType] | None = None,
) -> Self:
version = context._version
dtype_pa = narwhals_to_native_dtype(dtype, version) if dtype else None
return cls(
chunked_array([data]),
chunked_array([data], dtype_pa),
name=name,
backend_version=context._backend_version,
version=context._version,
version=version,
)

def _from_scalar(self, value: Any) -> Self:
Expand Down
5 changes: 3 additions & 2 deletions narwhals/_arrow/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# NOTE: stubs don't allow for `ChunkedArray[StructArray]`
# Intended to represent the `.chunks` property storing `list[pa.StructArray]`
ChunkedArrayStructArray: TypeAlias = ArrowChunkedArray
ArrayAny: TypeAlias = "ArrowArray | ArrowChunkedArray"

_T = TypeVar("_T")

Expand Down Expand Up @@ -73,12 +74,12 @@ def extract_py_scalar(value: Any, /) -> Any:


def chunked_array(
arr: ArrowArray | list[Iterable[pa.Scalar[Any]]] | ArrowChunkedArray,
arr: ArrayAny | list[Iterable[Any]], dtype: pa.DataType | None = None, /
) -> ArrowChunkedArray:
if isinstance(arr, pa.ChunkedArray):
return arr
if isinstance(arr, list):
return pa.chunked_array(cast("Any", arr))
return pa.chunked_array(arr, dtype)
else:
return pa.chunked_array([arr], arr.type)

Expand Down
8 changes: 7 additions & 1 deletion narwhals/_compliant/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,13 @@ def _to_expr(self) -> CompliantExpr[Any, Self]: ...
def from_numpy(cls, data: Into1DArray, /, *, context: _FullContext) -> Self: ...
@classmethod
def from_iterable(
cls, data: Iterable[Any], /, *, context: _FullContext, name: str = ""
cls,
data: Iterable[Any],
/,
*,
context: _FullContext,
name: str = "",
dtype: DType | type[DType] | None = None,
) -> Self: ...
def _change_version(self, version: Version) -> Self: ...

Expand Down
9 changes: 2 additions & 7 deletions narwhals/_pandas_like/group_by.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from narwhals._compliant import EagerGroupBy
from narwhals._expression_parsing import evaluate_output_names_and_aliases
from narwhals._pandas_like.utils import horizontal_concat
from narwhals._pandas_like.utils import native_series_from_iterable
from narwhals._pandas_like.utils import select_columns_by_name
from narwhals._pandas_like.utils import set_columns
from narwhals.utils import find_stacklevel
Expand Down Expand Up @@ -283,12 +282,8 @@ def func(df: Any) -> Any:
for result_keys in results_keys:
out_group.append(result_keys.native.iloc[0])
out_names.append(result_keys.name)
return native_series_from_iterable(
out_group,
index=out_names,
name="",
implementation=implementation,
)
ns = self.compliant.__narwhals_namespace__()
return ns._series.from_iterable(out_group, index=out_names, context=ns).native

if implementation.is_pandas() and backend_version >= (2, 2):
result_complex = self._grouped.apply(func, include_groups=False)
Expand Down
29 changes: 19 additions & 10 deletions narwhals/_pandas_like/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from narwhals._pandas_like.utils import align_and_extract_native
from narwhals._pandas_like.utils import get_dtype_backend
from narwhals._pandas_like.utils import narwhals_to_native_dtype
from narwhals._pandas_like.utils import native_series_from_iterable
from narwhals._pandas_like.utils import native_to_narwhals_dtype
from narwhals._pandas_like.utils import object_native_to_narwhals_dtype
from narwhals._pandas_like.utils import rename
Expand Down Expand Up @@ -173,18 +172,28 @@ def from_iterable(
*,
context: _FullContext,
name: str = "",
dtype: DType | type[DType] | None = None,
index: Any = None,
) -> Self:
implementation = context._implementation
backend_version = context._backend_version
version = context._version
ns = implementation.to_native_namespace()
kwds: dict[str, Any] = {}
if dtype:
kwds["dtype"] = narwhals_to_native_dtype(
dtype, None, implementation, backend_version, version
)
else:
if implementation.is_pandas():
kwds["copy"] = False
if index is not None and len(index):
kwds["index"] = index
return cls(
native_series_from_iterable(
data,
name=name,
index=[] if index is None else index,
implementation=context._implementation,
),
implementation=context._implementation,
backend_version=context._backend_version,
version=context._version,
ns.Series(data, name=name, **kwds),
implementation=implementation,
backend_version=backend_version,
version=version,
)

@classmethod
Expand Down
21 changes: 0 additions & 21 deletions narwhals/_pandas_like/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from contextlib import suppress
from typing import TYPE_CHECKING
from typing import Any
from typing import Iterable
from typing import Sized
from typing import TypeVar
from typing import cast
Expand Down Expand Up @@ -218,26 +217,6 @@ def diagonal_concat(
raise TypeError(msg)


def native_series_from_iterable(
data: Iterable[Any],
name: str,
index: Any,
implementation: Implementation,
) -> Any:
"""Return native series."""
if implementation in PANDAS_LIKE_IMPLEMENTATION:
extra_kwargs = {"copy": False} if implementation is Implementation.PANDAS else {}
if len(index) == 0:
index = None
return implementation.to_native_namespace().Series(
data, name=name, index=index, **extra_kwargs
)

else: # pragma: no cover
msg = f"Expected pandas-like implementation ({PANDAS_LIKE_IMPLEMENTATION}), found {implementation}"
raise TypeError(msg)


def set_index(
obj: T,
index: Any,
Expand Down
23 changes: 23 additions & 0 deletions narwhals/_polars/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import TYPE_CHECKING
from typing import Any
from typing import Iterable
from typing import Sequence
from typing import cast
from typing import overload
Expand Down Expand Up @@ -74,6 +75,28 @@ def _change_version(self: Self, version: Version) -> Self:
self.native, backend_version=self._backend_version, version=version
)

@classmethod
def from_iterable(
cls,
data: Iterable[Any],
*,
context: _FullContext,
name: str = "",
dtype: DType | type[DType] | None = None,
) -> Self:
version = context._version
backend_version = context._backend_version
dtype_pl = (
narwhals_to_native_dtype(dtype, version, backend_version) if dtype else None
)
# NOTE: `Iterable` is fine, annotation is overly narrow
# https://github.com/pola-rs/polars/blob/82d57a4ee41f87c11ca1b1af15488459727efdd7/py-polars/polars/series/series.py#L332-L333
return cls(
pl.Series(name=name, values=cast("Sequence[Any]", data), dtype=dtype_pl),
backend_version=backend_version,
version=version,
)

@classmethod
def from_numpy(cls, data: Into1DArray, /, *, context: _FullContext) -> Self:
return cls(
Expand Down
52 changes: 7 additions & 45 deletions narwhals/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
if TYPE_CHECKING:
from types import ModuleType

import pyarrow as pa
from typing_extensions import Self
from typing_extensions import TypeAlias
from typing_extensions import TypeIs
Expand Down Expand Up @@ -260,58 +259,21 @@ def _new_series_impl(
version: Version,
) -> Series[Any]:
implementation = Implementation.from_backend(backend)
native_namespace = implementation.to_native_namespace()

if implementation is Implementation.POLARS:
if dtype:
from narwhals._polars.utils import (
narwhals_to_native_dtype as polars_narwhals_to_native_dtype,
)

backend_version = parse_version(native_namespace.__version__)
dtype_pl = polars_narwhals_to_native_dtype(
dtype, version=version, backend_version=backend_version
)
else:
dtype_pl = None

native_series = native_namespace.Series(name=name, values=values, dtype=dtype_pl)
elif implementation.is_pandas_like():
if dtype:
from narwhals._pandas_like.utils import (
narwhals_to_native_dtype as pandas_like_narwhals_to_native_dtype,
)

backend_version = parse_version(native_namespace)
pd_dtype = pandas_like_narwhals_to_native_dtype(
dtype, None, implementation, backend_version, version
)
native_series = native_namespace.Series(values, name=name, dtype=pd_dtype)
else:
native_series = native_namespace.Series(values, name=name)

elif implementation is Implementation.PYARROW:
pa_dtype: pa.DataType | None = None
if dtype:
from narwhals._arrow.utils import (
narwhals_to_native_dtype as arrow_narwhals_to_native_dtype,
)

pa_dtype = arrow_narwhals_to_native_dtype(dtype, version=version)
native_series = native_namespace.chunked_array([values], type=pa_dtype)

if is_eager_allowed(implementation):
ns = _into_compliant_namespace(implementation, version)
series = ns._series.from_iterable(values, name=name, context=ns, dtype=dtype)
return from_native(series, series_only=True)
elif implementation is Implementation.DASK: # pragma: no cover
msg = "Dask support in Narwhals is lazy-only, so `new_series` is not supported"
raise NotImplementedError(msg)
else: # pragma: no cover
native_namespace = implementation.to_native_namespace()
try:
# implementation is UNKNOWN, Narwhals extension using this feature should
# implement `from_dict` function in the top-level namespace.
native_series = native_namespace.new_series(name, values, dtype)
return from_native(native_series, series_only=True).alias(name)
except AttributeError as e:
msg = "Unknown namespace is expected to implement `Series` constructor."
msg = "Unknown namespace is expected to implement `new_series` constructor."
Comment on lines -312 to +275
Copy link
Member

Choose a reason for hiding this comment

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

nice one

raise AttributeError(msg) from e
return from_native(native_series, series_only=True).alias(name)


@deprecate_native_namespace(warn_version="1.26.0")
Expand Down
Loading