Как добиться group_by_dynamic с последующей распаковкой?Python

Программы на Python
Ответить
Anonymous
 Как добиться group_by_dynamic с последующей распаковкой?

Сообщение Anonymous »

Контекст
У меня есть кадр данных Polars («df»), состоящий из временного столбца («дата»), потенциального столбца идентификатора («id») и ряда числовых столбцов (т. е. функций). Форма: (14852, 431).
Формат df представляет данные о финансовых транзакциях. Идентификатор указывает на клиента; дата является начальной датой месяца, в котором были осуществлены эти операции. Характеристики представляют собой некие «средние значения» (например, средние потраченные деньги, количество транзакций и т. д.).
После соответствующих манипуляций этот df должен быть передан в модель машинного обучения для целей обучения.
Цель
Качественно я пытаюсь сделать следующее:
  • Для каждого уникального идентификатора (клиента) создайте Скользящее окно на 6 месяцев;
  • Ограничить df транзакциями за этот период для этого идентификатора;
  • "Разложить" его строки. То есть: если в данном временном окне происходит 6 транзакций для данного идентификатора, т. е. у нас есть ограниченный_df формы (6, 431), мне нужно будет заменить его на unstacked_df формы (1, 431 * 6). На практике, если у меня есть имя функции "money_spent" в ограниченном_df, то unstacked_df должно содержать что-то вроде "money_spent_0", "money_spent_1", ..., "money_spent_5".
Подход
Я вроде как знаю различные части головоломки, хотя, по общему признанию, я новичок в этом Поляры. На мой взгляд это:
  • group_by_dynamic("date", every="180d", period="180d", group_by="id")
  • unstack(step=1, columns=features)
Однако я не могу заставить их работать, по крайней мере, эффективно. Неэффективное решение см. ниже.
Проблема
Я считаю, что основная проблема заключается в том, что, насколько я понимаю, после group_by_dynamic Polars ожидает .agg, который предназначен для применения к отдельным столбцам, например через pl.col("foo").some_function(). Однако в Series нет метода разбивки, поэтому он не совсем работает.
Попытка решения
Один крайне неэффективный подход — преобразовать вышеупомянутую серию в DataFrame, а затем вместо этого разложить ее. Однако само по себе это не совсем решает проблему. Фактически, мы просто получаем df с теми же 431 столбцами, где каждый из них содержит DataFrame (тот, который мы разобрали) для каждой строки.
Это получается с помощью
df.group_by_dynamic("date", every="180d", period="180d", group_by="id").agg(pl.col(features).apply(lambda x: pl.DataFrame(x).unstack(step=1)))

Схематически для функции «foo» мы получаем что-то вроде
| foo |
|col_0 1, col_1 2, ...|

Вместо желаемого
|foo_0|foo_1|...|
| 1 | 2 |...|

Чтобы исправить это, мы могли бы вставить to_dict() и в конце использовать функцию unnest. Это достигается с помощью
df.group_by_dynamic("date", every="180d", period="180d", group_by="id").agg(pl.col(features).map_elements(lambda x: pl.DataFrame(x).unstack(step=1).to_dict())).unnest()

Вопрос
Это могло бы сработать, но, очевидно, очень неэффективно и кажется мне излишним. Есть ли способ добиться этого?
Минимальный пример
import numpy as np
import polars as pl
from datetime import date

# Generate fake data
ids = [1]*6 + [2]*6
start = date(2023, 1, 1)
end = date(2023, 12, 1)
dates = pl.date_range(start, end, "1mo", eager=True)
foos = np.arange(0, 12)
bars = np.arange(12, 24)

# Generate df
df = pl.DataFrame({"id":ids, "date":dates, "foo":foos, "bar":bars})

# Print df
print(df)
┌─────┬────────────┬─────┬─────┐
│ id ┆ date ┆ foo ┆ bar │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ date ┆ i64 ┆ i64 │
╞═════╪════════════╪═════╪═════╡
│ 1 ┆ 2023-01-01 ┆ 0 ┆ 12 │
│ 1 ┆ 2023-02-01 ┆ 1 ┆ 13 │
│ 1 ┆ 2023-03-01 ┆ 2 ┆ 14 │
│ 1 ┆ 2023-04-01 ┆ 3 ┆ 15 │
│ … ┆ … ┆ … ┆ … │
│ 2 ┆ 2023-09-01 ┆ 8 ┆ 20 │
│ 2 ┆ 2023-10-01 ┆ 9 ┆ 21 │
│ 2 ┆ 2023-11-01 ┆ 10 ┆ 22 │
│ 2 ┆ 2023-12-01 ┆ 11 ┆ 23 │

# Group df as required
grouped_df = df.group_by_dynamic("date", every="180d", period="180d", group_by="id")

# Check group content
for _name, group in grouped_df:

print(group)

shape: (6, 4)
┌─────┬────────────┬─────┬─────┐
│ id ┆ date ┆ foo ┆ bar │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ date ┆ i64 ┆ i64 │
╞═════╪════════════╪═════╪═════╡
│ 1 ┆ 2023-01-01 ┆ 0 ┆ 12 │
│ 1 ┆ 2023-02-01 ┆ 1 ┆ 13 │
│ 1 ┆ 2023-03-01 ┆ 2 ┆ 14 │
│ 1 ┆ 2023-04-01 ┆ 3 ┆ 15 │
│ 1 ┆ 2023-05-01 ┆ 4 ┆ 16 │
│ 1 ┆ 2023-06-01 ┆ 5 ┆ 17 │
└─────┴────────────┴─────┴─────┘
shape: (6, 4)
┌─────┬────────────┬─────┬─────┐
│ id ┆ date ┆ foo ┆ bar │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ date ┆ i64 ┆ i64 │
╞═════╪════════════╪═════╪═════╡
│ 2 ┆ 2023-07-01 ┆ 6 ┆ 18 │
│ 2 ┆ 2023-08-01 ┆ 7 ┆ 19 │
│ 2 ┆ 2023-09-01 ┆ 8 ┆ 20 │
│ 2 ┆ 2023-10-01 ┆ 9 ┆ 21 │
│ 2 ┆ 2023-11-01 ┆ 10 ┆ 22 │
│ 2 ┆ 2023-12-01 ┆ 11 ┆ 23 │
└─────┴────────────┴─────┴─────┘

# Manipulation
result = ...

# Expected output after correct manipulation
print(result)

shape: (2, 14)
┌─────┬────────────┬───────┬───────┬───┬───────┬───────┬───────┬───────┐
│ id ┆ date ┆ foo_0 ┆ foo_1 ┆ … ┆ bar_2 ┆ bar_3 ┆ bar_4 ┆ bar_5 │
│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ date ┆ i64 ┆ i64 ┆ ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═════╪════════════╪═══════╪═══════╪═══╪═══════╪═══════╪═══════╪═══════╡
│ 1 ┆ 2023-01-01 ┆ 0 ┆ 1 ┆ … ┆ 14 ┆ 15 ┆ 16 ┆ 17 │
│ 2 ┆ 2023-07-01 ┆ 6 ┆ 7 ┆ … ┆ 20 ┆ 21 ┆ 22 ┆ 23 │
└─────┴────────────┴───────┴───────┴───┴───────┴───────┴───────┴───────┘


Подробнее здесь: https://stackoverflow.com/questions/763 ... by-unstack
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»