Skip to content

Commit

Permalink
struct rename fields (#2845)
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchie46 committed Mar 7, 2022
1 parent 96980ff commit 5a5c993
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 1 deletion.
36 changes: 36 additions & 0 deletions polars/polars-lazy/src/dsl/struct_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::*;
pub struct StructNameSpace(pub(crate) Expr);

impl StructNameSpace {
/// Retrieve one of the fields of this [`StructChunked`] as a new Series.
pub fn field_by_name(self, name: &str) -> Expr {
let name1 = name.to_string();
let name2 = name.to_string();
Expand All @@ -29,4 +30,39 @@ impl StructNameSpace {
.with_fmt("struct.field_by_name")
.alias(name)
}

/// Rename the fields of the [`StructChunked`].
pub fn rename_fields(self, names: Vec<String>) -> Expr {
let names = Arc::new(names);
let names2 = names.clone();
self.0
.map(
move |s| {
let ca = s.struct_()?;
let fields = ca
.fields()
.iter()
.zip(names.as_ref())
.map(|(s, name)| {
let mut s = s.clone();
s.rename(name);
s
})
.collect::<Vec<_>>();
StructChunked::new(ca.name(), &fields).map(|ca| ca.into_series())
},
GetOutput::map_dtype(move |dt| match dt {
DataType::Struct(fields) => {
let fields = fields
.iter()
.zip(names2.as_ref())
.map(|(fld, name)| Field::new(name, fld.data_type().clone()))
.collect();
DataType::Struct(fields)
}
_ => dt.clone(),
}),
)
.with_fmt("struct.rename_fields")
}
}
1 change: 1 addition & 0 deletions py-polars/docs/source/reference/expression.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,4 @@ The following methods are available under the `expr.struct` attribute.
:toctree: api/

ExprStructNameSpace.field
ExprStructNameSpace.rename_fields
1 change: 1 addition & 0 deletions py-polars/docs/source/reference/series.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,4 @@ The following methods are available under the `Series.struct` attribute.
StructNameSpace.to_frame
StructNameSpace.field
StructNameSpace.fields
StructNameSpace.rename_fields
11 changes: 11 additions & 0 deletions py-polars/polars/internals/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2847,6 +2847,17 @@ def field(self, name: str) -> Expr:
"""
return wrap_expr(self._pyexpr.struct_field_by_name(name))

def rename_fields(self, names: List[str]) -> Expr:
"""
Rename the fields of the struct
Parameters
----------
names
New names in the order of the struct's fields
"""
return wrap_expr(self._pyexpr.struct_rename_fields(names))


class ExprListNameSpace:
"""
Expand Down
12 changes: 12 additions & 0 deletions py-polars/polars/internals/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3560,12 +3560,24 @@ def field(self, name: str) -> Series:
"""
return pli.select(pli.lit(self.s).struct.field(name)).to_series()

@property
def fields(self) -> List[str]:
"""
Get the names of the fields
"""
return self.s._s.struct_fields()

def rename_fields(self, names: List[str]) -> Series:
"""
Rename the fields of the struct
Parameters
----------
names
New names in the order of the struct's fields
"""
return pli.select(pli.lit(self.s).struct.rename_fields(names)).to_series()


class StringNameSpace:
"""
Expand Down
4 changes: 4 additions & 0 deletions py-polars/src/lazy/dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,10 @@ impl PyExpr {
pub fn struct_field_by_name(&self, name: &str) -> PyExpr {
self.inner.clone().struct_().field_by_name(name).into()
}

pub fn struct_rename_fields(&self, names: Vec<String>) -> PyExpr {
self.inner.clone().struct_().rename_fields(names).into()
}
}

impl From<dsl::Expr> for PyExpr {
Expand Down
10 changes: 9 additions & 1 deletion py-polars/tests/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def test_struct_various() -> None:
)
s = df.to_struct("my_struct")

assert s.struct.fields() == ["int", "str", "bool", "list"]
assert s.struct.fields == ["int", "str", "bool", "list"]
assert s[0] == (1, "a", True, pl.Series([1, 2]))
assert s[1] == (2, "b", None, pl.Series([3]))
assert s.struct.field("list").to_list() == [[1, 2], [3]]
Expand Down Expand Up @@ -43,3 +43,11 @@ def test_apply_to_struct() -> None:
)

assert df.frame_equal(expected)


def test_rename_fields() -> None:
df = pl.DataFrame({"int": [1, 2], "str": ["a", "b"], "bool": [True, None]})
assert df.to_struct("my_struct").struct.rename_fields(["a", "b"]).struct.fields == [
"a",
"b",
]

0 comments on commit 5a5c993

Please sign in to comment.