Найти пересекающиеся сроки
Есть таблица со сроками проведения промо-акций (date_from
, date_to
) на различные товары (product_id
) в разных магазинах (store_id
). Мне нужно выяснить, есть ли пересекающиеся сроки промо-акций для пары товар-магазин?
Желательно ещё добавить столбец с метками, типа "Пересекается\ Не пересекается".
product_id | store_id | promo_id | date_from | date_to |
---|---|---|---|---|
1 | 1 | 8 | 2024-01-01 | 2024-01-07 |
1 | 2 | 9 | 2024-01-08 | 2024-01-14 |
2 | 1 | 1 | 2024-02-19 | 2024-02-25 |
2 | 2 | 10 | 2023-03-13 | 2023-03-19 |
3 | 1 | 3 | 2023-03-20 | 2023-03-26 |
3 | 2 | 11 | 2023-05-08 | 2023-05-14 |
4 | 1 | 12 | 2024-02-12 | 2024-02-18 |
4 | 2 | 13 | 2024-04-01 | 2024-04-07 |
5 | 1 | 14 | 2023-07-03 | 2023-07-09 |
5 | 2 | 20 | 2024-07-29 | 2024-08-04 |
Ответы (1 шт):
Вдвоём с ChatGPT вот удалось родить нужный код (она по моим указаниям написала функцию mark_overlaps
, причём первоначально она хотела использовать цикл с iterrows
вместо векторного сравнения с помощью shift
). Я ещё добавил пересекающийся диапазон в конце (специально с неправильной сортировкой событий), в ваших данных его не было.
План проверки простой:
- Группируем строки по магазину и товару
- Сортируем группу по дате начала
- Сравниваем в каждой строке дату начала с датой окончания из предыдущей строки, чтобы найти пересечения
- Если хоть в одной строке группы есть пересечение, помечаем дополнительным флагом всю группу
from io import StringIO
import pandas as pd
def mark_overlaps(group: pd.DataFrame) -> pd.DataFrame:
group = group.sort_values("date_from").reset_index(drop=True)
group["prev_end"] = group["date_to"].shift()
group["overlap_row"] = group["date_from"] <= group["prev_end"]
group["has_overlap"] = group["overlap_row"].any()
return group.drop(columns="prev_end")
data = """
product_id store_id promo_id date_from date_to
1 1 8 2024-01-01 2024-01-07
1 2 9 2024-01-08 2024-01-14
2 1 1 2024-02-19 2024-02-25
2 2 10 2023-03-13 2023-03-19
3 1 3 2023-03-20 2023-03-26
3 2 11 2023-05-08 2023-05-14
4 1 12 2024-02-12 2024-02-18
4 2 13 2024-04-01 2024-04-07
5 1 14 2023-07-03 2023-07-09
5 2 21 2024-08-03 2024-08-05
5 2 20 2024-07-29 2024-08-04
"""
df = pd.read_csv(StringIO(data), sep='\t') #, parse_dates=['date_from', 'date_to'])
result = df.groupby(["product_id", "store_id"], group_keys=False) \
.apply(mark_overlaps) \
.reset_index(drop=True)
Результат:
product_id | store_id | promo_id | date_from | date_to | overlap_row | has_overlap | |
---|---|---|---|---|---|---|---|
0 | 1 | 1 | 8 | 2024-01-01 | 2024-01-07 | False | False |
1 | 1 | 2 | 9 | 2024-01-08 | 2024-01-14 | False | False |
2 | 2 | 1 | 1 | 2024-02-19 | 2024-02-25 | False | False |
3 | 2 | 2 | 10 | 2023-03-13 | 2023-03-19 | False | False |
4 | 3 | 1 | 3 | 2023-03-20 | 2023-03-26 | False | False |
5 | 3 | 2 | 11 | 2023-05-08 | 2023-05-14 | False | False |
6 | 4 | 1 | 12 | 2024-02-12 | 2024-02-18 | False | False |
7 | 4 | 2 | 13 | 2024-04-01 | 2024-04-07 | False | False |
8 | 5 | 1 | 14 | 2023-07-03 | 2023-07-09 | False | False |
9 | 5 | 2 | 20 | 2024-07-29 | 2024-08-04 | False | True |
10 | 5 | 2 | 21 | 2024-08-03 | 2024-08-05 | True | True |
Пояснения:
overlap_row
- диапазон дат в этой строке пересекается с предыдущим в данной группеhas_overlap
- в данной группе магазин & продукт есть пересечения