Учитывая полярный DataFramedata = pl.DataFrame({"user_id": [1, 1, 1, 2, 2, 2], "login": [False, True, False, False, False, True]})
Как я могу добавить столбец, который добавляет количество строк с момента последнего входа пользователя в систему, причем для всех строк перед входом в систему для этого пользователя установлено значение «Нет»?
Пример вывода для приведенных выше данных:[None, 0, 1, None, None, 0]
Я пытался адаптировать ответы из здесь, но не смог заставить это работать с группами.
Ваш пример немного ограничен. Что произойдет, если для одного пользователя имеется более 1 логина? Вы сказали, что числа должны увеличиваться после последнего входа в систему, а перед первым входом в систему они должны быть «Нет», но что должно произойти между первым и последним входом в систему?
Я предполагаю, что вы хотите сбросить счетчик при каждом входе в систему, поэтому, если я расширяю ваш входной DataFrame:
data = pl.DataFrame({
"user_id": [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
"login": [False, True, False, False, True, False, False, False, True, False]
})
┌─────────┬───────┐
│ user_id ┆ login │
│ --- ┆ --- │
│ i64 ┆ bool │
╞═════════╪═══════╡
│ 1 ┆ false │
│ 1 ┆ true │
│ 1 ┆ false │
│ 1 ┆ false │
│ 1 ┆ true │
│ 2 ┆ false │
│ 2 ┆ false │
│ 2 ┆ false │
│ 2 ┆ true │
│ 2 ┆ false │
└─────────┴───────┘
Тогда я ожидаю, что результат будет выглядеть так:
┌─────────┬───────┬──────┐
│ user_id ┆ login ┆ res │
│ --- ┆ --- ┆ --- │
│ i64 ┆ bool ┆ u32 │
╞═════════╪═══════╪══════╡
│ 1 ┆ false ┆ null │
│ 1 ┆ true ┆ 0 │
│ 1 ┆ false ┆ 1 │
│ 1 ┆ false ┆ 2 │
│ 1 ┆ true ┆ 0 │
│ 2 ┆ false ┆ null │
│ 2 ┆ false ┆ null │
│ 2 ┆ false ┆ null │
│ 2 ┆ true ┆ 0 │
│ 2 ┆ false ┆ 1 │
└─────────┴───────┴──────┘
В этом случае вы можете использовать Expr.cum_count() поверх Expr.cum_sum() , разделенную на user_id
через Expr.over():
(
data
.with_columns(
res = pl.col('login').cum_sum().over('user_id'),
).with_columns(
pl.when('login').then(pl.col('res').cum_count().over('user_id', 'res') - 1),
)
)
┌─────────┬───────┬──────┐
│ user_id ┆ login ┆ res │
│ --- ┆ --- ┆ --- │
│ i64 ┆ bool ┆ u32 │
╞═════════╪═══════╪══════╡
│ 1 ┆ false ┆ null │
│ 1 ┆ true ┆ 0 │
│ 1 ┆ false ┆ 1 │
│ 1 ┆ false ┆ 2 │
│ 1 ┆ true ┆ 0 │
│ 2 ┆ false ┆ null │
│ 2 ┆ false ┆ null │
│ 2 ┆ false ┆ null │
│ 2 ┆ true ┆ 0 │
│ 2 ┆ false ┆ 1 │
└─────────┴───────┴──────┘
Вы можете вычесть номер строки предыдущего входа в систему.
(df.with_row_index()
.with_columns(distance =
pl.col.index - pl.when("login").then("index").forward_fill().over("user_id")
)
)
shape: (6, 4)
┌───────┬─────────┬───────┬──────────┐
│ index ┆ user_id ┆ login ┆ distance │
│ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ i64 ┆ bool ┆ u32 │
╞═══════╪═════════╪═══════╪══════════╡
│ 0 ┆ 1 ┆ false ┆ null │
│ 1 ┆ 1 ┆ true ┆ 0 │
│ 2 ┆ 1 ┆ false ┆ 1 │
│ 3 ┆ 2 ┆ false ┆ null │
│ 4 ┆ 2 ┆ false ┆ null │
│ 5 ┆ 2 ┆ true ┆ 0 │
└───────┴─────────┴───────┴──────────┘
Если данные не отсортированы, вы можете вместо этого использовать .int_range() в качестве номера строки.
df.with_columns(distance =
pl.int_range(pl.len()).over("user_id")
- pl.when("login").then(pl.int_range(pl.len())).forward_fill().over("user_id")
)
(df.with_row_index()
.with_columns(
new_index = pl.when("login").then("index"),
last_index = pl.when("login").then("index").forward_fill(),
last_index_groupwise = pl.when("login").then("index").forward_fill().over("user_id")
)
)
shape: (6, 6)
┌───────┬─────────┬───────┬───────────┬────────────┬──────────────────────┐
│ index ┆ user_id ┆ login ┆ new_index ┆ last_index ┆ last_index_groupwise │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ i64 ┆ bool ┆ u32 ┆ u32 ┆ u32 │
╞═══════╪═════════╪═══════╪═══════════╪════════════╪══════════════════════╡
│ 0 ┆ 1 ┆ false ┆ null ┆ null ┆ null │
│ 1 ┆ 1 ┆ true ┆ 1 ┆ 1 ┆ 1 │
│ 2 ┆ 1 ┆ false ┆ null ┆ 1 ┆ 1 │
│ 3 ┆ 2 ┆ false ┆ null ┆ 1 ┆ null │
│ 4 ┆ 2 ┆ false ┆ null ┆ 1 ┆ null │
│ 5 ┆ 2 ┆ true ┆ 5 ┆ 5 ┆ 5 │
└───────┴─────────┴───────┴───────────┴────────────┴──────────────────────┘
Спасибо!! Этот ответ хорошо работает, когда user_ids отсортированы, @RomanPekar ваше решение работает, даже если они не отсортированы
@cdkdrf Вместо этого вы можете использовать .int_range()
для несортированного кода — это немного более подробно, я добавил пример.
Благодарить! Не могли бы вы добавить пример того, как я мог бы добавить еще один столбец для количества строк до следующего входа пользователя в систему? Я пробовал с ним поиграться и использовать backward_fill()
, но не получилось.
@cdkdrf Может быть, вы можете добавить новый вопрос - он немного другой.
это хорошо,
forward_fill()
прекрасно следует логике этой задачи.