У меня есть проблема, которую я пытаюсь решить, используя предпочтительно поляры, но с пандами тоже все в порядке. Скажем, у нас есть следующий набор данных (образец):
{
"date" : [2022-01-01, 2022-01-02, 2022-01-03, 2022-01-04, 2022-01-05],
"customers" : [3, 4, 5, 3, 2],
"is_reporting_day?" : [True, False, False, False, True]
}
чтобы было немного понятнее, вот формат таблицы
Что я хочу получить, так это: если reporting_day is True
оставить количество клиентов как есть, и если reporting_day is False
я хочу суммировать всех клиентов (4, 5, 3 = 12 + 2 = 14) и добавить его к следующему True value reporting day
поэтому после применения преобразования это должно выглядеть так:
Я пытался использовать функцию cumsum()
в полярах с помощью оператора pl.when
, но это неправильная логика, поскольку она суммируется с самого начала, то есть с первого дня (около 700 дней).
Примечание: решение должно быть динамическим, т. е. иногда разрыв между reporting_day и non-reporting_day составляет 1 день, 2 дня и т. д.
Любые идеи или вклад высоко ценятся! Заранее спасибо!
Предполагая, что даты уже отсортированы, используйте groupby.agg:
out = (df.groupby(df['is_reporting_day?'].shift(fill_value=False).cumsum(), as_index=False)
.agg({'date': 'max', 'customers': 'sum', 'is_reporting_day?': 'max'})
)
Выход:
date customers is_reporting_day?
0 2022-01-01 3 True
1 2022-01-05 14 True
Если вам нужны как начальная, так и сумма "клиентов":
out = (df.groupby(df['is_reporting_day?'].shift(fill_value=False).cumsum(), as_index=False)
.agg(**{'date': ('date', 'max'),
'customers': ('customers', 'last'),
'is_reporting_day?': ('is_reporting_day?', 'max'),
'customers_sum': ('customers', 'sum'),
})
)
Выход:
date customers is_reporting_day? customers_sum
0 2022-01-01 3 True 3
1 2022-01-05 2 True 14
Альтернатива:
out = (
df.assign(date=df['date'].where(df['is_reporting_day?']).bfill())
.groupby('date', as_index=False)
.agg(**{'date': ('date', 'max'),
'customers': ('customers', 'last'),
'is_reporting_day?': ('is_reporting_day?', 'max'),
'customers_sum': ('customers', 'sum'),
})
)
То же самое и с полярами, используя подходы @mozway:
(df
.groupby(
pl.col("is_reporting_day?")
.shift_and_fill(False, periods=1)
.cumsum().alias("group"),
maintain_order=True)
.agg(
pl.all().last(),
sum = pl.sum("customers"))
)
shape: (2, 5)
┌───────┬─────────────────────┬───────────┬───────────────────┬─────┐
│ group ┆ date ┆ customers ┆ is_reporting_day? ┆ sum │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ datetime[ns] ┆ i64 ┆ bool ┆ i64 │
╞═══════╪═════════════════════╪═══════════╪═══════════════════╪═════╡
│ 0 ┆ 2022-01-01 00:00:00 ┆ 3 ┆ true ┆ 3 │
│ 1 ┆ 2022-01-05 00:00:00 ┆ 2 ┆ true ┆ 14 │
└───────┴─────────────────────┴───────────┴───────────────────┴─────┘
df.groupby(
pl.when(pl.col("is_reporting_day?"))
.then(pl.col("date"))
.backward_fill()
.alias("group"),
maintain_order=True
).agg(
pl.all().last(),
sum = pl.sum("customers")
)
shape: (2, 5)
┌─────────────────────┬─────────────────────┬───────────┬───────────────────┬─────┐
│ group ┆ date ┆ customers ┆ is_reporting_day? ┆ sum │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ datetime[ns] ┆ datetime[ns] ┆ i64 ┆ bool ┆ i64 │
╞═════════════════════╪═════════════════════╪═══════════╪═══════════════════╪═════╡
│ 2022-01-01 00:00:00 ┆ 2022-01-01 00:00:00 ┆ 3 ┆ true ┆ 3 │
│ 2022-01-05 00:00:00 ┆ 2022-01-05 00:00:00 ┆ 2 ┆ true ┆ 14 │
└─────────────────────┴─────────────────────┴───────────┴───────────────────┴─────┘
Вы можете использовать .over(), если хотите сохранить исходные строки.
df.with_columns(
pl.cumsum("customers").over(
pl.when(pl.col("is_reporting_day?"))
.then(pl.col("date"))
.backward_fill())
.alias("cumsum")
)
shape: (5, 4)
┌─────────────────────┬───────────┬───────────────────┬────────┐
│ date ┆ customers ┆ is_reporting_day? ┆ cumsum │
│ --- ┆ --- ┆ --- ┆ --- │
│ datetime[ns] ┆ i64 ┆ bool ┆ i64 │
╞═════════════════════╪═══════════╪═══════════════════╪════════╡
│ 2022-01-01 00:00:00 ┆ 3 ┆ true ┆ 3 │
│ 2022-01-02 00:00:00 ┆ 4 ┆ false ┆ 4 │
│ 2022-01-03 00:00:00 ┆ 5 ┆ false ┆ 9 │
│ 2022-01-04 00:00:00 ┆ 3 ┆ false ┆ 12 │
│ 2022-01-05 00:00:00 ┆ 2 ┆ true ┆ 14 │
└─────────────────────┴───────────┴───────────────────┴────────┘
col1=(df1.is_reporting_day.eq(False)&df1.is_reporting_day.shift().eq(True)).cumsum()
df1.groupby(col1,group_keys=False).apply(lambda dd:dd.tail(1)
.assign(customers2=dd['customers'].sum()))
вне:
date customers is_reporting_day customers2
0 2022-01-01 3 True 3
4 2022-01-05 2 True 14
Спасибо! именно то, что я пытался достичь. действительно очень похоже на решение mozway.