#pandas
#pandas
Вопрос:
Я хочу выбирать все записи за предыдущие 6 месяцев для клиента всякий раз, когда клиент выполняет конкретную транзакцию. Данные выглядят следующим образом:
Cust_ID Transaction_Date Amount Description
1 08/01/2017 12 Moved
1 03/01/2017 15 X
1 01/01/2017 8 Y
2 10/01/2018 6 Moved
2 02/01/2018 12 Z
Здесь я хочу увидеть описание «Перемещено», а затем выбрать все последние 6 месяцев для каждого Cust_ID.
Вывод должен выглядеть следующим образом:
Cust_ID Transaction_Date Amount Description
1 08/01/2017 12 Moved
1 03/01/2017 15 X
2 10/01/2018 6 Moved
Я хочу сделать это на python. Пожалуйста, помогите.
Комментарии:
1. значит
08/01/2017
08 — это число, а 01 — месяц ryt?2. Почему в ожидаемом наборе результатов есть строка с описанием «X» для описания «Перемещено»? Где твоя попытка?
3. Есть только один
Moved
для каждой группы?4. @anky_91 да, верно
Ответ №1:
Идея создается Series
из datetimes
отфильтрованных Moved
и сдвинутых MonthOffset
значений, последний фильтр по Series.map
значениям, менее подобным этому, смещает:
РЕДАКТИРОВАТЬ: получить все даты и времени для каждого Moved
значения:
df['Transaction_Date'] = pd.to_datetime(df['Transaction_Date'])
df = df.sort_values(['Cust_ID','Transaction_Date'])
df['g'] = df['Description'].iloc[::-1].eq('Moved').cumsum()
s = (df[df['Description'].eq('Moved')]
.set_index(['Cust_ID','g'])['Transaction_Date'] - pd.offsets.MonthOffset(6))
mask = df.join(s.rename('a'), on=['Cust_ID','g'])['a'] < df['Transaction_Date']
df1 = df[mask].drop('g', axis=1)
ПРАВКА1: Получить все даты для Moved
с минимальными датами для групп, другие Moved
для групп удаляются:
print (df)
Cust_ID Transaction_Date Amount Description
0 1 10/01/2017 12 X
1 1 01/23/2017 15 Moved
2 1 03/01/2017 8 Y
3 1 08/08/2017 12 Moved
4 2 10/01/2018 6 Moved
5 2 02/01/2018 12 Z
#convert to datetimes
df['Transaction_Date'] = pd.to_datetime(df['Transaction_Date'])
#mask for filter Moved rows
mask = df['Description'].eq('Moved')
#filter and sorting this rows
df1 = df[mask].sort_values(['Cust_ID','Transaction_Date'])
print (df1)
Cust_ID Transaction_Date Amount Description
1 1 2017-01-23 15 Moved
3 1 2017-08-08 12 Moved
4 2 2018-10-01 6 Moved
#get duplicated filtered rows in df1
mask = df1.duplicated('Cust_ID')
#create Series for map
s = df1[~mask].set_index('Cust_ID')['Transaction_Date'] - pd.offsets.MonthOffset(6)
print (s)
Cust_ID
1 2016-07-23
2 2018-04-01
Name: Transaction_Date, dtype: datetime64[ns]
#create mask for filter out another Moved (get only first for each group)
m2 = ~mask.reindex(df.index, fill_value=False)
df1 = df[(df['Cust_ID'].map(s) < df['Transaction_Date']) amp; m2]
print (df1)
Cust_ID Transaction_Date Amount Description
0 1 2017-10-01 12 X
1 1 2017-01-23 15 Moved
2 1 2017-03-01 8 Y
4 2 2018-10-01 6 Moved
ПРАВКА2:
#get last duplicated filtered rows in df1
mask = df1.duplicated('Cust_ID', keep='last')
#create Series for map
s = df1[~mask].set_index('Cust_ID')['Transaction_Date']
print (s)
Cust_ID
1 2017-08-08
2 2018-10-01
Name: Transaction_Date, dtype: datetime64[ns]
m2 = ~mask.reindex(df.index, fill_value=False)
#filter by between Moved and next 6 months
df3 = df[df['Transaction_Date'].between(df['Cust_ID'].map(s), df['Cust_ID'].map(s pd.offsets.MonthOffset(6))) amp; m2]
print (df3)
Cust_ID Transaction_Date Amount Description
3 1 2017-08-08 12 Moved
0 1 2017-10-01 12 X
4 2 2018-10-01 6 Moved
Комментарии:
1. Это хороший вариант, но в некоторых случаях он завершится неудачей, если у нас будет несколько
Moved
событий. Я обновил свой пример на случай, когда этот подход не работает (см. Обновление). В моем примере с вашим кодом мы бы отобразили2017-03-01
событие, хотя оно не предшествует ни однойMoved
записи с окном за 6 месяцев (следующимMoved
является2017-10-01
)2. @perl — я, это зависит от того, что нужно op. К сожалению, фильтрация по группам возможна, но медленная…
3. Я думаю, что нашел быстрый способ (мне нужно только
bfill
сгруппировать данные) и правильно обрабатывает эти случаи. Обновил мой ответ этим4. @jezrael Привет, 1 последний question…in приведенный выше сценарий, если мне нужно извлечь данные за 6 месяцев… что все, что мне нужно сделать в коде «РЕДАКТИРОВАТЬ». Заранее спасибо.
5. @AnkitaPatnaik — Я думаю, нужно изменить только
- pd.offsets.MonthOffset(6)
наpd.offsets.MonthOffset(6)