Это сложный вопрос, когда я хочу улучшить производительность кода. Представьте себе такой кадр данных:
TOUR_ID ID PAGE_ID CREATED DATE AVAILABILITY
T_1 ID1 P1 2018-07-03 19:10:19 AVAILABLE
T_1 ID1 P1 2018-07-03 19:10:20 AVAILABLE
T_1 ID1 P2 2018-07-03 19:12:33 AVAILABLE
T_1 ID2 P3 2018-07-03 19:13:34 AVAILABLE
T_1 ID2 P3 2018-07-03 19:13:35 NOT AVAILABLE
T_1 ID2 P4 2018-07-03 19:16:24 AVAILABLE
T_2 ID3 P4 2018-07-03 18:23:19 AVAILABLE
T_2 ID3 P4 2018-07-03 18:23:20 NOT AVAILABLE
T_2 ID1 P1 2018-07-03 19:10:21 NOT AVAILABLE
T_2 ID2 P3 2018-07-03 19:13:37 NOT AVAILABLE
T_2 ID2 P3 2018-07-03 19:13:38 NOT AVAILABLE
T_2 ID3 P5 2018-07-03 20:56:33 AVAILABLE
T_2 ID3 P5 2018-07-03 20:56:34 NOT AVAILABLE
T_2 ID3 P5 2018-07-03 22:56:35 AVAILABLE
T_2 ID3 P6 2018-07-03 22:57:20 NOT AVAILABLE
T_2 ID3 P7 2018-07-03 22:58:35 AVAILABLE
T_2 ID4 P8 2018-07-03 22:59:00 AVAILABLE
T_2 ID1 P1 2018-07-03 23:12:00 AVAILABLE
T_2 ID1 P3 2018-07-03 23:32:00 AVAILABLE
В каждой группе (Tour_ID, ID, Page_ID) мне нужно создать столбец с последним значением предыдущей группы. Кроме того, в первый раз, когда tour_ID или ID меняются, я получаю NaNs, потому что для этой комбинации нет никаких предыдущих значений.
Результат должен выглядеть следующим образом:
TOUR_ID ID PAGE_ID CREATED DATE AVAILABILITY PREVIOUS AVAILABILITY
T_1 ID1 P1 2018-07-03 19:10:19 AVAILABLE NaN
T_1 ID1 P1 2018-07-03 19:10:20 AVAILABLE NaN
T_1 ID1 P2 2018-07-03 19:12:33 AVAILABLE AVAILABLE
T_1 ID2 P3 2018-07-03 19:13:34 AVAILABLE NaN
T_1 ID2 P3 2018-07-03 19:13:35 NOT_AVAILABLE NaN
T_1 ID2 P4 2018-07-03 19:16:24 AVAILABLE NOT_AVAILABLE
T_2 ID3 P4 2018-07-03 18:23:19 AVAILABLE NaN
T_2 ID3 P4 2018-07-03 18:23:20 NOT AVAILABLE NaN
T_2 ID1 P1 2018-07-03 19:10:21 NOT AVAILABLE NaN
T_2 ID2 P3 2018-07-03 19:13:37 NOT AVAILABLE NaN
T_2 ID2 P3 2018-07-03 19:13:38 NOT AVAILABLE NaN
T_2 ID3 P5 2018-07-03 20:56:33 AVAILABLE NOT AVAILABLE
T_2 ID3 P5 2018-07-03 20:56:34 NOT AVAILABLE NOT AVAILABLE
T_2 ID3 P5 2018-07-03 22:56:35 AVAILABLE NOT AVAILABLE
T_2 ID3 P6 2018-07-03 22:57:20 NOT AVAILABLE AVAILABLE
T_2 ID3 P7 2018-07-03 22:58:35 AVAILABLE NOT AVAILABLE
T_2 ID4 P8 2018-07-03 22:59:00 AVAILABLE NaN
T_2 ID1 P1 2018-07-03 23:12:00 AVAILABLE NaN
T_2 ID1 P3 2018-07-03 23:32:00 AVAILABLE AVAILABLE
У меня есть код, который работает, но он не масштабируется (блок данных имеет около 900 000). Любая помощь в улучшении производительности кода была бы должным образом оценена.
Вот что у меня есть до сих пор:
for current_op in df.TOUR_ID.unique():
dummy = df[df.TOUR_ID == current_op].ID.unique()
for current_ID in dummy:
dummy_m = df[(df.TOUR_ID == current_op) & (df.ID == current_ID)].PAGE_ID.unique()
for current_page in dummy_m:
mask = (df.TOUR_ID == current_op) & (df.ID == current_ID) & (df.PAGE_ID == current_page)
indexes = mask.reset_index().rename(columns ={0:'Bool'})
ind = indexes.index[indexes['Bool'] == True].tolist()[0]
if (ind == 0) | ((current_page == dummy_m[0])):
df.loc[mask,'Previous_availability'] = np.nan
else:
previous_aval = df.AVAILABILITY.loc[indexes['index'].loc[ind-1]]
df.loc[mask, 'Previous_availability'] = previous_aval
Примечание: NaN в конечном итоге будет сброшен
-- Редактировать
Ниже приведен код для создания фрейма данных:
import pandas as pd
import numpy as np
df = pd.DataFrame([['T_1','ID1','P1','2018-07-03 19:10:19', 'AVAILABLE'],
['T_1','ID1','P1','2018-07-03 19:10:20', 'AVAILABLE'],
['T_1','ID1','P2','2018-07-03 19:12:33', 'AVAILABLE'],
['T_1','ID2','P3','2018-07-03 19:13:34', 'AVAILABLE'],
['T_1','ID2','P3','2018-07-03 19:13:35', 'NOT AVAILABLE'],
['T_1','ID2','P4','2018-07-03 19:16:24', 'AVAILABLE'],
['T_2','ID3','P4','2018-07-03 18:23:19', 'AVAILABLE'],
['T_2','ID3','P4','2018-07-03 18:23:20', 'NOT AVAILABLE'],
['T_2','ID1','P1','2018-07-03 19:10:21', 'NOT AVAILABLE'],
['T_2','ID2','P3','2018-07-03 19:13:36', 'NOT AVAILABLE'],
['T_2','ID2','P3','2018-07-03 19:13:37', 'NOT AVAILABLE'],
['T_2','ID3','P5','2018-07-03 20:56:33', 'AVAILABLE'],
['T_2','ID3','P5','2018-07-03 20:56:34', 'NOT AVAILABLE'],
['T_2','ID3','P5','2018-07-03 22:56:35', 'AVAILABLE'],
['T_2','ID3','P6','2018-07-03 22:57:20', 'NOT AVAILABLE'],
['T_2','ID3','P7','2018-07-03 22:58:35', 'AVAILABLE'],
['T_2','ID4','P8','2018-07-03 22:59:00', 'AVAILABLE'],
['T_2','ID1','P1','2018-07-03 23:12:00', 'AVAILABLE'],
['T_2','ID1','P3','2018-07-03 23:32:00', 'AVAILABLE']
], columns=['TOUR_ID','ID','PAGE_ID','CREATED DATE', 'AVAILABILITY'])
Это был довольно важный руководитель, но здесь один из способов решить эту проблему:
df = pd.read_csv('test.tsv').set_index(['TOUR_ID', 'ID', 'PAGE_ID'])
Получить последнюю строку каждой группы, сдвинуть вперед на один:
shifted = df.groupby(['TOUR_ID', 'ID', 'PAGE_ID']).last().shift(1).reset_index()
Теперь нас интересуют строки, где мы видим изменения в PAGE_ID
но не в ID
, поэтому мы строим логическую маску:
change = shifted != shifted.shift(1)
mask = np.array(change.PAGE_ID & ~change.ID & ~change.TOUR_ID)
Наконец, мы применяем маску и объединяемся для создания нового столбца:
shifted.set_index(['TOUR_ID', 'ID', 'PAGE_ID'], inplace=True)
shifted[~mask] = np.nan
result = df.join(shifted['AVAILABILITY'], rsuffix='LAST')
TOUR_ID
, в дополнение к ID
? Добавление ~change.TOUR_ID
к конструкции маски должно учитывать это. Я отредактировал свой ответ соответственно.
Хорошо, вот мой удар.
1) Создать вспомогательную серию P_INT
(целая часть PAGE_ID
)
2) Создайте вспомогательный DataFrame df_last_availability
с помощью MultiIndex ['TOUR_ID', 'ID', 'P_INT']
3) Смещение P_INT
на 1
4) Сбросьте индекс исходного df, чтобы он соответствовал df_last_availability
. Отсюда вы можете легко объединить (используя левое соединение) 2 DataFrames на индекс.
5) Последние прикованные методы - это просто очистка, чтобы вернуть рамку данных обратно в исходную форму - то есть передать вспомогательное поле и вернуть индекс обратно к нему.
df['P_INT'] = df.PAGE_ID.str.extract('(\d+)').astype(int)
df_last_availability = df.groupby(['TOUR_ID', 'ID', 'P_INT']).last()
df['P_INT'] = df.P_INT - 1
(df.set_index(['TOUR_ID', 'ID', 'P_INT'])
.merge(df_last_availability[['AVAILABILITY']], how='left',
left_index=True, right_index=True, suffixes=('', '_PREV'))
.reset_index()
.drop(['P_INT'], axis=1))