Панды, объединяющие время входа и выхода пользователя

#pandas #dataframe

#pandas #фрейм данных

Вопрос:

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

     User    Action  ActionTime
0   User1   logon   1/1/2020 8:00
1   User1   lock    1/1/2020 12:00
2   User1   unlock  1/1/2020 13:00
3   User1   logoff  1/1/2020 16:00

  

Прямо сейчас я пытаюсь объединить действия входа в систему (вход в систему и разблокировка) и действия выхода из системы (выход из системы и блокировка) в отдельные строки с их временными метками. Пример:

         Action_x ActionTime_x   Action_y    ActionTime_y
User                
User1   logon   1/1/2020 8:00   lock    1/1/2020 12:00
User1   unlock  1/1/2020 13:00  logoff  1/1/2020 16:00
  

Для достижения этой цели я попытался поместить свои действия входа и выхода из системы в их собственный фрейм данных, а затем попытался объединить их вместе.

 logon = df[df["Action"].isin(["logon","unlock"])]
logon.set_index("User", inplace= True)

logoff = df[df["Action"].isin(["logoff","lock"])]
logoff.set_index("User", inplace= True)

merged = pd.merge(logon, logoff, right_index=True, left_index=True)
  

Что я получаю на выходе, так это:

         Action_x ActionTime_x   Action_y ActionTime_y
User                
User1   logon   1/1/2020 8:00   lock    1/1/2020 12:00
User1   logon   1/1/2020 8:00   logoff  1/1/2020 16:00
User1   unlock  1/1/2020 13:00  lock    1/1/2020 12:00
User1   unlock  1/1/2020 13:00  logoff  1/1/2020 16:00

  

Очевидно, мне еще многое предстоит узнать о слиянии. Возможно ли это действие при слиянии или я что-то упускаю.

Редактировать: в этом примере User — это мой индекс для фрейма данных.

Ответ №1:

Вместо слияния используйте другой подход:

  1. Чтобы получить отдельные результаты для каждого пользователя, сгруппируйте фрейм данных по пользователям.

  2. К каждой группе строк (для конкретного пользователя) применяется функция, которая:

    • Выполняет группировку второго уровня для Action.isin([‘logon’, ‘unlock’].cumsum(). Таким образом, каждая строка с действием входа в систему или разблокировки запускает новую группу (фактически состоящую из 2 строк, вторая строка в этой паре — это строка, касающаяся либо блокировки, либо выхода из системы).
    • Результат каждой такой группы должен содержать:
      • Действие входадействие из первой строки,
      • Запуск — время действия из первой строки,
      • Действие выхода из системы — действие из последней строки,
      • Остановите ActionTime с последней строки.

Код для этого:

  1. Определите функцию, которая будет применяться к каждой группе строк для текущего пользователя:

     def act(grp):
        return grp.sort_values('ActionTime').groupby(grp.Action.isin(['logon', 'unlock'])
            .cumsum()).agg(**{'Login Action': ('Action', 'first'),
                'Start': ('ActionTime', 'first'), 'Logoff Action': ('Action', 'last'),
                'Stop': ('ActionTime', 'last')})
      

    Чтобы в именах столбцов были пробелы, я использовал распаковку словаря.

  2. Примените его к каждой группе:

      result = df.groupby('User').apply(act).reset_index(level=1, drop=True)
      

    Дополнительный элемент заключается в удалении ненужного уровня индекса.

Чтобы представить более поучительный пример, я создал исходный фрейм данных (для 2 пользователей) как:

     User  Action          ActionTime
0  User1   logon 2020-01-01 08:00:00
1  User1    lock 2020-01-01 12:00:00
2  User1  unlock 2020-01-01 13:00:00
3  User1  logoff 2020-01-01 16:00:00
4  User2   logon 2020-01-01 08:15:00
5  User2    lock 2020-01-01 08:17:00
6  User2  unlock 2020-01-01 09:22:00
7  User2  logoff 2020-01-01 09:35:00
  

Результатом моего кода является:

       Login Action               Start Logoff Action                Stop
User                                                                    
User1        logon 2020-01-01 08:00:00          lock 2020-01-01 12:00:00
User1       unlock 2020-01-01 13:00:00        logoff 2020-01-01 16:00:00
User2        logon 2020-01-01 08:15:00          lock 2020-01-01 08:17:00
User2       unlock 2020-01-01 09:22:00        logoff 2020-01-01 09:35:00
  

Я предполагаю, что ваш фрейм данных сортируется по времени действия либо
глобально, либо, по крайней мере, для каждого пользователя (фактически, сортируется по пользователю и
ActionTime, поэтому я не включил какой-либо вид.
Если это условие не выполняется, добавьте сортировку, например, в функции act:

 return grp.sort_values('ActionTime').groupby(...)
  

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

1. Спасибо Valdi_Bo. Это дает мне много поводов для размышлений. Есть ли возможность возвращать строки в результирующий фрейм данных. Например, если бы я хотел передать жало из столбца действий. Так что у меня были столбцы «Пользователь, действие входа в систему, запуск, действие выхода из системы, остановка»

2. Я исправил свое решение, включив также оба столбца «Действие».