Выбор строк по идентификатору и вычисление среднего значения в Polars с помощью Rust

У меня есть следующий фрейм данных и вектор имен.

название возраст панда 5 Полярный медведь 7 морской конек 1

Я хотел бы выбрать строки по именам в векторе и рассчитать средний возраст выбранных строк. У меня есть следующий код:

let names = vec!["panda", "seahorse"];
let avg = df.lazy()
    .select([col("name").filter(|c| names.contains(c))])
    .agg([col("age").mean()]);

Интуиция подсказывает, что нужно передать функцию фильтру (как это сделал я), однако это неправильно. По-видимому, в игре есть какой-то Expr API. Как это работает? Я нахожу документы немного загадочными.

этот API выполняет проверку регулярных выражений?

easleyfixed 11.11.2022 17:02

Это помогает? pola-rs.github.io/polars-book/user-guide/dsl/…

isaactfa 11.11.2022 17:34

Они представляют собой API, в котором вы можете выбирать столбцы с помощью регулярных выражений, но вместо этого у меня есть произвольный список имен. Должен быть какой-то простой способ сделать это, которого мне не хватает.

xosxos 11.11.2022 18:08
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
3
128
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не уверен, что есть простой способ отфильтровать так, как вы хотите. Один из вариантов, который у вас есть, — создать отдельный DataFrame, содержащий имена, которые вы хотите отфильтровать, и выполнить соединение:

let filter_df = df! [
    "name" => ["panda", "seahorse"]
].unwrap();

let avg = df.lazy()
    .join(filter_df.lazy(), [col("name")], [col("name")], JoinType::Inner)
    .select([col("age").mean()]);

Было неясно, хотите ли вы выполнить среднее значение для всех возрастов, возвращенных этой фильтрацией (именно так это было сформулировано в вашем вопросе), если это так, то этот код должен работать для вас. Это результат:

Ok(shape: (1, 1)
┌─────┐
│ age │
│ --- │
│ f64 │
╞═════╡
│ 3.0 │
└─────┘)

Ваш код создает впечатление, что вы пытались вычислить среднее значение для всех панд по сравнению со средним значением для всех морских коньков. Если это так, вы можете использовать цепочку .groupby().agg():

let avg = df.lazy()
    .join(names_df.lazy(), [col("name")], [col("name")], JoinType::Inner)
    .groupby([col("name")])
    .agg([col("age").mean()]);

Что производит:

Ok(shape: (2, 2)
┌──────────┬─────┐
│ name     ┆ age │
│ ---      ┆ --- │
│ str      ┆ f64 │
╞══════════╪═════╡
│ seahorse ┆ 1.0 │
├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤
│ panda    ┆ 5.0 │
└──────────┴─────┘)

Спасибо, это работает! Я думаю, что is_in и создание Series, а затем Expr от BallpointBen немного больше подходят для моего варианта использования, но вы также правильно поняли мой немного глупый пример и помогли со средним значением для всех выбранных имен, а не для групп!

xosxos 12.11.2022 09:11
Ответ принят как подходящий

Если вы используете функцию is_in, вы можете фильтровать непосредственно столбец, присутствующий в другом Expr, что приводит к следующему.

fn main() -> Result<(), PolarsError> {
    // Added another panda to see the effect of groupby-mean
    let df = df! [
        "name" => ["panda", "panda", "polarbear", "seahorse"],
        "age" => [5, 55, 7, 1],
    ]?;

    let names = vec!["panda", "seahorse"];
    let df = df
        .lazy()
        .filter(col("name").is_in(lit(Series::from_iter(names))))
        .groupby([col("name")])
        .agg([col("age").mean()]);
    println!("{:?}", df.collect()?);

    Ok(())
}
┌──────────┬──────┐
│ name     ┆ age  │
│ ---      ┆ ---  │
│ str      ┆ f64  │
╞══════════╪══════╡
│ panda    ┆ 30.0 │
├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ seahorse ┆ 1.0  │
└──────────┴──────┘

Спасибо, у меня все заработало! Действительно, для тех, кто интересуется, функция is_in должна быть добавлена ​​в Cargo.toml. polars = { version = "*", features = ["lazy", "is_in"] }

xosxos 12.11.2022 11:22

Другие вопросы по теме