You can use a sort_by expression to sort your observations in each group by score, and then use the last expression to take the last observation.
For example, to take all columns:
df.groupby('class').agg([
pl.all().sort_by('score').last(),
])
shape: (2, 3)
┌───────┬──────┬───────┐
│ class ┆ name ┆ score │
│ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 │
╞═══════╪══════╪═══════╡
│ a ┆ Jon ┆ 0.5 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Von ┆ 0.4 │
└───────┴──────┴───────┘
Edit: using over
If you have more than one observation that is the max, another easy way to get all rows is to use over.
For example, if your data has two students in class b ('Von' and 'Yvonne') who tied for highest score:
df = pl.DataFrame(
{
"class": ["a", "a", "b", "b", "b"],
"name": ["Ron", "Jon", "Don", "Von", "Yvonne"],
"score": [0.2, 0.5, 0.3, 0.4, 0.4],
}
)
df
shape: (5, 3)
┌───────┬────────┬───────┐
│ class ┆ name ┆ score │
│ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 │
╞═══════╪════════╪═══════╡
│ a ┆ Ron ┆ 0.2 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ a ┆ Jon ┆ 0.5 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Don ┆ 0.3 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Von ┆ 0.4 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Yvonne ┆ 0.4 │
└───────┴────────┴───────┘
df.filter(pl.col('score') == pl.col('score').max().over('class'))
shape: (3, 3)
┌───────┬────────┬───────┐
│ class ┆ name ┆ score │
│ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 │
╞═══════╪════════╪═══════╡
│ a ┆ Jon ┆ 0.5 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Von ┆ 0.4 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ b ┆ Yvonne ┆ 0.4 │
└───────┴────────┴───────┘