Панды — Создайте столбец фрейма данных и заполните значения из выходного столбца на основе даты и категории

#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 незначительных изменения:

  1. сгруппировать по еще одному полю на item
  2. добавьте параметр sort=False , groupby() чтобы убедиться, что исходный заказ сохранен (сначала за последний год).
  3. используйте 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