#python #pandas
Вопрос:
У меня есть фрейм данных, который показывает продажи по товару в магазине, он выглядит так:
date item storeNbr Sales
2021-06-29 soap 123 100
2021-05-29 hat 129 500
2020-06-29 soap 123 0
2020-05-29 hat 129 10
Я пытаюсь создать столбец для продаж за прошлый год, который должен принимать значения, которые
уже существуют в кадре данных, где дата равна предыдущему году и где
номер магазина и товар совпадают. Так что это должно выглядеть так:
date item storeNbr Sales LY
2021-06-29 soap 123 100 0
2021-05-29 hat 129 500 10
2020-06-29 soap 123 0 Nan
2020-05-29 hat 129 10 Nan
Я пробовал это:
df['Previous'] =
df.groupby([df['date'].dt.month,df['date'].dt.day,df['StoreNbr']])
['Sales'].shift()
но у меня возникают проблемы с получением желаемого результата. Заранее благодарим вас за любую помощь здесь!
Комментарии:
1. Вы могли бы просто
.shift(-1)
получить предыдущее значение вместо следующего. (предполагая, что ваши годы отсортированы в порядке убывания, как в примере)
Ответ №1:
Примеры данных:
import pandas as pd
from pandas import Timestamp
df = pd.DataFrame({'date': {0: Timestamp('2021-06-29 00:00:00'), 1: Timestamp('2021-05-29 00:00:00'), 2: Timestamp('2020-06-29 00:00:00'), 3: Timestamp('2020-05-29 00:00:00')}, 'item': {0: 'soap', 1: 'hat', 2: 'soap', 3: 'hat'}, 'storeNbr': {0: 123, 1: 129, 2: 123, 3: 129}, 'Sales': {0: 100, 1: 500, 2: 0, 3: 10}})
Код:
# create copy of your data, but add 1 year from the date, then merge.
df2 = df.copy()
df2['date'] = df2['date'] pd.DateOffset(years=1)
df['LY'] = df.drop('Sales', axis=1).merge(df2, on=['date', 'item', 'storeNbr'])['Sales']
Выход:
date item storeNbr Sales LY
0 2021-06-29 soap 123 100 0.0
1 2021-05-29 hat 129 500 10.0
2 2020-06-29 soap 123 0 NaN
3 2020-05-29 hat 129 10 NaN
Однострочное предложение от @ScottBoston
df.merge(df.assign(date = df['date'] pd.DateOffset(years=1)),
on=['date','item','storeNbr'],
how='left',
suffixes=('','_y'))
.rename(columns={'Sales_y':'LY'})
Комментарии:
1. Не возражаете, если обновите свой ответ? Пожалуйста, удалите правки, если хотите.
2. @ScottBoston, хорошее редактирование и использование assign! Однако один вопрос: не было бы короче и проще для чтения, если бы вы заменили часть суффикса/переименования на .drop() в первом df вместо этого?
df.drop('Sales', axis=1).merge(...)
3. ДА. Вы тоже можете так поступить.
Ответ №2:
Если вы сначала разберетесь с этим, вы можете сделать групповую проверку и сдвиг.
df = df.sort_values(by=['item','date'])
df['LY'] = df.groupby('item')['Sales'].shift()
Выход
date item storeNbr Sales LY
3 2020-05-29 hat 129 10 NaN
1 2021-05-29 hat 129 500 10.0
2 2020-06-29 soap 123 0 NaN
0 2021-06-29 soap 123 100 0.0
Ответ №3:
Ваш код близок, всего 3 незначительных изменения:
- сгруппировать по еще одному полю на
item
- добавьте параметр
sort=False
,groupby()
чтобы убедиться, что исходный заказ сохранен (сначала за последний год). - используйте
shift(-1)
, чтобы получить значение «следующего» значения, вместоshift()
которого получает предыдущее значение.
df['LY'] = df.groupby([df['date'].dt.month ,df['date'].dt.day , df['storeNbr'], df['item']], sort=False)['Sales'].shift(-1)
Результат:
print(df)
date item storeNbr Sales LY
0 2021-06-29 soap 123 100 0.0
1 2021-05-29 hat 129 500 10.0
2 2020-06-29 soap 123 0 NaN
3 2020-05-29 hat 129 10 NaN