Прокручивающееся окно Pandas, которое пропускает дубликаты

У меня есть следующий фрейм данных Pandas, который состоит из столбца Member и SaleDate. Я разработал дополнительный столбец для представления начала месяца для каждой строки.

a = pd.DataFrame({'Member':['A','A','A','A','A','A','B','B','B','B','B'],
'SaleDate': ['2020-01-01','2020-02-01','2020-02-29','2020-02-14','2020-03-31','2020-04-30','2020-02-29','2020-03-31','2020-04-30','2020-05-30','2020-06-14']})

a['SaleDate'] = a['SaleDate'].apply(pd.to_datetime)
a['Date_Month_Start'] = a['SaleDate'].values.astype('datetime64[M]')

Я использовал следующее, чтобы найти дату следующей транзакции для каждого члена, которая работает нормально.

a['last_tx'] = a.sort_values(by=['SaleDate'], ascending= True).groupby(['Member'])['SaleDate'].shift(1)
a['next_tx'] = a.sort_values(by=['SaleDate'], ascending= True).groupby(['Member'])['SaleDate'].shift(-1)

Теперь я хочу, чтобы для каждой строки был найден последний и следующий месяц транзакции, но

a['last_month_tx'] = a.sort_values(by=['Date_Month_Start'], ascending= True).groupby(['Member'])['Date_Month_Start'].shift(1)
a['next_month_tx'] = a.sort_values(by=['Date_Month_Start'], ascending= True).groupby(['Member'])['Date_Month_Start'].shift(-1)

не удаляет дубликаты значений в столбце Date_Month_Start.

Я использовал следующее для решения проблемы, но мне было интересно, есть ли более элегантное решение этой проблемы без создания дополнительного фрейма данных и его объединения.

a_month = a[['Member','Date_Month_Start']].drop_duplicates()
a_month['last_month_tx'] = a.sort_values(by=['Date_Month_Start'], ascending= True).groupby(['Member'])['Date_Month_Start'].shift(1)
a_month['next_month_tx'] = a.sort_values(by=['Date_Month_Start'], ascending= True).groupby(['Member'])['Date_Month_Start'].shift(-1)


result = pd.merge(a,a_month,how = 'left', left_on=['Member','Date_Month_Start'], right_on =['Member','Date_Month_Start'])
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
134
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, то, как вы это сделали, достаточно элегантно. Я не смог найти способ не использовать drop_duplicates, но мы можем использовать его внутри функции groupby (так что вам не нужно создавать новую переменную с кадром данных).

# First, sort the date values per group at once
a = a.groupby('Member').apply(lambda x: x.sort_values(['Date_Month_Start','SaleDate'])).reset_index(drop=True)

# Then, perform the calculations by Member:
a[['last_tx','next_tx','last_month_tx','next_month_tx']] = (
    a.groupby('Member')
    .apply(lambda x: pd.DataFrame({
        'last_tx'       : x['SaleDate'].shift(),
        'next_tx'       : x['SaleDate'].shift(-1),
        'last_month_tx' : x['Date_Month_Start'].drop_duplicates().shift().reindex(x.index).ffill(),
        'next_month_tx' : x['Date_Month_Start'].drop_duplicates().shift(-1).reindex(x.index[:-1]).ffill().reindex(x.index)
    })
))

Выход:

   Member   SaleDate Date_Month_Start    last_tx    next_tx last_month_tx next_month_tx
0       A 2020-01-01       2020-01-01        NaT 2020-02-01           NaT    2020-02-01
1       A 2020-02-01       2020-02-01 2020-01-01 2020-02-29    2020-01-01    2020-03-01
2       A 2020-02-29       2020-02-01 2020-02-01 2020-02-14    2020-01-01    2020-03-01
3       A 2020-02-14       2020-02-01 2020-02-29 2020-03-31    2020-01-01    2020-03-01
4       A 2020-03-31       2020-03-01 2020-02-14 2020-04-30    2020-02-01    2020-04-01
5       A 2020-04-30       2020-04-01 2020-03-31        NaT    2020-03-01           NaT
6       B 2020-02-29       2020-02-01        NaT 2020-03-31           NaT    2020-03-01
7       B 2020-03-31       2020-03-01 2020-02-29 2020-04-30    2020-02-01    2020-04-01
8       B 2020-04-30       2020-04-01 2020-03-31 2020-05-30    2020-03-01    2020-05-01
9       B 2020-05-30       2020-05-01 2020-04-30 2020-06-14    2020-04-01    2020-06-01
10      B 2020-06-14       2020-06-01 2020-05-30        NaT    2020-05-01           NaT

Здесь я использовал reindex для воссоздания потерянных индексов с помощью sort_values, а затем заполнил NaN соответственно. Поскольку мы не можем заполнить все NaN для следующей даты с помощью прямого заполнения (поскольку последняя дата не имеет следующей даты), я дважды переиндексировал ее (в первый раз я исключил последнюю строку группы).

Другие вопросы по теме