Вопрос или проблема
У меня есть проблема с следующими данными:
месяц | продукт | версия | цена | категория |
---|---|---|---|---|
1 | A | alpha | 500 | foo |
2 | A | alpha | 500 | foo |
3 | A | alpha | 550 | foo |
4 | A | alpha | 600 | bar |
5 | A | alpha | 500 | bar |
6 | A | alpha | 500 | faz |
7 | A | alpha | 500 | faz |
8 | A | alpha | 500 | faz |
9 | A | alpha | 500 | faz |
10 | A | alpha | 500 | foo |
4 | A | beta | 700 | bar |
5 | A | beta | 800 | bar |
6 | A | beta | 800 | faz |
7 | A | beta | 800 | faz |
8 | A | beta | 800 | faz |
9 | A | beta | 700 | faz |
10 | A | beta | 500 | foo |
1 | B | alpha | 200 | foo |
2 | B | alpha | 200 | foo |
3 | B | alpha | 250 | foo |
4 | B | alpha | 250 | bar |
5 | B | alpha | 250 | bar |
6 | B | alpha | 250 | faz |
7 | B | alpha | 250 | faz |
8 | B | alpha | 250 | faz |
9 | B | alpha | 500 | faz |
10 | B | alpha | 500 | foo |
4 | B | beta | 700 | bar |
5 | B | beta | 800 | bar |
6 | B | beta | 800 | faz |
7 | B | beta | 800 | faz |
8 | B | beta | 800 | faz |
9 | B | beta | 700 | faz |
10 | B | beta | 500 | foo |
Что я хотел бы сделать, так это сгруппировать по продуктах и версиям, и отметить строки, где цена остается неизменной, НО категория меняется с предыдущего месяца для каждой группы продукт+версия. Кроме того, я хочу отметить все строки после первого изменения категории, где цена соответствует последней строке предыдущей категории.
Я пробовал подходы в SQL с LEAD и LAG, а также CASE WHEN exists, но не смог получить логику, которая работала бы так, как нужно. Мои подходы смогли отметить один следующий месяц, но не несколько месяцев после изменения категории.
Я также открыт к реализации решения на Python.
Столбец меток ниже представляет желаемый вывод.
месяц | продукт | версия | цена | категория | метка |
---|---|---|---|---|---|
1 | A | alpha | 500 | foo | OK |
2 | A | alpha | 500 | foo | OK |
3 | A | alpha | 550 | foo | OK |
4 | A | alpha | 600 | bar | OK |
5 | A | alpha | 500 | bar | OK |
6 | A | alpha | 500 | faz | FLAG |
7 | A | alpha | 500 | faz | FLAG |
8 | A | alpha | 500 | faz | FLAG |
9 | A | alpha | 500 | faz | FLAG |
10 | A | alpha | 500 | foo | FLAG |
4 | A | beta | 700 | bar | OK |
5 | A | beta | 800 | bar | OK |
6 | A | beta | 800 | faz | FLAG |
7 | A | beta | 800 | faz | FLAG |
8 | A | beta | 800 | faz | FLAG |
9 | A | beta | 700 | faz | OK |
10 | A | beta | 500 | foo | OK |
1 | B | alpha | 200 | foo | OK |
2 | B | alpha | 200 | foo | OK |
3 | B | alpha | 250 | foo | OK |
4 | B | alpha | 250 | bar | FLAG |
5 | B | alpha | 250 | bar | FLAG |
6 | B | alpha | 250 | faz | FLAG |
7 | B | alpha | 250 | faz | FLAG |
8 | B | alpha | 250 | faz | FLAG |
9 | B | alpha | 500 | faz | OK |
10 | B | alpha | 500 | foo | OK |
4 | B | beta | 700 | bar | OK |
5 | B | beta | 800 | bar | OK |
6 | B | beta | 800 | faz | FLAG |
7 | B | beta | 800 | faz | FLAG |
8 | B | beta | 800 | faz | FLAG |
9 | B | beta | 700 | faz | OK |
10 | B | beta | 500 | foo | OK |
Я попробовал этот подход:
WITH cte AS ( SELECT , ROW_NUMBER() OVER (PARTITION BY product, version ORDER BY month ASC) AS rn FROM table ),
flux AS ( SELECT c., CASE WHEN EXISTS( SELECT 1 FROM cte c2 WHERE (c.product = c2.product AND c.version = c2.version) AND c2.rn < c.rn AND c2.price BETWEEN -1000 AND 1000 AND c.category != c2.category ) THEN 1 ELSE 0 END AS tag FROM cte c ORDER BY month )
SELECT * FROM flux
Ответ или решение
Для решения вашей задачи можно использовать Python с библиотекой pandas, что является гибким и мощным инструментом для обработки данных. Ниже я приведу детальный шаг за шагом метод, который поможет вам добавить столбец с тегами в ваш набор данных в зависимости от условия изменения категории при неизменной цене.
Шаг 1: Подготовка данных
Прежде всего, вам необходимо импортировать необходимые библиотеки и создать DataFrame из ваших данных:
import pandas as pd
data = {
'month': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
4, 5, 6, 7, 8, 9, 10,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
4, 5, 6, 7, 8, 9, 10],
'product': ['A']*10 + ['A']*7 + ['B']*10 + ['B']*7,
'version': ['alpha']*10 + ['beta']*7 + ['alpha']*10 + ['beta']*7,
'price': [500, 500, 550, 600, 500, 500, 500, 500, 500, 500,
700, 800, 800, 800, 800, 700, 500,
200, 200, 250, 250, 250, 250, 250, 250, 250, 500,
700, 800, 800, 800, 800, 700, 500],
'category': ['foo', 'foo', 'foo', 'bar', 'bar', 'faz', 'faz', 'faz', 'faz', 'foo',
'bar', 'bar', 'faz', 'faz', 'faz', 'faz', 'foo',
'foo', 'foo', 'foo', 'bar', 'bar', 'faz', 'faz', 'faz', 'faz', 'faz', 'foo',
'bar', 'bar', 'faz', 'faz', 'faz', 'faz', 'foo']
}
df = pd.DataFrame(data)
Шаг 2: Логика тегирования
Теперь мы реализуем логику для тегирования:
# Добавляем столбец "tag" с предначальным значением "OK"
df['tag'] = 'OK'
# Определяем функцию для применения логики тегирования
def tag_rows(group):
# Сохраняем предыдущее значение цены и категории
prev_price = None
prev_category = None
for index, row in group.iterrows():
if (prev_price is not None and
row['price'] == prev_price and
row['category'] != prev_category):
# Если цена не изменилась, но категория изменилась, тегируем
group.loc[index, 'tag'] = 'FLAG'
# Обновляем предыдущее значение
prev_price = row['price']
prev_category = row['category']
return group
# Применяем функцию по группам "product" и "version"
df = df.groupby(['product', 'version'], as_index=False).apply(tag_rows)
Шаг 3: Проверка результата
Теперь вы можете проверить ваш DataFrame:
print(df)
Шаг 4: Объяснение работы кода
- Импортируем необходимых библиотеки: Используем pandas для работы с табличными данными.
- Создаем DataFrame: Инициализируем DataFrame из ваших данных.
- Добавляем столбец ‘tag’: Устанавливаем ему предначальное значение ‘OK’.
- Создаем функцию ‘tag_rows’: Проходим по каждой группе (по продукту и версии) и сравниваем текущую строку с предыдущей. Если цена не изменилась, но категория изменилась, ставим тег ‘FLAG’.
- Применяем функцию: Группируем DataFrame по продукту и версии, применяем функцию для тегирования.
Таким образом, вы получаете DataFrame с нужными тегами, отвечающими вашему запросу.