From 0628e0e991f215c6d98d8ca063389ddd62c9bc6d Mon Sep 17 00:00:00 2001 From: alm Date: Sun, 13 Oct 2024 13:47:42 +0300 Subject: [PATCH] feat(python): Add Expr.struct.unnest() as alias for Expr.struct.field("*") (#19212) --- .../source/reference/expressions/struct.rst | 1 + py-polars/polars/expr/struct.py | 37 +++++++++++++++++++ py-polars/tests/unit/test_expansion.py | 14 +++++++ 3 files changed, 52 insertions(+) diff --git a/py-polars/docs/source/reference/expressions/struct.rst b/py-polars/docs/source/reference/expressions/struct.rst index 958436e4066b..cd081477b23b 100644 --- a/py-polars/docs/source/reference/expressions/struct.rst +++ b/py-polars/docs/source/reference/expressions/struct.rst @@ -10,6 +10,7 @@ The following methods are available under the `expr.struct` attribute. :template: autosummary/accessor_method.rst Expr.struct.field + Expr.struct.unnest Expr.struct.json_encode Expr.struct.rename_fields Expr.struct.with_fields diff --git a/py-polars/polars/expr/struct.py b/py-polars/polars/expr/struct.py index 57b8b6eddfb3..1df3a16f4411 100644 --- a/py-polars/polars/expr/struct.py +++ b/py-polars/polars/expr/struct.py @@ -152,6 +152,43 @@ def field(self, name: str | list[str], *more_names: str) -> Expr: return wrap_expr(self._pyexpr.struct_field_by_name(name)) + def unnest(self) -> Expr: + """ + Expand the struct into its individual fields. + + Alias for `Expr.struct.field("*")`. + + >>> df = pl.DataFrame( + ... { + ... "aaa": [1, 2], + ... "bbb": ["ab", "cd"], + ... "ccc": [True, None], + ... "ddd": [[1, 2], [3]], + ... } + ... ).select(pl.struct("aaa", "bbb", "ccc", "ddd").alias("struct_col")) + >>> df + shape: (2, 1) + ┌──────────────────────┐ + │ struct_col │ + │ --- │ + │ struct[4] │ + ╞══════════════════════╡ + │ {1,"ab",true,[1, 2]} │ + │ {2,"cd",null,[3]} │ + └──────────────────────┘ + >>> df.select(pl.col("struct_col").struct.unnest()) + shape: (2, 4) + ┌─────┬─────┬──────┬───────────┐ + │ aaa ┆ bbb ┆ ccc ┆ ddd │ + │ --- ┆ --- ┆ --- ┆ --- │ + │ i64 ┆ str ┆ bool ┆ list[i64] │ + ╞═════╪═════╪══════╪═══════════╡ + │ 1 ┆ ab ┆ true ┆ [1, 2] │ + │ 2 ┆ cd ┆ null ┆ [3] │ + └─────┴─────┴──────┴───────────┘ + """ + return self.field("*") + def rename_fields(self, names: Sequence[str]) -> Expr: """ Rename the fields of the struct. diff --git a/py-polars/tests/unit/test_expansion.py b/py-polars/tests/unit/test_expansion.py index 795d5511cc50..b01fc625f5cc 100644 --- a/py-polars/tests/unit/test_expansion.py +++ b/py-polars/tests/unit/test_expansion.py @@ -138,6 +138,20 @@ def test_struct_field_expand_star() -> None: assert_frame_equal(struct_df.select(pl.col("struct_col").struct.field("*")), df) +def test_struct_unnest() -> None: + """Same as test_struct_field_expand_star but using the unnest alias.""" + df = pl.DataFrame( + { + "aaa": [1, 2], + "bbb": ["ab", "cd"], + "ccc": [True, None], + "ddd": [[1, 2], [3]], + } + ) + struct_df = df.select(pl.struct(["aaa", "bbb", "ccc", "ddd"]).alias("struct_col")) + assert_frame_equal(struct_df.select(pl.col("struct_col").struct.unnest()), df) + + def test_struct_field_expand_rewrite() -> None: df = pl.DataFrame({"A": [1], "B": [2]}) assert df.select(