Я застрял в получении последних, предпоследних и предпоследних месячных значений переменной в python, ДАННЫХ контрольной датой. я У меня есть два фрейма данных:
В этом кадре данных клиенты могут появляться более одного раза (поэтому есть некоторые клиенты, у которых есть более одной контрольной даты).
И другие:
Итак, мой желаемый результат:
Я знаю, что это можно получить с помощью функций numpy и pandas, но я не могу найти решение...
Сначала фильтруем строки после df2['date']
по df1['reference_date']
per id_cliente
по Series.map , сравниваем на большее по Series.gt и фильтруем по логическому индексированию, затем сортируем по DataFrame.sort_values для правильного порядка:
s = df1.set_index('id_cliente')['reference_date']
df = (df2[df2['id_client'].map(s).gt(df2['date'])]
.sort_values(['id_client','date'], ascending=[True, False]))
Затем создайте вспомогательный столбец по счетчику с помощью GroupBy.cumcount для фильтрации 3 строк, а также используйте его для поворота с помощью DataFrame.pivot:
df['g'] = df.groupby('id_client').cumcount()
df = df[df['g'].lt(3)].copy()
df = df.pivot('id_client','g','sales')
Последним установите имена новых столбцов, добавьте reference_date
путем сопоставления и конвертируйте индекс в столбец:
df.columns = ['last_date','penultimate_date','antepenultimate_date']
df = df.reset_index()
df.insert(0,'reference_date', df['id_client'].map(s))
print (df)
reference_date id_client last_date penultimate_date antepenultimate_date
0 201504 A8781 3200 6500 3400
1 201706 B7638 100 200 300
Обновлено: Аналогичный ответ для нескольких id_cliente
- вместо map
используется DataFrame.merge , а для различения дубликатов id_cliente
используется GroupBy.cumcount:
df1['groups'] = df1.groupby('id_cliente').cumcount()
df = df2.merge(df1, left_on='id_client', right_on='id_cliente')
df = (df[df['reference_date'].gt(df['date'])]
.sort_values(['groups','id_client','date'], ascending=[True, True, False])
)
df['g'] = df.groupby(['id_client','groups']).cumcount()
df = df[df['g'].lt(3)].copy()
df = df.pivot(['groups','id_client'],'g','sales')
print (df)
df.columns = ['last_date','penultimate_date','antepenultimate_date']
df = df1.join(df, on=['groups','id_cliente']).drop('groups', axis=1)
print (df)
reference_date id_cliente last_date penultimate_date \
0 201504 A8781 3200 6500
1 201706 B7638 100 200
2 201401 A8781 3 2
antepenultimate_date
0 3400
1 300
2 1
@GusCR - это означает, что id_cliente
дублируются, при необходимости максимальное date
за id_cliente
изменение s = df1.set_index('id_cliente')['reference_date']
на s = df1.groupby('id_cliente')['reference_date'].max()
@jezreael Мне действительно это нужно для всех клиентов, независимо от того, дублируется он или нет ... (клиент может быть дублирован, но не комбинация id_client и reference _date)
@GusCR - я думаю, изменить образец данных, чтобы увидеть, как изменить мое решение.
Давайте продолжим обсуждение в чате.
Предполагая структуру фреймов данных в ваших примерах -
Агрегируйте второй фрейм данных в столбце date
и выберите n-е записи. Вы можете использовать .groupby.nth()
с .pivot()
для этой задачи.
n_obs = 3 # number of observations per item
grouped = df2[['id_client', 'sales']].groupby('id_client').nth(list(range(n_obs))
# Reformat the table to desired shape for last 3 values, change
grouped.assign(row_num=grouped.index%n_obs).pivot(
index='id_client', columns='row_num', values='sales').rename_axis(
'', axis=1).reset_index().rename(
columns = {0:'last', 1:'penulltimate', 2:'antepenultimate'})
Выход:
id_client last penulltimate antepenultimate
0 A8781 3200 6500 3400
1 B7638 300 200 100
Теперь вы можете объединить это с исходным фреймом данных в поле id_client.
См.: GroupBy nth
Спасибо за ваше время. Это не решение. Мне нужно найти последние 3 значения с учетом исходной даты каждого клиента...
спасибо за Ваш ответ! Это дает мне следующую ошибку после выполнения первых двух строк: переиндексация действительна только для объектов индекса с уникальным значением.