Содержание
Цена на момент заказа: сопоставьте историю через merge_asof
Для сверки цены заказа с прайсом нужна запись, действовавшая в момент заказа. Текущая цена и ближайшая по времени будущая запись могут дать неверную базу сравнения. Ниже метод для аналитической копии данных, когда история действительно содержит время начала действия каждой цены.
Подготовьте две таблицы
В orders нужны уникальный идентификатор строки заказа order_line_id, sku и ordered_at. В prices нужны sku, effective_at и price. Если цены различаются по магазину или валюте, добавьте эти поля в обе таблицы и в ключ группировки. Сопоставлять рублёвую цену одного магазина с другим предложением нельзя.
Проверьте смысл времени: момент загрузки файла не обязательно равен началу действия цены. Приведите временные поля к одному часовому поясу и типу. Строки с пропусками в ключах вынесите в отдельный отчёт. Для одинаковых sku и effective_at сначала разрешите конфликт источников; порядок строк не должен решать, какую цену считать правильной.
Ищите последнюю действовавшую запись
В документации pandas режим backward выбирает последнюю правую запись с ключом не позже левой. Параметр by ограничивает поиск группой, а allow_exact_matches разрешает совпадение времени. Обе таблицы должны быть отсортированы по временному ключу по возрастанию.
import pandas as pd
# Здесь обе таблицы уже очищены, время приведено к одному типу.
assert orders['order_line_id'].is_unique
assert not prices.duplicated(['sku', 'effective_at']).any()
result = pd.merge_asof(
orders.sort_values('ordered_at'),
prices.sort_values('effective_at'),
left_on='ordered_at',
right_on='effective_at',
by='sku',
direction='backward',
allow_exact_matches=True,
)
assert len(result) == len(orders)
missing = result[result['effective_at'].isna()]
matched = result[result['effective_at'].notna()]
assert (matched['effective_at'] <= matched['ordered_at']).all()
Сортировка только внутри каждого SKU недостаточна, если весь временной столбец при переходе между группами снова убывает. В коде сортировка выполнена непосредственно по времени.
Отделите отсутствие истории от расхождения цены
Строки missing означают, что подходящей предшествующей записи нет. Не подставляйте в них ноль или текущий прайс: запросите более раннюю историю либо оставьте результат неопределённым.
Если бизнес-правило ограничивает допустимую давность записи, задайте tolerance после согласования этого интервала. Без такого ограничения старый прайс может корректно действовать долго. Сам возраст записи ещё не доказывает ошибку.
Для ручной проверки выберите реальные заказы до изменения цены, ровно на границе и после него. Сверьте найденный effective_at с первичной историей. Если новая цена применяется строго после указанного времени, правило точного совпадения надо изменить осознанно.
Что означает результат
Полученная price является ценой из выбранной истории. Она не обязана равняться фактической цене продажи: акции, скидки и состав показателя нужно проверять отдельно. Сохраните исходные версии файлов, временной пояс, ключи и нерешённые строки. Только после этого используйте сопоставление для разбора конкретных расхождений.
Комментарии 0