Группировка строк фрейма данных по отметкам времени начала/окончания

#python #python-3.x #pandas #dataframe

Вопрос:

У меня есть журнал приложений, который я помещаю в фрейм данных pandas, разбирая различные фрагменты каждого события на отдельные столбцы, что похоже на это:

 data1 = {'timestamp': ['01-01-2021 12:00:00','01-01-2021 12:01:00','01-01-2021 12:02:00','01-01-2021 12:03:00','01-01-2021 12:04:00','01-01-2021 12:05:00','01-01-2021 12:06:00','01-01-2021 12:07:00','01-01-2021 12:08:00','01-01-2021 12:09:00','01-01-2021 12:10:00','01-01-2021 12:11:00','01-01-2021 12:12:00','01-01-2021 12:13:00','01-01-2021 12:14:00'],
    'event':     ['start','x','y','start','z','end','x','end','start','x','end','start','q','end','start'],
    'account':   ['bob','bob','bob','jane','bob','bob','jane','jane','todd','todd','todd','bob','bob','bob','ned'] }
df1 = pd.DataFrame(data1)
print(df1)

    timestamp            event     account
0   01-01-2021 12:00:00  start     bob
1   01-01-2021 12:01:00      x     bob
2   01-01-2021 12:02:00      y     bob
3   01-01-2021 12:03:00  start    jane
4   01-01-2021 12:04:00      z     bob
5   01-01-2021 12:05:00    end     bob
6   01-01-2021 12:06:00      x    jane
7   01-01-2021 12:07:00    end    jane
8   01-01-2021 12:08:00  start    todd
9   01-01-2021 12:09:00      x    todd
10  01-01-2021 12:10:00    end    todd
11  01-01-2021 12:11:00  start     bob
12  01-01-2021 12:12:00      q     bob
13  01-01-2021 12:13:00    end     bob
14  01-01-2021 12:14:00  start     ned
 

Довольно полосатое переднее бревно. Метки времени являются строками, но при необходимости их можно легко преобразовать в объекты datetime. В журнале перечислены различные действия, выполняемые пользователем, но меня интересует создание отчета о сеансах учетной записи пользователем. Что-то вроде этого:

      account  start                end
0    bob      01-01-2021 12:00:00  01-01-2021 12:05:00
1    jane     01-01-2021 12:03:00  01-01-2021 12:07:00
2    todd     01-01-2021 12:08:00  01-01-2021 12:10:00
3    bob      01-01-2021 12:11:00  01-01-2021 12:13:00
4    ned      01-01-2021 12:14:00                  NaN
 

Достаточно легко сгруппировать по времени начала и окончания, проблема, которую я не могу понять, заключается в том, как это сделать, когда у пользователя несколько сеансов за рассматриваемый период времени. В приведенных выше данных псевдо-журнала у Боба открыто 2 сеанса, но теоретически он мог открыть 100 сеансов в зависимости от диапазона дат данных, на которые я смотрю. Рассматриваемое приложение ограничивает пользователей 1 сеансом за раз, поэтому я не должен видеть одну и ту же учетную запись с 2 сеансами, открытыми одновременно.

Как мне сгруппировать время начала/окончания сеанса в 2 столбца, подобных этому, по учетной записи, когда в данных учетной записи может быть несколько сеансов?

Ответ №1:

Попробуй:

 (df1.assign(idx=df1.event.eq('start').groupby(df1.account).cumsum())  # enumerate the `start` event by account
    .loc[lambda x: x['event'].isin(['start','end'])]                  # only keep `start` and `end` events
    .set_index(['idx','account','event'])                             # set index and unstack
    ['timestamp'].unstack()                               # then unstack
    .reset_index(level=1).reset_index(drop=True)          # tidying up
)
 

Выход:

 event account                  end                start
0         bob  01-01-2021 12:05:00  01-01-2021 12:00:00
1        jane  01-01-2021 12:07:00  01-01-2021 12:03:00
2         ned                  NaN  01-01-2021 12:14:00
3        todd  01-01-2021 12:10:00  01-01-2021 12:08:00
4         bob  01-01-2021 12:13:00  01-01-2021 12:11:00
 

Ответ №2:

Вы могли бы определить старты и использовать pivot_table :

 (df1.assign(idx=df1['event'].eq('start').groupby(df1['account']).cumsum())
    .pivot_table(index=['account', 'idx'], columns='event', values='timestamp', aggfunc='first')
    [['start', 'end']]
 )
 

Выход:

 event                      start                  end
account idx                                          
bob     1    01-01-2021 12:00:00  01-01-2021 12:05:00
        2    01-01-2021 12:11:00  01-01-2021 12:13:00
jane    1    01-01-2021 12:03:00  01-01-2021 12:07:00
ned     1    01-01-2021 12:14:00                  NaN
todd    1    01-01-2021 12:08:00  01-01-2021 12:10:00
 

Комментарии:

1. Извините, забыл обновить сообщение раньше. Это сработало, спасибо!