首页 > 解决方案 > 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

显然,我有很多关于合并的知识。合并后是否可以执行此操作,或者我是否遗漏了什么。

编辑:在此示例中,用户是我的数据框索引。

标签: pandasdataframe

解决方案


而不是merge,采取另一种方法:

  1. 要为每个用户获取单独的结果,请按User对 DataFrame 进行分组。

  2. 对每组行(针对特定用户)应用一个函数,该函数:

    • 对Action.isin(['logon', 'unlock'].cumsum()执行第二级分组。这样,每行具有登录解锁动作的每一行都会 启动一个新组(实际上由 2 行组成,第二行在这对是关于locklogoff的一行)。
    • 每个此类组的结果应包含:
      • 登录操作- 第一行的操作
      • Start - 第一行的ActionTime
      • 注销操作- 最后一行的操作
      • 从最后一行停止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)
    

    附加元素是删除不必要的索引级别。

为了提供一个更有启发性的示例,我将源 DataFrame(用于 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

我假设您的DataFrameActionTime排序,全局或至少为每个用户排序(实际上,按UserActionTime排序,所以我没有包含任何排序。如果不满足此条件,请添加排序,例如在act函数中:

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

推荐阅读