#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:
Вместо слияния используйте другой подход:
-
Чтобы получить отдельные результаты для каждого пользователя, сгруппируйте фрейм данных по пользователям.
-
К каждой группе строк (для конкретного пользователя) применяется функция, которая:
- Выполняет группировку второго уровня для Action.isin([‘logon’, ‘unlock’].cumsum(). Таким образом, каждая строка с действием входа в систему или разблокировки запускает новую группу (фактически состоящую из 2 строк, вторая строка в этой паре — это строка, касающаяся либо блокировки, либо выхода из системы).
- Результат каждой такой группы должен содержать:
- Действие входа — действие из первой строки,
- Запуск — время действия из первой строки,
- Действие выхода из системы — действие из последней строки,
- Остановите ActionTime с последней строки.
Код для этого:
-
Определите функцию, которая будет применяться к каждой группе строк для текущего пользователя:
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')})
Чтобы в именах столбцов были пробелы, я использовал распаковку словаря.
-
Примените его к каждой группе:
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. Я исправил свое решение, включив также оба столбца «Действие».