Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0d18f96
Update
osoucy Mar 3, 2025
496cbd0
Update
osoucy Mar 4, 2025
9592277
Update
osoucy Mar 4, 2025
aaebbbe
Update
osoucy Mar 4, 2025
273f331
Update
osoucy Mar 4, 2025
2d95d3d
Update
osoucy Mar 4, 2025
decba02
Update
osoucy Mar 4, 2025
f89f7d3
Merge branch 'main' into feat/struct_namespace
osoucy Mar 4, 2025
e75da43
Addressed PR comment
osoucy Mar 5, 2025
ef8b863
Update docs/api-completeness/expr_struct.md
osoucy Mar 5, 2025
fa802a4
Merge remote-tracking branch 'origin/feat/struct_namespace' into feat…
osoucy Mar 5, 2025
5e04a46
Addressed PR comment
osoucy Mar 5, 2025
47345f5
Addressed PR comment
osoucy Mar 5, 2025
9517b91
Merge branch 'main' into feat/struct_namespace
osoucy Mar 5, 2025
e8623e2
Addressed PR comment
osoucy Mar 5, 2025
2d4ce6b
Addressed PR comment
osoucy Mar 5, 2025
825d29e
Addressed PR comment
osoucy Mar 5, 2025
6f867ef
Addressed PR comment
osoucy Mar 5, 2025
e54663c
Addressed PR comment
osoucy Mar 5, 2025
8fe86ae
Addressed PR comment
osoucy Mar 5, 2025
37dc7fc
Addressed PR comment
osoucy Mar 5, 2025
104d40d
Addressed PR comment
osoucy Mar 5, 2025
9ead00a
Merge branch 'main' into feat/struct_namespace
osoucy Mar 5, 2025
9e2a24d
Merge branch 'main' into feat/struct_namespace
osoucy Mar 5, 2025
20ec4eb
Merge branch 'main' into feat/struct_namespace
osoucy Mar 5, 2025
ecfd180
Merge branch 'main' into feat/struct_namespace
osoucy Mar 5, 2025
6c5597b
PR Comments
osoucy Mar 6, 2025
99a78e3
Merge branch 'main' into feat/struct_namespace
osoucy Mar 6, 2025
105d3f9
Update docs/api-completeness/series_struct.md
osoucy Mar 6, 2025
d21bdc2
PR Comments
osoucy Mar 6, 2025
9db179a
Merge branch 'main' into feat/struct_namespace
osoucy Mar 6, 2025
865bedd
Merge branch 'main' into feat/struct_namespace
osoucy Mar 7, 2025
3ad7ae9
PR Comments
osoucy Mar 8, 2025
7e5d813
PR Comments
osoucy Mar 8, 2025
c5cf993
PR Comments
osoucy Mar 8, 2025
4d36295
PR Comments
osoucy Mar 8, 2025
8701e43
Merge branch 'main' into feat/struct_namespace
osoucy Mar 8, 2025
4ed080c
PR Comments
osoucy Mar 9, 2025
195088c
PR Comments
osoucy Mar 9, 2025
71df89d
Merge branch 'main' into feat/struct_namespace
osoucy Mar 9, 2025
ebe4d6b
remove unnecessary aliases
MarcoGorelli Mar 9, 2025
59fb567
remove unnecessary aliases
MarcoGorelli Mar 9, 2025
736599e
extra test
MarcoGorelli Mar 9, 2025
f71e677
remove outdated
MarcoGorelli Mar 9, 2025
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
9 changes: 9 additions & 0 deletions docs/api-reference/expr_struct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `narwhals.Expr.struct`

::: narwhals.expr.ExprStructNamespace
handler: python
options:
members:
- field
show_source: false
show_bases: false
2 changes: 2 additions & 0 deletions docs/api-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [narwhals.Expr.list](expr_list.md)
- [narwhals.Expr.name](expr_name.md)
- [narwhals.Expr.str](expr_str.md)
- [narwhals.Expr.struct](expr_struct.md)
- [narwhals.GroupBy](group_by.md)
- [narwhals.LazyGroupBy](lazy_group_by.md)
- [narwhals.LazyFrame](lazyframe.md)
Expand All @@ -17,6 +18,7 @@
- [narwhals.Series.dt](series_dt.md)
- [narwhals.Series.list](series_list.md)
- [narwhals.Series.str](series_str.md)
- [narwhals.Series.struct](series_struct.md)
- [narwhals.dependencies](dependencies.md)
- [narwhals.Implementation](implementation.md)
- [narwhals.dtypes](dtypes.md)
Expand Down
9 changes: 9 additions & 0 deletions docs/api-reference/series_struct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `narwhals.Series.struct`

::: narwhals.series.SeriesStructNamespace
handler: python
options:
members:
- field
show_source: false
show_bases: false
4 changes: 4 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ nav:
- Supported Expr.list methods: api-completeness/expr_list.md
- Supported Expr.name methods: api-completeness/expr_name.md
- Supported Expr.str methods: api-completeness/expr_str.md
- Supported Expr.struct methods: api-completeness/expr_struct.md
- Supported Series methods: api-completeness/series.md
- Supported Series.cat methods: api-completeness/series_cat.md
- Supported Series.dt methods: api-completeness/series_dt.md
- Supported Series.list methods: api-completeness/series_list.md
- Supported Series.str methods: api-completeness/series_str.md
- Supported Series.struct methods: api-completeness/series_struct.md
- API Reference:
- api-reference/index.md
- api-reference/narwhals.md
Expand All @@ -49,6 +51,7 @@ nav:
- api-reference/expr_list.md
- api-reference/expr_name.md
- api-reference/expr_str.md
- api-reference/expr_struct.md
- api-reference/group_by.md
- api-reference/lazy_group_by.md
- api-reference/lazyframe.md
Expand All @@ -58,6 +61,7 @@ nav:
- api-reference/series_dt.md
- api-reference/series_list.md
- api-reference/series_str.md
- api-reference/series_struct.md
- api-reference/dependencies.md
- api-reference/implementation.md
- api-reference/dtypes.md
Expand Down
5 changes: 5 additions & 0 deletions narwhals/_arrow/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from narwhals._arrow.expr_list import ArrowExprListNamespace
from narwhals._arrow.expr_name import ArrowExprNameNamespace
from narwhals._arrow.expr_str import ArrowExprStringNamespace
from narwhals._arrow.expr_struct import ArrowExprStructNamespace
from narwhals._arrow.series import ArrowSeries
from narwhals._expression_parsing import ExprKind
from narwhals._expression_parsing import evaluate_output_names_and_aliases
Expand Down Expand Up @@ -635,3 +636,7 @@ def name(self: Self) -> ArrowExprNameNamespace:
@property
def list(self: Self) -> ArrowExprListNamespace:
return ArrowExprListNamespace(self)

@property
def struct(self: Self) -> ArrowExprStructNamespace:
return ArrowExprStructNamespace(self)
23 changes: 23 additions & 0 deletions narwhals/_arrow/expr_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from narwhals._expression_parsing import reuse_series_namespace_implementation

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals._arrow.expr import ArrowExpr


class ArrowExprStructNamespace:
def __init__(self: Self, expr: ArrowExpr) -> None:
self._compliant_expr = expr

def field(self: Self, name: str) -> ArrowExpr:
return reuse_series_namespace_implementation(
self._compliant_expr,
"struct",
"field",
name=name,
).alias(name)
5 changes: 5 additions & 0 deletions narwhals/_arrow/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from narwhals._arrow.series_dt import ArrowSeriesDateTimeNamespace
from narwhals._arrow.series_list import ArrowSeriesListNamespace
from narwhals._arrow.series_str import ArrowSeriesStringNamespace
from narwhals._arrow.series_struct import ArrowSeriesStructNamespace
from narwhals._arrow.utils import cast_for_truediv
from narwhals._arrow.utils import chunked_array
from narwhals._arrow.utils import extract_native
Expand Down Expand Up @@ -1213,3 +1214,7 @@ def str(self: Self) -> ArrowSeriesStringNamespace:
@property
def list(self: Self) -> ArrowSeriesListNamespace:
return ArrowSeriesListNamespace(self)

@property
def struct(self: Self) -> ArrowSeriesStructNamespace:
return ArrowSeriesStructNamespace(self)
20 changes: 20 additions & 0 deletions narwhals/_arrow/series_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import annotations

from typing import TYPE_CHECKING

import pyarrow.compute as pc

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals._arrow.series import ArrowSeries


class ArrowSeriesStructNamespace:
def __init__(self: Self, series: ArrowSeries) -> None:
self._compliant_series: ArrowSeries = series

def field(self: Self, name: str) -> ArrowSeries:
return self._compliant_series._from_native_series(
pc.struct_field(self._compliant_series._native_series, name),
).alias(name)
1 change: 1 addition & 0 deletions narwhals/_dask/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,4 @@ def name(self: Self) -> DaskExprNameNamespace:

cat = not_implemented() # pyright: ignore[reportAssignmentType]
list = not_implemented() # pyright: ignore[reportAssignmentType]
struct = not_implemented() # pyright: ignore[reportAssignmentType]
5 changes: 5 additions & 0 deletions narwhals/_duckdb/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from narwhals._duckdb.expr_list import DuckDBExprListNamespace
from narwhals._duckdb.expr_name import DuckDBExprNameNamespace
from narwhals._duckdb.expr_str import DuckDBExprStringNamespace
from narwhals._duckdb.expr_struct import DuckDBExprStructNamespace
from narwhals._duckdb.utils import lit
from narwhals._duckdb.utils import maybe_evaluate_expr
from narwhals._duckdb.utils import narwhals_to_native_dtype
Expand Down Expand Up @@ -484,6 +485,10 @@ def name(self: Self) -> DuckDBExprNameNamespace:
def list(self: Self) -> DuckDBExprListNamespace:
return DuckDBExprListNamespace(self)

@property
def struct(self: Self) -> DuckDBExprStructNamespace:
return DuckDBExprStructNamespace(self)

arg_min = not_implemented()
arg_max = not_implemented()
arg_true = not_implemented()
Expand Down
23 changes: 23 additions & 0 deletions narwhals/_duckdb/expr_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from duckdb import FunctionExpression

from narwhals._duckdb.utils import lit

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals._duckdb.expr import DuckDBExpr


class DuckDBExprStructNamespace:
def __init__(self: Self, expr: DuckDBExpr) -> None:
self._compliant_expr = expr

def field(self: Self, name: str) -> DuckDBExpr:
return self._compliant_expr._from_call(
lambda _input: FunctionExpression("struct_extract", _input, lit(name)),
"field",
).alias(name)
1 change: 1 addition & 0 deletions narwhals/_expression_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def reuse_series_namespace_implementation(
kwargs: keyword arguments to pass to function.
"""
plx = expr.__narwhals_namespace__()

return plx._create_expr_from_callable( # type: ignore[return-value]
lambda df: [
getattr(getattr(series, series_namespace), attr)(**kwargs)
Expand Down
5 changes: 5 additions & 0 deletions narwhals/_pandas_like/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from narwhals._pandas_like.expr_list import PandasLikeExprListNamespace
from narwhals._pandas_like.expr_name import PandasLikeExprNameNamespace
from narwhals._pandas_like.expr_str import PandasLikeExprStringNamespace
from narwhals._pandas_like.expr_struct import PandasLikeExprStructNamespace
from narwhals._pandas_like.group_by import AGGREGATIONS_TO_PANDAS_EQUIVALENT
from narwhals._pandas_like.series import PandasLikeSeries
from narwhals.dependencies import get_numpy
Expand Down Expand Up @@ -753,3 +754,7 @@ def name(self: Self) -> PandasLikeExprNameNamespace:
@property
def list(self: Self) -> PandasLikeExprListNamespace:
return PandasLikeExprListNamespace(self)

@property
def struct(self: Self) -> PandasLikeExprStructNamespace:
return PandasLikeExprStructNamespace(self)
23 changes: 23 additions & 0 deletions narwhals/_pandas_like/expr_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from narwhals._expression_parsing import reuse_series_namespace_implementation

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals._pandas_like.expr import PandasLikeExpr


class PandasLikeExprStructNamespace:
def __init__(self: Self, expr: PandasLikeExpr) -> None:
self._compliant_expr = expr

def field(self, name: str) -> PandasLikeExpr:
return reuse_series_namespace_implementation(
self._compliant_expr,
"struct",
"field",
name=name,
).alias(name)
5 changes: 5 additions & 0 deletions narwhals/_pandas_like/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from narwhals._pandas_like.series_dt import PandasLikeSeriesDateTimeNamespace
from narwhals._pandas_like.series_list import PandasLikeSeriesListNamespace
from narwhals._pandas_like.series_str import PandasLikeSeriesStringNamespace
from narwhals._pandas_like.series_struct import PandasLikeSeriesStructNamespace
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
Expand Down Expand Up @@ -1131,3 +1132,7 @@ def cat(self: Self) -> PandasLikeSeriesCatNamespace:
@property
def list(self: Self) -> PandasLikeSeriesListNamespace:
return PandasLikeSeriesListNamespace(self)

@property
def struct(self: Self) -> PandasLikeSeriesStructNamespace:
return PandasLikeSeriesStructNamespace(self)
3 changes: 3 additions & 0 deletions narwhals/_pandas_like/series_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

class PandasLikeSeriesListNamespace:
def __init__(self: Self, series: PandasLikeSeries) -> None:
if not hasattr(series._native_series, "list"):
msg = "Series must be of PyArrow List type to support list namespace."
raise TypeError(msg)
self._compliant_series = series

def len(self: Self) -> PandasLikeSeries:
Expand Down
21 changes: 21 additions & 0 deletions narwhals/_pandas_like/series_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals._pandas_like.series import PandasLikeSeries


class PandasLikeSeriesStructNamespace:
def __init__(self: Self, series: PandasLikeSeries) -> None:
if not hasattr(series._native_series, "struct"):
msg = "Series must be of PyArrow Struct type to support struct namespace."
raise TypeError(msg)
self._compliant_series = series

def field(self: Self, name: str) -> PandasLikeSeries:
return self._compliant_series._from_native_series(
self._compliant_series._native_series.struct.field(name)
).alias(name)
20 changes: 20 additions & 0 deletions narwhals/_polars/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ def name(self: Self) -> PolarsExprNameNamespace:
def list(self: Self) -> PolarsExprListNamespace:
return PolarsExprListNamespace(self)

@property
def struct(self: Self) -> PolarsExprStructNamespace:
return PolarsExprStructNamespace(self)


class PolarsExprDateTimeNamespace:
def __init__(self: Self, expr: PolarsExpr) -> None:
Expand Down Expand Up @@ -401,3 +405,19 @@ def func(*args: Any, **kwargs: Any) -> PolarsExpr:
)

return func


class PolarsExprStructNamespace:
def __init__(self: Self, expr: PolarsExpr) -> None:
self._expr = expr

def __getattr__(
self: Self, attr: str
) -> Callable[[Any], PolarsExpr]: # pragma: no cover
def func(*args: Any, **kwargs: Any) -> PolarsExpr:
args, kwargs = extract_args_kwargs(args, kwargs) # type: ignore[assignment]
return self._expr._from_native_expr(
getattr(self._expr._native_expr.struct, attr)(*args, **kwargs)
)

return func
20 changes: 20 additions & 0 deletions narwhals/_polars/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,10 @@ def cat(self: Self) -> PolarsSeriesCatNamespace:
def list(self: Self) -> PolarsSeriesListNamespace:
return PolarsSeriesListNamespace(self)

@property
def struct(self: Self) -> PolarsSeriesStructNamespace:
return PolarsSeriesStructNamespace(self)


class PolarsSeriesDateTimeNamespace:
def __init__(self: Self, series: PolarsSeries) -> None:
Expand Down Expand Up @@ -650,3 +654,19 @@ def func(*args: Any, **kwargs: Any) -> Any:
)

return func


class PolarsSeriesStructNamespace:
def __init__(self: Self, series: PolarsSeries) -> None:
self._compliant_series = series

def __getattr__(self: Self, attr: str) -> Any:
def func(*args: Any, **kwargs: Any) -> Any:
args, kwargs = extract_args_kwargs(args, kwargs) # type: ignore[assignment]
return self._compliant_series._from_native_series(
getattr(self._compliant_series._native_series.struct, attr)(
*args, **kwargs
)
)

return func
5 changes: 5 additions & 0 deletions narwhals/_spark_like/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from narwhals._spark_like.expr_list import SparkLikeExprListNamespace
from narwhals._spark_like.expr_name import SparkLikeExprNameNamespace
from narwhals._spark_like.expr_str import SparkLikeExprStringNamespace
from narwhals._spark_like.expr_struct import SparkLikeExprStructNamespace
from narwhals._spark_like.utils import maybe_evaluate_expr
from narwhals._spark_like.utils import narwhals_to_native_dtype
from narwhals.dependencies import get_pyspark
Expand Down Expand Up @@ -617,6 +618,10 @@ def dt(self: Self) -> SparkLikeExprDateTimeNamespace:
def list(self: Self) -> SparkLikeExprListNamespace:
return SparkLikeExprListNamespace(self)

@property
def struct(self: Self) -> SparkLikeExprStructNamespace:
return SparkLikeExprStructNamespace(self)

arg_min = not_implemented()
arg_max = not_implemented()
arg_true = not_implemented()
Expand Down
20 changes: 20 additions & 0 deletions narwhals/_spark_like/expr_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pyspark.sql import Column
from typing_extensions import Self

from narwhals._spark_like.expr import SparkLikeExpr


class SparkLikeExprStructNamespace:
def __init__(self: Self, expr: SparkLikeExpr) -> None:
self._compliant_expr = expr

def field(self: Self, name: str) -> SparkLikeExpr:
def func(_input: Column) -> Column:
return _input.getField(name)

return self._compliant_expr._from_call(func, "field").alias(name)
Loading
Loading